import numpy as npLec 2-3 - Create arrays, array shapes
np.ones, np.ones_like, np.zeros, np.eye, np.arange, np.linspace, np.logspace(<function numpy.ones(shape, dtype=None, order='C', *, like=None)>,
<function ones_like at 0x7f1f9c7c3230>,
<function numpy.zeros>,
<function numpy.eye(N, M=None, k=0, dtype=<class 'float'>, order='C', *, like=None)>,
<function numpy.arange>,
<function linspace at 0x7f1f9c619730>,
<function logspace at 0x7f1f9c619930>)
np.ones(3)array([1., 1., 1.])
np.ones(3).shape(3,)
np.zeros(4)array([0., 0., 0., 0.])
np.eye(4)array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
np.eye(4).shape(4, 4)
np.zeros_like(np.eye(4))array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
np.zeros_like(np.eye(4)).shape(4, 4)
np.ones_like(np.eye(4))array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
np.arange(7)array([0, 1, 2, 3, 4, 5, 6])
np.linspace(4, 8, num=9)array([4. , 4.5, 5. , 5.5, 6. , 6.5, 7. , 7.5, 8. ])
np.logspace(0, 2, num=3)array([ 1., 10., 100.])
Specify entries manually with a list
np.array([1.0, 2.0, 3.3])array([1. , 2. , 3.3])
A = np.array([[1.0, 2.0, 3.0],
[1.1, 2.2, 3.3]])
Aarray([[1. , 2. , 3. ],
[1.1, 2.2, 3.3]])
A.shape(2, 3)
Random numbers
np.random.uniform(size=(3, 4))array([[0.45277498, 0.71551166, 0.22367996, 0.2476122 ],
[0.34208851, 0.77209602, 0.68308149, 0.32963277],
[0.35762772, 0.791859 , 0.21868476, 0.33329781]])
np.random.normal(size=(3, 4))array([[-1.72524041, 2.32250734, -0.29485922, 0.64230775],
[-1.73508202, 2.3038945 , 0.64756682, -1.36955389],
[-0.65310368, -0.58493861, -0.84548246, 0.46837203]])
Chanding dtype
o = np.ones(3, dtype=np.int8)o.dtypedtype('int8')
Accessing array by single cells
o[1]1
o[2]1
A array([[1. , 2. , 3. ],
[1.1, 2.2, 3.3]])
A[0, 0]1.0
A[0, 1]2.0
A[1, 2]3.3
A[1, 3]IndexError: index 3 is out of bounds for axis 1 with size 3
Accessing arrays with slices
B = np.arange(24).reshape((4, 6))
Barray([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
# The two middle rows:
B[1:3, :]array([[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17]])
# The two middle rows, only second column
B[1:3, 1]array([ 7, 13])
# Use two columns (two ":") to skip some integers periodically
Barray([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
B[:, 0:7:2] # keep every other columnarray([[ 0, 2, 4],
[ 6, 8, 10],
[12, 14, 16],
[18, 20, 22]])
B[:, ::2] # keep every other columnarray([[ 0, 2, 4],
[ 6, 8, 10],
[12, 14, 16],
[18, 20, 22]])
B[:, 0:7:3] # keep one in three columnsarray([[ 0, 3],
[ 6, 9],
[12, 15],
[18, 21]])
Comparing arrays
The tol (precision under which two numbers will be considered equal) can be modified, see the rtol and atol argument at https://numpy.org/doc/stable/reference/generated/numpy.isclose.html
np.isclose, np.allclose # these functions help to compare floats arrays for equality.(<function isclose at 0x7f1f9c7ccaf0>, <function allclose at 0x7f1f9c7cc970>)
np.isclose(np.exp(-20), 0.)True
np.exp(-3000) == 0.True
np.exp(-40) == 0.False
Operations along axis:
np.sum, np.mean(<function sum at 0x7f1f9c7b7af0>, <function mean at 0x7f1f9c7c1a30>)
Barray([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
B.sum()276
B.sum(axis=0)array([36, 40, 44, 48, 52, 56])
np.sum(B, axis=0)array([36, 40, 44, 48, 52, 56])
np.mean(B, axis=1)array([ 2.5, 8.5, 14.5, 20.5])
Adding, multiplying arrays componentwise
C = np.ones_like(B)Carray([[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1]])
Barray([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
B + Carray([[ 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12],
[13, 14, 15, 16, 17, 18],
[19, 20, 21, 22, 23, 24]])
B + 3*Carray([[ 3, 4, 5, 6, 7, 8],
[ 9, 10, 11, 12, 13, 14],
[15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26]])
Componentwise multiplicaiont (this is not the matrix product)
C * Barray([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
Matrix product
C @ B # fails because C is (4, 6) and B is (4, 6)ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 4 is different from 6)
C.shape, B.T.shape((4, 6), (6, 4))
C @ B.Tarray([[ 15, 51, 87, 123],
[ 15, 51, 87, 123],
[ 15, 51, 87, 123],
[ 15, 51, 87, 123]])
B.T @ Carray([[36, 36, 36, 36, 36, 36],
[40, 40, 40, 40, 40, 40],
[44, 44, 44, 44, 44, 44],
[48, 48, 48, 48, 48, 48],
[52, 52, 52, 52, 52, 52],
[56, 56, 56, 56, 56, 56]])
Shape
np.transpose, np.newaxis, np.expand_dims, np.reshape, np.swapaxes, np.moveaxis(<function transpose at 0x7f1f9c7b5830>,
None,
<function expand_dims at 0x7f1f9c50f570>,
<function reshape at 0x7f1f9c7b4330>,
<function swapaxes at 0x7f1f9c7b5670>,
<function moveaxis at 0x7f1f9c7cc430>)
B.shape(4, 6)
B.Tarray([[ 0, 6, 12, 18],
[ 1, 7, 13, 19],
[ 2, 8, 14, 20],
[ 3, 9, 15, 21],
[ 4, 10, 16, 22],
[ 5, 11, 17, 23]])
B.T.shape(6, 4)
B[np.newaxis, :, :].shape(1, 4, 6)
B[:, :, np.newaxis].shape(4, 6, 1)
B[:, np.newaxis, :].shape(4, 1, 6)
Concatenate, stacking arrays
np.stack, np.vstack, np.hstack, np.column_stack(<function stack at 0x7f1f9c7c2df0>,
<function vstack at 0x7f1f9c7c2a30>,
<function hstack at 0x7f1f9c7c2c30>,
<function column_stack at 0x7f1f9c50f670>)
A = np.random.normal(size=(2, 3))
AA= np.random.normal(size=(2, 3))
B = np.random.normal(size=(2, 2))
C = np.random.normal(size=(3, 3))The following concatenation/stacking operations are allowed
stack by creating a new axis
np.stack([A, AA]).shape(2, 2, 3)
print(np.stack([A, AA, A, AA]).shape)
np.stack([A, AA, A, AA])(4, 2, 3)
array([[[ 1.09640081, 2.30891361, 0.86050472],
[-0.53307374, -0.79932614, 0.03586915]],
[[ 0.81326335, 0.78246611, -0.01831325],
[ 0.91955669, 0.71259824, 0.51169635]],
[[ 1.09640081, 2.30891361, 0.86050472],
[-0.53307374, -0.79932614, 0.03586915]],
[[ 0.81326335, 0.78246611, -0.01831325],
[ 0.91955669, 0.71259824, 0.51169635]]])
# the first index of the newly created axis (first new axis) contais A
# the second index of the newly created axis (first new axis) contais AA
assert np.allclose(np.stack([A, AA])[0], A)
assert np.allclose(np.stack([A, AA])[1], AA)np.stack([A, AA, A**2, 7*A]).shape(4, 2, 3)
vertical stacking, without creating a new axis
np.vstack([A, AA]).shape(4, 3)
np.stack([A, AA])array([[[ 1.09640081, 2.30891361, 0.86050472],
[-0.53307374, -0.79932614, 0.03586915]],
[[ 0.81326335, 0.78246611, -0.01831325],
[ 0.91955669, 0.71259824, 0.51169635]]])
np.vstack([A, AA])array([[ 1.09640081, 2.30891361, 0.86050472],
[-0.53307374, -0.79932614, 0.03586915],
[ 0.81326335, 0.78246611, -0.01831325],
[ 0.91955669, 0.71259824, 0.51169635]])
A.shape, C.shape((2, 3), (3, 3))
np.vstack([A, C]).shape(5, 3)
A.shape, B.shape((2, 3), (2, 2))
np.vstack([A, B]).shapeValueError: all the input array dimensions except for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 3 and the array at index 1 has size 2
Horizontal stacking
np.hstack([A, AA]).shape(2, 6)
np.hstack([A, AA, B]).shape(2, 8)
A.shape, C.shape((2, 3), (3, 3))
# Horizontal stacking fails
np.hstack([A, C])ValueError: all the input array dimensions except for the concatenation axis must match exactly, but along dimension 0, the array at index 0 has size 2 and the array at index 1 has size 3
column_stack performs horizontal stacking, allowing both 2D and 1D arrays of the form (n, p) and (n, )
vec = np.random.uniform(size=(2, ))
vec.shape, A.shape((2,), (2, 3))
np.column_stack([A, vec]).shape(2, 4)
# same as first reshaping vec to (2, 1)
assert np.allclose(np.column_stack([A, vec]),
np.column_stack([A, vec.reshape((2, 1))]))Concatenate lets us specify an axis along which to stack
A.shape, B.shape((2, 3), (2, 2))
np.concatenate([A, B], axis=1).shape(2, 5)
A.shape, C.shape((2, 3), (3, 3))
Selecting columns or rows based on values/comparison
U = np.arange(24).reshape((4, 6))
Uarray([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
U.sum(axis=1)array([ 15, 51, 87, 123])
U.sum(axis=1) > 60array([False, False, True, True])
# rows with row-sum at last 60
U[ U.sum(axis=1) > 60, :]array([[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
Columns with a minimum element smaller or equal to 2
Uarray([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
U[:, U.min(axis=0) <= 2]array([[ 0, 1, 2],
[ 6, 7, 8],
[12, 13, 14],
[18, 19, 20]])
Matrix multiplication, matrix inversion, matrix determinant
M = np.random.uniform(size=(2,2)).round(2) # a two-by-two matrix
Marray([[0.72, 0.21],
[0.4 , 0.32]])
M.Tarray([[0.72, 0.4 ],
[0.21, 0.32]])
M @ M # matrix productarray([[0.6024, 0.2184],
[0.416 , 0.1864]])
np.linalg.inv, np.linalg.det, np.linalg.matrix_rank, np.linalg.solve(<function inv at 0x7f1f9c6d1730>,
<function det at 0x7f1f9c6d2770>,
<function matrix_rank at 0x7f1f9c6d2370>,
<function solve at 0x7f1f9c6879f0>)
np.linalg.inv(M)array([[ 2.18579235, -1.43442623],
[-2.73224044, 4.91803279]])
np.linalg.inv(M) @ Marray([[ 1.00000000e+00, -5.55111512e-17],
[ 2.22044605e-16, 1.00000000e+00]])
np.allclose(np.linalg.inv(M) @ M, np.eye(2))True
Matrix operations and 3+ dimensions: Stacks of matrix
A stack of three 2x2 matrices
MM = np.random.normal(size=(2, 2)).round(2)
MMM = np.random.normal(size=(2, 2)).round(2)
S = np.stack([M, MM, MM])
Sarray([[[ 0.72, 0.21],
[ 0.4 , 0.32]],
[[ 1.62, -0.29],
[ 0.17, 0.77]],
[[ 1.62, -0.29],
[ 0.17, 0.77]]])
Matrix operations apply to a single matrix, or simultaneously to each matrix in the stack
np.linalg.det(M)0.1464
np.linalg.det(MM)1.2967000000000002
np.linalg.det(MMM)-0.24600000000000002
np.linalg.det(S)array([0.1464, 1.2967, 1.2967])
np.linalg.inv(S)array([[[ 2.18579235, -1.43442623],
[-2.73224044, 4.91803279]],
[[ 0.59381507, 0.22364464],
[-0.13110203, 1.24932521]],
[[ 0.59381507, 0.22364464],
[-0.13110203, 1.24932521]]])
Broadcasting: automatic expansion of arrays that do not have the same size
V = np.arange(12).reshape(3, 4)
u3 = np.arange(3)*0.1 # vector size 3
u4 = np.arange(4)*0.01 # vector size 4
Varray([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
u3array([0. , 0.1, 0.2])
matrix product: matrix (3, 4) times vector (4, )
V @ u4array([0.14, 0.38, 0.62])
V + u3ValueError: operands could not be broadcast together with shapes (3,4) (3,)
u3.reshape((3, 1))array([[0. ],
[0.1],
[0.2]])
Varray([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
V + u3.reshape((3, 1)) # broadcasting u3 horizontally along axis of length 1array([[ 0. , 1. , 2. , 3. ],
[ 4.1, 5.1, 6.1, 7.1],
[ 8.2, 9.2, 10.2, 11.2]])
# Same as copying u3 four times:
np.allclose(
V + u3.reshape((3, 1)),
V + np.column_stack([u3, u3, u3, u3]))True
V * u4.reshape((1, 4)) # broadcasting u4 vertically along axis of length 1array([[0. , 0.01, 0.04, 0.09],
[0. , 0.05, 0.12, 0.21],
[0. , 0.09, 0.2 , 0.33]])
# Same as the sum of three u4 stacked vertically:
np.allclose(
V * u4.reshape((1, 4)),
V * np.stack([u4, u4, u4 ]))True
np.random.normal(size=(4, 4)) * np.random.normal(size=( 4, 1))array([[-1.05536828e+00, -1.45665816e+00, -2.67210738e-02,
-1.30359472e+00],
[-3.18971599e+00, -2.55942816e+00, -3.02727644e+00,
-9.09620851e-02],
[ 3.02577245e-03, -6.85844562e-03, 1.84479952e-03,
5.14571677e-03],
[ 6.86403995e-01, -3.58362051e-01, 6.80766710e-01,
-4.92289423e-01]])
np.random.normal(size=(4, 4)) * np.random.normal(size=( 4, 1))array([[ 0.39426168, -0.31273592, -2.69650199, -1.83475489],
[ 1.02741182, 0.51285263, -1.34163647, -0.62279637],
[-0.06590297, 0.23226254, -0.10689879, 0.17579929],
[-0.43327821, 0.33308693, 0.05473837, 0.4973698 ]])
np.random.normal(size=(2, 3, 4)) * np.random.normal(size=(1, 1, 4))array([[[ 8.16248889e-02, -1.34795777e-01, -2.57715064e+00,
-2.23339041e+00],
[-1.94035393e-01, 3.23548528e-01, 3.22602216e-05,
-1.27705988e+00],
[ 2.53878986e-03, -5.39723529e-02, 1.24454397e+00,
-1.26687821e-01]],
[[ 5.43656925e-02, 3.77687041e-01, 1.78233734e+00,
-1.81216835e+00],
[-1.10908238e-01, -3.24302457e-01, 2.68640291e+00,
-2.37375553e+00],
[ 1.66429948e-02, -1.49013948e-02, 3.58979607e-02,
-9.55966914e-01]]])
Multiply many matrices:
S = np.stack([np.random.randn(2, 2) for a in range(5)]).round(2)
S.shape
Sarray([[[ 1.45, -1.05],
[-1.15, -0.77]],
[[-0.08, -0.49],
[-1.78, -0.92]],
[[-0.87, 0.21],
[-2. , 0.56]],
[[-2.09, 0.6 ],
[ 1.03, 2.39]],
[[-0.39, 0.29],
[-0.25, -0.74]]])
Sarray([[[ 1.45, -1.05],
[-1.15, -0.77]],
[[-0.08, -0.49],
[-1.78, -0.92]],
[[-0.87, 0.21],
[-2. , 0.56]],
[[-2.09, 0.6 ],
[ 1.03, 2.39]],
[[-0.39, 0.29],
[-0.25, -0.74]]])
Computing inverses of the five matrices one by one
S[0]array([[ 1.45, -1.05],
[-1.15, -0.77]])
S[0, :, :]array([[ 1.45, -1.05],
[-1.15, -0.77]])
np.linalg.inv(S[0])array([[ 0.3313253 , -0.45180723],
[-0.49483649, -0.62392427]])
With many matrices
S = np.stack([np.random.randn(2, 2) for a in range(100000)])
S.shape(100000, 2, 2)
from timeit import default_timer as timer# fast
start = timer()
np.linalg.inv(S)
end = timer()
print(end - start)0.01585862999490928
# slow
start = timer()
[np.linalg.inv(mat) for mat in S]
end = timer()
print(end - start)0.39238803800253663