import numpy as np
Lec 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>)
3) np.ones(
array([1., 1., 1.])
3).shape np.ones(
(3,)
4) np.zeros(
array([0., 0., 0., 0.])
4) np.eye(
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
4).shape np.eye(
(4, 4)
4)) np.zeros_like(np.eye(
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
4)).shape np.zeros_like(np.eye(
(4, 4)
4)) np.ones_like(np.eye(
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
7) np.arange(
array([0, 1, 2, 3, 4, 5, 6])
4, 8, num=9) np.linspace(
array([4. , 4.5, 5. , 5.5, 6. , 6.5, 7. , 7.5, 8. ])
0, 2, num=3) np.logspace(
array([ 1., 10., 100.])
Specify entries manually with a list
1.0, 2.0, 3.3]) np.array([
array([1. , 2. , 3.3])
= np.array([[1.0, 2.0, 3.0],
A 1.1, 2.2, 3.3]])
[ A
array([[1. , 2. , 3. ],
[1.1, 2.2, 3.3]])
A.shape
(2, 3)
Random numbers
=(3, 4)) np.random.uniform(size
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]])
=(3, 4)) np.random.normal(size
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
= np.ones(3, dtype=np.int8) o
o.dtype
dtype('int8')
Accessing array by single cells
1] o[
1
2] o[
1
A
array([[1. , 2. , 3. ],
[1.1, 2.2, 3.3]])
0, 0] A[
1.0
0, 1] A[
2.0
1, 2] A[
3.3
1, 3] A[
IndexError: index 3 is out of bounds for axis 1 with size 3
Accessing arrays with slices
= np.arange(24).reshape((4, 6))
B B
array([[ 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:
1:3, :] B[
array([[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17]])
# The two middle rows, only second column
1:3, 1] B[
array([ 7, 13])
# Use two columns (two ":") to skip some integers periodically
B
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
0:7:2] # keep every other column B[:,
array([[ 0, 2, 4],
[ 6, 8, 10],
[12, 14, 16],
[18, 20, 22]])
2] # keep every other column B[:, ::
array([[ 0, 2, 4],
[ 6, 8, 10],
[12, 14, 16],
[18, 20, 22]])
0:7:3] # keep one in three columns B[:,
array([[ 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
# these functions help to compare floats arrays for equality. np.isclose, np.allclose
(<function isclose at 0x7f1f9c7ccaf0>, <function allclose at 0x7f1f9c7cc970>)
-20), 0.) np.isclose(np.exp(
True
-3000) == 0. np.exp(
True
-40) == 0. np.exp(
False
Operations along axis:
sum, np.mean np.
(<function sum at 0x7f1f9c7b7af0>, <function mean at 0x7f1f9c7c1a30>)
B
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
sum() B.
276
sum(axis=0) B.
array([36, 40, 44, 48, 52, 56])
sum(B, axis=0) np.
array([36, 40, 44, 48, 52, 56])
=1) np.mean(B, axis
array([ 2.5, 8.5, 14.5, 20.5])
Adding, multiplying arrays componentwise
= np.ones_like(B) C
C
array([[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1]])
B
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
+ C B
array([[ 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12],
[13, 14, 15, 16, 17, 18],
[19, 20, 21, 22, 23, 24]])
+ 3*C B
array([[ 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)
* B C
array([[ 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
@ B # fails because C is (4, 6) and B is (4, 6) C
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))
@ B.T C
array([[ 15, 51, 87, 123],
[ 15, 51, 87, 123],
[ 15, 51, 87, 123],
[ 15, 51, 87, 123]])
@ C B.T
array([[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.T
array([[ 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>)
= np.random.normal(size=(2, 3))
A = np.random.normal(size=(2, 3))
AA= np.random.normal(size=(2, 2))
B = np.random.normal(size=(3, 3)) C
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)
**2, 7*A]).shape np.stack([A, AA, A
(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]).shape
ValueError: 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, )
= np.random.uniform(size=(2, ))
vec 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]),
2, 1))])) np.column_stack([A, vec.reshape((
Concatenate lets us specify an axis along which to stack
A.shape, B.shape
((2, 3), (2, 2))
=1).shape np.concatenate([A, B], axis
(2, 5)
A.shape, C.shape
((2, 3), (3, 3))
Selecting columns or rows based on values/comparison
= np.arange(24).reshape((4, 6))
U U
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
sum(axis=1) U.
array([ 15, 51, 87, 123])
sum(axis=1) > 60 U.
array([False, False, True, True])
# rows with row-sum at last 60
sum(axis=1) > 60, :] U[ U.
array([[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
Columns with a minimum element smaller or equal to 2
U
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])
min(axis=0) <= 2] U[:, U.
array([[ 0, 1, 2],
[ 6, 7, 8],
[12, 13, 14],
[18, 19, 20]])
Matrix multiplication, matrix inversion, matrix determinant
= np.random.uniform(size=(2,2)).round(2) # a two-by-two matrix
M M
array([[0.72, 0.21],
[0.4 , 0.32]])
M.T
array([[0.72, 0.4 ],
[0.21, 0.32]])
@ M # matrix product M
array([[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]])
@ M np.linalg.inv(M)
array([[ 1.00000000e+00, -5.55111512e-17],
[ 2.22044605e-16, 1.00000000e+00]])
@ M, np.eye(2)) np.allclose(np.linalg.inv(M)
True
Matrix operations and 3+ dimensions: Stacks of matrix
A stack of three 2x2 matrices
= np.random.normal(size=(2, 2)).round(2)
MM = np.random.normal(size=(2, 2)).round(2)
MMM = np.stack([M, MM, MM])
S S
array([[[ 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
= np.arange(12).reshape(3, 4)
V = np.arange(3)*0.1 # vector size 3
u3 = np.arange(4)*0.01 # vector size 4
u4 V
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
u3
array([0. , 0.1, 0.2])
matrix product: matrix (3, 4) times vector (4, )
@ u4 V
array([0.14, 0.38, 0.62])
+ u3 V
ValueError: operands could not be broadcast together with shapes (3,4) (3,)
3, 1)) u3.reshape((
array([[0. ],
[0.1],
[0.2]])
V
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
+ u3.reshape((3, 1)) # broadcasting u3 horizontally along axis of length 1 V
array([[ 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(+ u3.reshape((3, 1)),
V + np.column_stack([u3, u3, u3, u3])) V
True
* u4.reshape((1, 4)) # broadcasting u4 vertically along axis of length 1 V
array([[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(* u4.reshape((1, 4)),
V * np.stack([u4, u4, u4 ])) V
True
=(4, 4)) * np.random.normal(size=( 4, 1)) np.random.normal(size
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]])
=(4, 4)) * np.random.normal(size=( 4, 1)) np.random.normal(size
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 ]])
=(2, 3, 4)) * np.random.normal(size=(1, 1, 4)) np.random.normal(size
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:
= np.stack([np.random.randn(2, 2) for a in range(5)]).round(2)
S
S.shape S
array([[[ 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]]])
S
array([[[ 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
0] S[
array([[ 1.45, -1.05],
[-1.15, -0.77]])
0, :, :] S[
array([[ 1.45, -1.05],
[-1.15, -0.77]])
0]) np.linalg.inv(S[
array([[ 0.3313253 , -0.45180723],
[-0.49483649, -0.62392427]])
With many matrices
= np.stack([np.random.randn(2, 2) for a in range(100000)])
S S.shape
(100000, 2, 2)
from timeit import default_timer as timer
# fast
= timer()
start
np.linalg.inv(S)= timer()
end print(end - start)
0.01585862999490928
# slow
= timer()
start for mat in S]
[np.linalg.inv(mat) = timer()
end print(end - start)
0.39238803800253663