Chapters
Python114 chapters

Beyond the basicsChapter 91 of 114

NumPy Intro

Arrays that do arithmetic on every element at once.

Why it exists

A Python list is flexible and slow for numbers: each item is a separate object, and a loop over a million of them is a million interpreted steps. NumPy stores numbers in one block of memory and does the loop in C.

Python needs numpy, downloaded on first run
import numpy as np

a = np.array([1, 2, 3, 4])
print(a)
print(a.dtype.kind, a.shape)

Output

[1 2 3 4]
i (4,)

Note the printing: no commas. That is how you can tell an array from a list at a glance. The kind is i for integer; the exact width behind it is int64 on most desktops and int32 on Windows and here in the browser, so do not rely on it unless you set dtype yourself.

Arithmetic applies to everything

This is the whole idea. No loop, no comprehension:

Python needs numpy, downloaded on first run
import numpy as np

a = np.array([1, 2, 3, 4])
print(a * 2)
print(a + 10)
print(a ** 2)
print(a + np.array([10, 20, 30, 40]))

Output

[2 4 6 8]
[11 12 13 14]
[ 1  4  9 16]
[11 22 33 44]

With a list, a * 2 would repeat it and a + 10 would be a TypeError.

Making arrays

Python needs numpy, downloaded on first run
import numpy as np

print(np.zeros(3))
print(np.ones((2, 3)))
print(np.arange(0, 10, 3))
print(np.linspace(0, 1, 5))

Output

[0. 0. 0.]
[[1. 1. 1.]
 [1. 1. 1.]]
[0 3 6 9]
[0.   0.25 0.5  0.75 1.  ]

arange is like range with a step; linspace gives a fixed number of evenly spaced values, which is usually what you want for plotting.

One type per array

Python needs numpy, downloaded on first run
import numpy as np

print(np.array([1, 2, 3]).dtype.kind)
print(np.array([1.0, 2, 3]).dtype.kind)
print(np.array([1, 2, 3], dtype=float))

Output

i
f
[1. 2. 3.]

Everything is promoted to a single type. That fixed layout is where the speed comes from.

Shapes

Python needs numpy, downloaded on first run
import numpy as np

grid = np.array([[1, 2, 3], [4, 5, 6]])
print(grid.shape)
print(grid[1, 2])
print(grid[0])
print(grid.reshape(3, 2))

Output

(2, 3)
6
[1 2 3]
[[1 2]
 [3 4]
 [5 6]]

Indexing a grid is grid[row, column], not grid[row][column] — though both work.

Selecting with a condition

A comparison gives an array of booleans, and that can index the array:

Python needs numpy, downloaded on first run
import numpy as np

a = np.array([1, 5, 3, 8, 2])
print(a > 3)
print(a[a > 3])
print(a[a > 3].sum())

Output

[False  True False  True False]
[5 8]
13

This is called boolean masking, and it replaces most of the filtering loops you would otherwise write.

Summarising

Python needs numpy, downloaded on first run
import numpy as np

a = np.array([[1, 2], [3, 4]])
print(a.sum(), a.mean(), a.max())
print(a.sum(axis=0))
print(a.sum(axis=1))

Output

10 2.5 4
[4 6]
[3 7]

axis=0 collapses down the columns, axis=1 across the rows. Getting those the right way round is most of learning NumPy.

A slice is a view

Unlike a list slice, a NumPy slice shares memory with the original:

Python needs numpy, downloaded on first run
import numpy as np

a = np.array([1, 2, 3, 4])
piece = a[0:2]
piece[0] = 99
print(a)

safe = a[0:2].copy()
safe[0] = 0
print(a)

Output

[99  2  3  4]
[99  2  3  4]

That avoids copying large data, and it means an edit through a slice changes the original. Call .copy() when you do not want that.

Test yourself

2 questions

What does a * 2 do to a NumPy array, compared with a list?

Show the answer

Doubles every element, where a list would repeat itself — That elementwise arithmetic without a loop is the whole point of an array.

How does a NumPy slice differ from a list slice?

Show the answer

It shares memory with the original, so edits show through — That avoids copying large data. Call .copy() when you want independence.

Next chapter

Pandas Intro

Tables with named columns, for real data work.