Array Operations#

import numpy as np

All Python operators can be applied to NumPy arrays, where all operations work elementwise.

Mathematical Operations#

For instance, we can easily add two vectors or two matrices by using the + operator.

a = np.array([1, 2, 3])
b = np.array([9, 8, 7])

c = a + b

print(c)
[10 10 10]

Important

Because all operations work elementwise, the * operator on two-dimensional arrays does NOT multiply the two matrices in the mathematical sense, but simply yields the matrix of elementwise products. Mathematical matrix multiplication will be discussed in Linear Algebra Functions.

NumPy reimplements almost all mathematical functions, like sine, cosine and so on. NumPy’s functions take arrays as arguments and apply the mathematical functions elementwise. Have a look at Mathematical functions in NumPy’s documentation for a list of available functions.

a = np.array([1, 2 , 3])

b = np.sin(a)

print(b)
[0.84147098 0.90929743 0.14112001]

Hint

Functions min and amin are equivalent. The amin variant exists to avoid confusion and name conflicts with Python’s built-in function min. Writing np.min is okay.

Important

Functions np.min and np.minimum do different things. With min we get the smallest value in an array, whereas minimum yields the elementwise minimum of two equally sized arrays. With np.argmin we get the index (not the value) of the minimal element of an array.

Comparing Arrays#

Comparisons work elementwise, too.

a = np.array([1, 2, 3])
b = np.array([-1, 3, 3])

print(a > b)
[ True False False]

Comparisons result in NumPy arrays of data type bool. The function np.any returns True if and only if at least one item of the argument is True. The function np.all returns True if and only if all items of the argument are True.

a = np.array([True, True, False])

print(np.any(a))
print(np.all(a))
True
False

Some of NumPy’s functions also are accessible as methods of ndarray objects. Examples are any and all:

a = np.array([True, True, False])

print(a.any())
print(a.all())
True
False

To combine several conditions you might use logical_and and friends. Using Python’s and (and friends) results in an error, because Python tries to convert a NumPy array to a bool value and it’s not clear how to do this (any or all?).

Broadcasting#

If dimensions of the operands of a binary operation do not fit (short vector plus long vector, for instance) an exception is raised. But in some cases NumPy uses a technique called broadcasting to make dimensions fit by cloning suitable subarrays. Examples:

a = np.array([[1, 2, 3]])    # 1 x 3
b = np.ones((4, 3))          # 4 x 3

c = a + b

print(a, '\n')
print(b, '\n')
print(c)
print(c.shape)
[[1 2 3]] 

[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]] 

[[2. 3. 4.]
 [2. 3. 4.]
 [2. 3. 4.]
 [2. 3. 4.]]
(4, 3)
a = np.array([[1, 2, 3]])              # 1 x 3
b = np.array([[1], [2], [3], [4]])     # 4 x 1

c = a + b

print(a, '\n')
print(b, '\n')
print(c, '\n')
print(c.shape)
[[1 2 3]] 

[[1]
 [2]
 [3]
 [4]] 

[[2 3 4]
 [3 4 5]
 [4 5 6]
 [5 6 7]] 

(4, 3)
a = np.array([1, 2, 3])    #     3 (one-dimensional)
b = np.ones((4, 3))        # 4 x 3

c = a + b

print(a, '\n')
print(b, '\n')
print(c)
print(c.shape)
[1 2 3] 

[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]] 

[[2. 3. 4.]
 [2. 3. 4.]
 [2. 3. 4.]
 [2. 3. 4.]]
(4, 3)

Broadcasting follows two simple rules:

  • If one array has fewer dimensions than the other, add dimensions of size 1 till dimensions are equal.

  • Compare array sizes in each dimension. If they equal, do nothing. If they differ and one array has size 1 in the dimension under consideration, clone the array with size 1 sufficiently often to fit the other array’s size.

Broadcasting makes life much easier. On the one hand it allows for operations where one operand is a scalar:

a = np.array([[1, 2, 3], [4, 5, 6]])    # 2 x 3
b = 7                                   #     1 (one-dimensional)

c = a + b

print(a, '\n')
print(b, '\n')
print(c)
print(c.shape)
[[1 2 3]
 [4 5 6]] 

7 

[[ 8  9 10]
 [11 12 13]]
(2, 3)

On the other hand broadcasting allows for efficient column or row operations:

# multiply columns by different values

a = np.array([[1, 2, 3], [4, 5, 6]])    # 2 x 3
b = np.array([[0.5], [2]])              # 2 x 1

c = a * b

print(a, '\n')
print(b, '\n')
print(c)
print(c.shape)
[[1 2 3]
 [4 5 6]] 

[[0.5]
 [2. ]] 

[[ 0.5  1.   1.5]
 [ 8.  10.  12. ]]
(2, 3)

For higher-dimensional examples have a look at Broadcasting in NumPy’s documentation.