import numpy as np
prices = np.array([12.0, 9.0, 15.0])
print(prices)
print(prices.shape) # how many, in each dimension
print(prices.dtype) # what kind of number
print(prices.size) # how many in total[12. 9. 15.]
(3,)
float64
3
Programming with Python
The investor wants a numbers deck by Friday. A thousand orders sit in the system, one row each: minutes, price, zone.
. . .
Kevin’s plan is a 40-tab spreadsheet, one tab per zone, copied by hand. He looks at the pile, then at his list-of-lists, and says the only true thing he’ll say all week:
“We need a bigger boat than a list.”
. . .
Today we get the bigger boat: NumPy, one object that holds a thousand numbers and does arithmetic on all of them at once.
A NumPy array holds many numbers under one name, like a list, but built for math. You make one from a list, then ask it about itself:
import numpy as np
prices = np.array([12.0, 9.0, 15.0])
print(prices)
print(prices.shape) # how many, in each dimension
print(prices.dtype) # what kind of number
print(prices.size) # how many in total[12. 9. 15.]
(3,)
float64
3
. . .
np.array([...]) wraps a list; import numpy as np is the nickname everyone uses. .shape is (3,), .dtype is float64, .size is 3. One dtype for the whole array: every element shares the same type; that’s part of what makes it fast.
Here’s the bigger boat. The deck needs gross prices: 19% VAT on all three. With a list you loop; with an array you just multiply:
# the painful way — a loop, item by item
gross = []
for p in [12.0, 9.0, 15.0]:
gross.append(p * 1.19)import numpy as np
prices = np.array([12.0, 9.0, 15.0])
print(prices * 1.19) # every element, one expression[14.28 10.71 17.85]
. . .
One operation lands on all elements at once: [14.28 10.71 17.85]. No loop, no .append, and on a thousand orders it’s also far faster.
Two builders make evenly-spaced arrays without typing every number, handy for axes and ranges:
import numpy as np
print(np.arange(0, 10, 2)) # start, stop (excluded), step
print(np.linspace(0, 1, 5)) # start, stop (included), how many[0 2 4 6 8]
[0. 0.25 0.5 0.75 1. ]
. . .
arange walks by a step and stops before the end, just like range. linspace splits a span into a fixed count of points, endpoints included.
Kevin multiplies a row of counts by two. What does this print?
print([1, 2, 3] * 2)a) [1, 2, 3, 1, 2, 3] b) [2, 4, 6] c) an error
. . .
Predict first. Commit to an answer before the next slide.
a) [1, 2, 3, 1, 2, 3] — that’s a plain list, and * 2 on a list repeats it. Wrap it in an array and the same * 2 does the math instead:
import numpy as np
print([1, 2, 3] * 2) # list → repeated
print(np.array([1, 2, 3]) * 2) # array → doubled[1, 2, 3, 1, 2, 3]
[2 4 6]
. . .
Lists repeat; arrays compute. That’s why we’re here.
Compare an array to a number and you don’t get one True/False. You get a whole array of them, one per element. That’s a mask, and you can filter with it:
import numpy as np
times = np.array([25, 41, 18, 33])
print(times > 30) # a True/False for every element
print(times[times > 30]) # keep only where the mask is True[False True False True]
[41 33]
. . .
times > 30 is the mask; times[times > 30] reads the array through the mask and returns just the matching values. No loop, no if.
A mask answers two investor questions at once. .sum() counts the Trues (each counts as 1); .mean() gives the share that are True:
import numpy as np
times = np.array([25, 41, 18, 33, 52, 29, 44, 12])
late = times > 30
print(int(late.sum())) # how many were late
print(float(times[late].mean())) # average of just the late ones
print(float(late.mean())) # the SHARE that were late4
42.5
0.5
. . .
4 deliveries over 30 minutes, averaging 42.5, and late.mean() says half the run was late, one line each, straight into the deck.
What does calling .sum() on the mask give?
print((np.array([1, 5, 3]) > 2).sum())a) True b) 2 c) [False, True, True]
. . .
Predict first. Commit to an answer before the next slide.
b) 2 — .sum() adds the mask up, and each True is worth 1, each False 0. Two elements clear the bar, so the count is 2:
import numpy as np
print(np.array([1, 5, 3]) > 2) # [False True True]
print((np.array([1, 5, 3]) > 2).sum()) # Trues add up to 2[False True True]
2
. . .
Summing a mask counts; averaging a mask shares. Same two tricks the lab asks for.
Real data isn’t one row. Stack rows and you get a 2D array: here three days (rows) across four zones (columns: Nord, Sued, Hafen, Altstadt):
import numpy as np
deliveries = np.array([[ 9, 14, 11, 6],
[15, 12, 8, 9],
[13, 20, 16, 11]])
print(deliveries.shape) # (rows, columns) → (3, 4)
print(deliveries[0, 2]) # row 0, column 2(3, 4)
11
. . .
.shape is now (3, 4): three days, four zones. One index picks the row, a second the column: deliveries[0, 2] is day 0, zone 2 (Hafen).
To sum a grid you must say which way to collapse it. The axis tells NumPy which direction disappears:
Nord Sued Hafen Altstadt
day0 [ 9 14 11 6 ]
day1 [ 15 12 8 9 ]
day2 [ 13 20 16 11 ]
axis=0 collapses DOWN the rows:
↓ ↓ ↓ ↓
37 46 35 26 one number per column (zone)
axis=1 collapses ACROSS the columns:
day0 → 40 · day1 → 44 · day2 → 60 one number per row (day)
import numpy as np
deliveries = np.array([[ 9, 14, 11, 6],
[15, 12, 8, 9],
[13, 20, 16, 11]])
print(deliveries.sum(axis=0)) # DOWN the rows → per zone
print(deliveries.sum(axis=1)) # ACROSS the columns → per day[37 46 35 26]
[40 44 60]
. . .
axis=0 collapses DOWN the rows, one number per column (zone): [37 46 35 26]. axis=1 collapses across, one per day: [40 44 60].
The per-zone totals answer “how many?”, but the investor asks “which zone?”. argmax tells you WHERE the maximum sits, as an index:
import numpy as np
deliveries = np.array([[ 9, 14, 11, 6],
[15, 12, 8, 9],
[13, 20, 16, 11]])
zone_totals = deliveries.sum(axis=0) # [37 46 35 26]
print(int(zone_totals.argmax())) # index of the biggest1
. . .
max would give the value 46; argmax gives its position, 1. The busiest zone is Sued, sitting at index 1, not at the front. Position, not value: that’s the whole point of argmax.
The investor wants four numbers, one per zone. Which call?
week = np.array([[ 9, 14, 11, 6],
[15, 12, 8, 9],
[13, 20, 16, 11]])a) week.sum(axis=1) b) week.sum() c) week.sum(axis=0)
. . .
Predict first. Commit to an answer before the next slide.
c) week.sum(axis=0) — four zones means four numbers, so the days must disappear: axis=0 collapses DOWN the rows, one number per column (zone):
import numpy as np
week = np.array([[ 9, 14, 11, 6],
[15, 12, 8, 9],
[13, 20, 16, 11]])
print(week.sum(axis=0))[37 46 35 26]
. . .
b) would give three numbers (one per day); c) would give one number: the grand total, 144.
. . .
Download your .py before you leave. Closing the tab without downloading loses your work.
np.array([...]) holds many numbers; arithmetic hits every element at once. No loop. Ask it .shape, .dtype, .size to know what you’re holding.arr > 30 is a True/False array: arr[mask] filters, .sum() counts the Trues, .mean() gives their share.axis=0 collapses DOWN the rows (one number per column), axis=1 across the columns (one per row); argmax tells you where the maximum sits.. . .
Next episode: the investor opens a data room, and Kevin lets an AI write his pandas. The arrays get column names, and a thousand orders become a table you can query.
. . .
NumPy has excellent free docs: the NumPy absolute beginner’s guide covers everything in this session and a little more.
. . .
For more, see the literature list of this course.