NumPy: A Python Library for Scientific
Computing
The basics
In [2]:
import numpy as np
a = np.arange(15).reshape(3, 5) # Creates a 3x5 NumPy array with values in [0, 14]
a
array([[ 0, 1, 2, 3, 4],
Out[2]:
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
In [3]:
squares = np.array([x**2 for x in range(10)])
squares
array([ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81])
Out[3]:
In [4]:
type(squares)
numpy.ndarray
Out[4]:
Useful n-dimensional array (np.array) methods
In [5]:
a.shape
(3, 5)
Out[5]:
In [6]:
a.ndim
2
Out[6]:
In [7]:
a.dtype.name
'int32'
Out[7]:
In [8]:
a.itemsize
4
Out[8]:
In [9]:
a.size
15
Out[9]:
In [10]:
type(a)
numpy.ndarray
Out[10]:
Creating arrays from Python's sequence types
In [11]:
squares = [x**2 for x in range(10)] # python list
cubes = tuple(x**3 for x in range(10)) # python tuple
evens = {x for x in range(10) if x%2 == 0} # python set
print(type(squares))
print(type(cubes))
print(type(evens))
<class 'list'>
<class 'tuple'>
<class 'set'>
In [12]:
a = np.array(squares)
b = np.array(cubes)
c = np.array(evens)
if type(a) == type(b) == type(c):
print(type(a))
<class 'numpy.ndarray'>
In [13]:
seq2 = [[x, x**2, x**3] for x in range(10)]
seq3 = [[[x for x in range(3)], [y for y in range(3, 6)]] for z in range(10)]
a = np.array(seq2) # create array from nested lists of depth 2
b = np.array(seq3) # create array from nested lists of depth 3
print(a.ndim)
print(b.ndim)
2
3
In [14]:
odds = list(i for i in range(10) if i%2 != 0)
numpy_odds = np.array(odds, dtype=complex) # declare data type during instantiation
numpy_odds
array([1.+0.j, 3.+0.j, 5.+0.j, 7.+0.j, 9.+0.j])
Out[14]:
In [15]:
zeros = np.zeros((3, 3), dtype=np.int32) # create a 3x3 array of 0's
ones = np.ones((2, 2), dtype=np.float64) # create a 2x2 array of 1's
empty = np.empty((3, 3))
print(zeros, zeros.dtype.name)
print(ones, ones.dtype.name)
print(empty, empty.dtype.name)
[[0 0 0]
[0 0 0]
[0 0 0]] int32
[[1. 1.]
[1. 1.]] float64
[[0.00000000e+000 0.00000000e+000 0.00000000e+000]
[0.00000000e+000 0.00000000e+000 8.35959073e-321]
[1.37962117e-306 0.00000000e+000 0.00000000e+000]] float64
In [16]:
a = np.arange(5, 30, 5) # create array from range [5, 30) with step of 5
a
array([ 5, 10, 15, 20, 25])
Out[16]:
In [17]:
b = np.linspace(0, 1, 100) # create array of 100 values between 0 and 1
b
array([0. , 0.01010101, 0.02020202, 0.03030303, 0.04040404,
Out[17]:
0.05050505, 0.06060606, 0.07070707, 0.08080808, 0.09090909,
0.1010101 , 0.11111111, 0.12121212, 0.13131313, 0.14141414,
0.15151515, 0.16161616, 0.17171717, 0.18181818, 0.19191919,
0.2020202 , 0.21212121, 0.22222222, 0.23232323, 0.24242424,
0.25252525, 0.26262626, 0.27272727, 0.28282828, 0.29292929,
0.3030303 , 0.31313131, 0.32323232, 0.33333333, 0.34343434,
0.35353535, 0.36363636, 0.37373737, 0.38383838, 0.39393939,
0.4040404 , 0.41414141, 0.42424242, 0.43434343, 0.44444444,
0.45454545, 0.46464646, 0.47474747, 0.48484848, 0.49494949,
0.50505051, 0.51515152, 0.52525253, 0.53535354, 0.54545455,
0.55555556, 0.56565657, 0.57575758, 0.58585859, 0.5959596 ,
0.60606061, 0.61616162, 0.62626263, 0.63636364, 0.64646465,
0.65656566, 0.66666667, 0.67676768, 0.68686869, 0.6969697 ,
0.70707071, 0.71717172, 0.72727273, 0.73737374, 0.74747475,
0.75757576, 0.76767677, 0.77777778, 0.78787879, 0.7979798 ,
0.80808081, 0.81818182, 0.82828283, 0.83838384, 0.84848485,
0.85858586, 0.86868687, 0.87878788, 0.88888889, 0.8989899 ,
0.90909091, 0.91919192, 0.92929293, 0.93939394, 0.94949495,
0.95959596, 0.96969697, 0.97979798, 0.98989899, 1. ])
In [18]:
from numpy import pi
c = np.linspace(0, 2*pi, 10) # useful for evaluating trigonometric function at point
f = np.sin(c)
f
array([ 0.00000000e+00, 6.42787610e-01, 9.84807753e-01, 8.66025404e-01,
Out[18]:
3.42020143e-01, -3.42020143e-01, -8.66025404e-01, -9.84807753e-01,
-6.42787610e-01, -2.44929360e-16])
In [19]:
# creating an array from a function
def f(row_num, col_num):
return 2*row_num + col_num
F = np.fromfunction(f, (3, 3), dtype=int)
F
array([[0, 1, 2],
Out[19]:
[2, 3, 4],
[4, 5, 6]])
Printing arrays
In [20]:
dim1 = np.arange(15) # Create 1 1x15 matrix; n=1
dim2 = np.arange(15).reshape(3, 5) # Create 1 3x5 matrix; n=2
dim3 = np.arange(24).reshape(2, 3, 4) # Create 2 3x4 matrices; n=3
print(dim1, end='\t[1x15]\n\n')
print(dim2, end='\t[3x5]\n\n')
print(dim3, end='\t[2x3x4]')
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14] [1x15]
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]] [3x5]
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]] [2x3x4]
In [21]:
very_large = np.arange(10000).reshape(100, 100)
print(very_large) # printing a very large array
[[ 0 1 2 ... 97 98 99]
[ 100 101 102 ... 197 198 199]
[ 200 201 202 ... 297 298 299]
...
[9700 9701 9702 ... 9797 9798 9799]
[9800 9801 9802 ... 9897 9898 9899]
[9900 9901 9902 ... 9997 9998 9999]]
Common NumPy array operations
- Arithmetic operators on arrays apply elementwise.
- A new array is created and filled with the result.
In [22]:
a = np.arange(10)
b = np.array([2 for x in range(10)])
c = np.ones(10, dtype=int)
result = a + b - c # add 2 and subtract 1 from each element of a
print(result)
[ 1 2 3 4 5 6 7 8 9 10]
In [23]:
result **= 2 # square each element of result
result
array([ 1, 4, 9, 16, 25, 36, 49, 64, 81, 100])
Out[23]:
In [24]:
result < 10
array([ True, True, True, False, False, False, False, False, False,
Out[24]:
False])
In [25]:
a = np.ones(15).reshape(3, 5)
b = np.ones(15).reshape(5, 3)
mult = a * 10 # element-wise multiplication
dot_prod1 = a.dot(b) # dot product version 1
dot_prod2 = np.dot(a, b) # dot product version 2
print(mult, end='\n\n')
print(dot_prod1, end='\n\n')
print(dot_prod2)
[[10. 10. 10. 10. 10.]
[10. 10. 10. 10. 10.]
[10. 10. 10. 10. 10.]]
[[5. 5. 5.]
[5. 5. 5.]
[5. 5. 5.]]
[[5. 5. 5.]
[5. 5. 5.]
[5. 5. 5.]]
In [26]:
a, b = dot_prod1, dot_prod2
a += b # += and *= act in place -- they modify the existing array
a
array([[10., 10., 10.],
Out[26]:
[10., 10., 10.],
[10., 10., 10.]])
In [27]:
# array operations result in upcasting (result is more general type)
a = np.ones(10, dtype=int)
b = np.ones(10, dtype=float)
c = np.ones(10, dtype=complex)
a_plus_b = a + b # int + float
b_plus_c = b + c # float + complex
print(a_plus_b.dtype.name)
print(b_plus_c.dtype.name)
float64
complex128
In [28]:
# useful array methods
a = np.arange(1, 11)
print('Sum:', a.sum())
print('Min:', a.min())
print('Max:', a.max())
Sum: 55
Min: 1
Max: 10
In [29]:
# applying methods to a single axis
a = np.arange(15).reshape(3, 5) + 1
print(a.sum(axis=0)) # sum each column
print(a.min(axis=1)) # min of each row
a
[18 21 24 27 30]
[ 1 6 11]
array([[ 1, 2, 3, 4, 5],
Out[29]:
[ 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15]])
Universal functions
In [30]:
# universal functions (sin, cos, exp, sqrt, ... etc)
a = np.linspace(0, 2*pi, 10000)
sin_a = np.sin(a)
sqrt_a = np.sqrt(a)
exp_a = np.exp(a)
print(sin_a, end='\n\n')
print(sqrt_a, end='\n\n')
print(exp_a, end='\n\n')
[ 0.00000000e+00 6.28381328e-04 1.25676241e-03 ... -1.25676241e-03
-6.28381328e-04 -2.44929360e-16]
[0. 0.02506754 0.03545085 ... 2.50637757 2.50650293 2.50662827]
[ 1. 1.00062858 1.00125755 ... 534.81909228 535.15526825
535.49165552]
Indexing, slicing, and iterating
In [31]:
# indexing elements from multidimensional arrays
A = np.array([x**2 for x in range(1, 11)]).reshape(2, 5)
print(A, end='\n\n')
print('A[0]\t\t', A[0])
print('A[-1]\t\t', A[-1])
print('A[0][0]\t\t', A[0][0])
print('A[-1][-1]\t', A[-1][-1])
#print('', A[])
[[ 1 4 9 16 25]
[ 36 49 64 81 100]]
A[0] [ 1 4 9 16 25]
A[-1] [ 36 49 64 81 100]
A[0][0] 1
A[-1][-1] 100
In [32]:
# slicing NumPy arrays
cubes = np.arange(1, 13).reshape(4, 3)**3 # cubes of 1-12
print(cubes[:,:], end='\n\n') # all rows, all cols
print('First Column:\t', cubes[:, 0]) # first col
print('Last Column:\t', cubes[:, -1]) # last col
print('First Row:\t', cubes[0, :]) # first row
print('Last Row:\t', cubes[-1, :]) # last row
[[ 1 8 27]
[ 64 125 216]
[ 343 512 729]
[1000 1331 1728]]
First Column: [ 1 64 343 1000]
Last Column: [ 27 216 729 1728]
First Row: [ 1 8 27]
Last Row: [1000 1331 1728]
In [33]:
# iterating through array elements
A = np.array([x+1 for x in range(10)])
A
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
Out[33]:
In [34]:
for a in A:
print(a, end=',')
1,2,3,4,5,6,7,8,9,10,
In [35]:
A = A.reshape(2, 5)
for a in A[0,:]: # for element in row 1
print(a**2, end=',')
1,4,9,16,25,
In [36]:
# Advanced: using ... for axis completion
A = np.arange(125).reshape(5, 5, 5) + 1
print('A[0, ...] == A[0, :, :]', end='\n\n')
print(A[0, ...], '\n\n', A[0, :, :])
A[0, ...] == A[0, :, :]
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]]
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]]
In [37]:
# flattening a 5x5x5 array for iteration
for a in A.flat:
print(a, end=',')
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,3
2,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,6
0,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,8
8,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,11
2,113,114,115,116,117,118,119,120,121,122,123,124,125,
Shape manipulation
Changing the shape of an array
In [38]:
a = np.floor(10*np.random.random((3, 4)))
print(a)
print(a.shape)
[[8. 2. 9. 3.]
[1. 3. 3. 1.]
[7. 2. 3. 4.]]
(3, 4)
In [39]:
a.ravel() # returns a COPY of the array flattened
array([8., 2., 9., 3., 1., 3., 3., 1., 7., 2., 3., 4.])
Out[39]:
In [40]:
a.reshape(6, 2) # returns a COPY of the array reshaped
array([[8., 2.],
Out[40]:
[9., 3.],
[1., 3.],
[3., 1.],
[7., 2.],
[3., 4.]])
In [41]:
a.T # returns a COPY of the array, transposed
array([[8., 1., 7.],
Out[41]:
[2., 3., 2.],
[9., 3., 3.],
[3., 1., 4.]])
In [42]:
print('A:\t', a.shape)
print('A^T:\t', a.T.shape)
A: (3, 4)
A^T: (4, 3)
In [43]:
A = np.floor(10*np.random.random((3, 4)))
A.reshape(4, 3) # reshape returns a COPY of A
print(A, 'array A remains unchanged')
A.resize(4, 3) # resize modifies array A
print(A, 'array A is of a new shape')
[[3. 8. 3. 8.]
[5. 8. 2. 0.]
[4. 4. 0. 2.]] array A remains unchanged
[[3. 8. 3.]
[8. 5. 8.]
[2. 0. 4.]
[4. 0. 2.]] array A is of a new shape
Stacking NumPy arrays horizontally and vertically
In [44]:
a = np.zeros(9, dtype=int).reshape(3, 3)
b = np.ones(9, dtype=int).reshape(3, 3)
# stack horizontally:
hor = np.hstack((a, b))
ver = np.vstack((a, b))
print('a:\n', a)
print('b:\n', b)
print('Horizontal Stack:\n', hor)
print('Vertical Stack:\n', ver)
a:
[[0 0 0]
[0 0 0]
[0 0 0]]
b:
[[1 1 1]
[1 1 1]
[1 1 1]]
Horizontal Stack:
[[0 0 0 1 1 1]
[0 0 0 1 1 1]
[0 0 0 1 1 1]]
Vertical Stack:
[[0 0 0]
[0 0 0]
[0 0 0]
[1 1 1]
[1 1 1]
[1 1 1]]
In [45]:
# similarly, row_stack and column_stack can be used to stack into 2D arrays
r_stack = np.row_stack((a, b))
c_stack = np.column_stack((a, b))
print('Horizontal Stack:\n', hor)
print('Vertical Stack:\n', ver)
print('Column Stack:\n', c_stack)
print('Row Stack:\n', r_stack)
Horizontal Stack:
[[0 0 0 1 1 1]
[0 0 0 1 1 1]
[0 0 0 1 1 1]]
Vertical Stack:
[[0 0 0]
[0 0 0]
[0 0 0]
[1 1 1]
[1 1 1]
[1 1 1]]
Column Stack:
[[0 0 0 1 1 1]
[0 0 0 1 1 1]
[0 0 0 1 1 1]]
Row Stack:
[[0 0 0]
[0 0 0]
[0 0 0]
[1 1 1]
[1 1 1]
[1 1 1]]
In [46]:
from numpy import newaxis
r_stack = np.row_stack((a[newaxis], b[newaxis]))
c_stack = np.column_stack((a[newaxis], b[newaxis]))
print('Column Stack:\n', c_stack)
print('Row Stack:\n', r_stack)
Column Stack:
[[[0 0 0]
[0 0 0]
[0 0 0]
[1 1 1]
[1 1 1]
[1 1 1]]]
Row Stack:
[[[0 0 0]
[0 0 0]
[0 0 0]]
[[1 1 1]
[1 1 1]
[1 1 1]]]
In [47]:
# use range literals to concatenate arrays on the fly
np.r_[1:5, 5, 6:10, 10]
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
Out[47]:
In [48]:
np.c_[1:5]
array([[1],
Out[48]:
[2],
[3],
[4]])
Splitting large arrays into smaller ones
In [49]:
# use hsplit or vsplit to split arrays
a = np.ones(9).reshape(3, 3)
a_1, a_2, a_3 = np.hsplit(a, 3) # split a into 3 arrays
print(a, end='\n\n')
print(a_1, end='\n\n')
print(a_2, end='\n\n')
print(a_3, end='\n\n')
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]
[[1.]
[1.]
[1.]]
[[1.]
[1.]
[1.]]
[[1.]
[1.]
[1.]]
Views and copies
No copy at all
In [50]:
a = np.arange(15).reshape(5, 3)
b = a
a is b
True
Out[50]:
View or shallow copy
In [51]:
# use the view() method to create a shallow copy of an array
a = np.array([x**2 for x in range(6)])
b = a.view()
print('a is b:', a is b)
print('a is b.base:', a is b.base)
b.shape = 3, 2
print(a.shape, ': no change to a\'s shape') # a's shape doesn't change
b[0, 1] = 1000
print(a, 'a\'s data has changed!')
a is b: False
a is b.base: True
(6,) : no change to a's shape
[ 0 1000 4 9 16 25] a's data has changed!
Deep copy
In [52]:
a = np.array([x+1 for x in range(10)])
b = a.copy()
print('a:', a, end='\t(Original)\n\n')
print('b:', b, end='\t(Deep Copy)\n\n')
print('a is b:', a is b)
a: [ 1 2 3 4 5 6 7 8 9 10] (Original)
b: [ 1 2 3 4 5 6 7 8 9 10] (Deep Copy)
a is b: False