import numpy as npNumpy Arithmetics
Module 3: NumPy
NumPy: Arithmetics
Element-Wise Operations
With NumPy, you can easily perform array-with-array arithmetic, or scalar-with-array arithmetic.
# Create two arrays
a = np.array([1, 2, 3, 4])
b = np.array([4, 3, 2, 1])# Addition
c = a + b
print(c)# Subtraction
c = a - b
print(c)# Multiplication element-wise
c = a * b
print(c)# Division element-wise
c = a / b
print(c)# Power element-wise
c = a ** b
print(c)Exercise: How would you create an array containing 50 eights?
# Try itExercise: How would you create an array containing all the powers of 2 from \(2^0\) to \(2^{10}\)?
# Try itBroadcasting in NumPy
Broadcasting is a powerful mechanism that allows NumPy to work with arrays of different shapes when performing arithmetic operations.
# Create a scalar and an array
scalar = 5
arr = np.array([1, 2, 3, 4])
# Add scalar to array through broadcasting
print(scalar + arr)Broadcasting becomes extremely powerful when dealing with multidimensional arrays.
arr1 = np.ones((3, 3))
print(arr1)arr2 = np.array([-1, 0, 1])
print(arr2)# Use broadcasting
arr3 = arr1 + arr2
print(arr3)print(arr3[0])How does broadcasing works?
- NumPy compares the shapes of the arrays element-wise, starting from the trailing dimensions.
- If the dimensions are equal, or one of them is 1, they are considered compatible.
- NumPy then “stretches” the smaller array along the dimension with size 1 to match the larger array.
Example of broadcasting: - Let’s say you have a 2D array (matrix) A with shape (m, n) and a 1D vector v with shape (n,). - The array A has m rows and n columns, while the vector v has n elements.
A = np.array([[1, 2],
[3, 4],
[5, 6]])
print(A.shape)v = np.array([1, 0])
print(v.shape)# The last dimension matches! They can be added
result = A + v
print(result)In this case, the sum is performed along the columns of A (dimension 1) because v is added to each row of A.
If v had shape (m,) instead, an error would occur.
A = np.array([[1, 2],
[3, 4],
[5, 6]])
print(A.shape)
v = np.array([1, 0, -1])
print(v.shape)
result = A + v
print(result)Practice
Deactivate AI assistant tools, and try the following exercises.
Exercise: Perform the following operation:
\[5 \cdot A \cdot v\]
Where \[A=\begin{pmatrix} 1 & 2 & 3 & 4\\ 5 & 6 & 7 & 8\\ 9 & 10 & 11 & 12 \end{pmatrix}\] and \(v = [-2, -1, 0, 1]\)
Exercise: Given any matrix \(A\), multiply the elements of its first column times 1, the second column times 2, the third times 3, and so on.
For instance:
\[A = \begin{pmatrix} 1 & 2 & 3\\ 4 & 5 & 6\\ \end{pmatrix}\]
Would end up being: \[A' = \begin{pmatrix} 1 & 4 & 9\\ 4 & 10 & 18\\ \end{pmatrix}\]
# Try itMathematical Functions
arr = np.array([1, 2, 3, 4, 5])
# Square root
print(np.sqrt(arr))# For higher roots, we can just operate as we do with arrays
print(arr**(1/3))So why use np.sqrt if we can do **(1/2)?
Because the NumPy function is optimized. We can see it by executing the computation many times and looking at its execution time.
arr = np.arange(1, 10000) # We use a very big array
for _ in range(10000):
np.sqrt(arr)
# We are not printing anything, because we do not want to fill the output
# We only want to see how much time this takes!for _ in range(10000):
arr**(1/2)
# We are not printing anything, because we do not want to fill the output
# We only want to see how much time this takes!The same logic applies to exponentiation!
arr = np.array([1, 2, 3, 4, 5])
# Exponentiation
print(np.exp(arr))Geometry Functions
NumPy can do more than just element-wise operations. It also supports matrix multiplication, transposition, and other matrix math.
Matrix multiplication:
\[A\ B = \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix} \begin{pmatrix} b_{11} & b_{12} \\ b_{21} & b_{22} \end{pmatrix} = \begin{pmatrix} a_{11}b_{11} + a_{12}b_{21} & a_{11}b_{12} + a_{12}b_{22} \\ a_{21}b_{11} + a_{22}b_{21} & a_{21}b_{12} + a_{22}b_{22} \end{pmatrix}\]
# Matrix multiplication (option A)
c = np.matmul(a, b)
print(c)# Matrix multiplication (option B)
c = a @ b
print(c)Dot product?: The term “dot product” can be confussing when applied to matrices. For some, a dot product of two matrices is the following.
\[A \cdot B = \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix} \cdot \begin{pmatrix} b_{11} & b_{12} \\ b_{21} & b_{22} \end{pmatrix} = a_{11} b_{11} + a_{12} b_{12} + a_{21} b_{21} + a_{22} b_{22}\]
However, for NumPy, a dot product of two matrices is a matrix mutliplication.
\[A \cdot B = \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix} \cdot \begin{pmatrix} b_{11} & b_{12} \\ b_{21} & b_{22} \end{pmatrix} = \begin{pmatrix} a_{11} b_{11} & a_{21} b_{12} \\ a_{12} b_{21} & a_{22} b_{22} \end{pmatrix}\]
# Dot product?
c = np.dot(a, b)
print(c)Very careful with np.dot()!
Using np.dot() with two matrices, it will compute the matrix multiplication, not their dot product!
# To compute the actual dot product
c = np.sum(a * b)
print(c)Both dot and matmul are used for matrix multiplication in NumPy, but they behave differently, especially when dealing with higher-dimensional arrays (i.e., tensors). The primary difference emerges in multi-dimensional array multiplication.
The official documentation says: matmul differs from dot in two important ways.
- Multiplication by scalars is not allowed.
- Stacks of matrices are broadcast together as if the matrices were elements.
Cross product:
\[A \times B = \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix} \times \begin{pmatrix} b_{11} & b_{12} \\ b_{21} & b_{22} \end{pmatrix} = \begin{pmatrix} a_{12}b_{21} - a_{11}b_{22} \\ a_{21}b_{12} - a_{22}b_{11} \end{pmatrix}\]
# Create two arrays
a = np.array([[1, 2], [3, 4]])
b = np.array([[4, 3], [2, 1]])
# Cross product
c = np.cross(a, b)
print(c)Transposition:
\[A^T = \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix}^T = \begin{pmatrix} a_{11} & a_{21} \\ a_{12} & a_{22} \end{pmatrix}\]
a = np.array([[1, 2, 3], [4, 5, 6]])
print(a)
# Matrix transposition
b = a.T
print(b)Determinants provide information about the matrix’s invertibility, while the inverse of a matrix is crucial for solving systems of linear equations.
Exercise: Assuming the matrix you created is \(A\), perform the following operation (cross product):
\[A \times B / 6\]
Where
\[B=\begin{pmatrix} -1 & 0 & 0 & -1\\ 0 & 1 & 1 & 0\\ 1 & -1 & 1 & -1 \end{pmatrix}\]
Linear Algebra Functions
a = np.array([[1, 2], [3, 4]])
det_a = np.linalg.det(a)
print(det_a)a = np.array([[1, 2], [3, 4]])
inverse_a = np.linalg.inv(a)
print(inverse_a)Trigonometric Functions
NumPy operates in radians. If you are using degrees, make sure to convert them first!
angles = np.array([0, 30, 45, 60, 90]) # in degrees
# NumPy operates in radians!
angles_rad = np.radians(angles)
print(angles_rad)sines = np.sin(angles_rad)
print(sines)cosines = np.cos(angles_rad)
print(cosines)tangents = np.tan(angles_rad)
print(tangents)Statistical Functions
The mean is the average of a data set.
arr = np.array([1, 2, 3, 4, 5])
# Mean
print("Mean:", np.mean(arr))The median is the middle value when the data set is ordered.
arr = np.array([1, 2, 3, 4, 5])
# Median
print("Median:", np.median(arr))The standard deviation measures the dispersion of data from its mean.
arr = np.array([1, 2, 3, 4, 5])
# Standard Deviation
print("Standard Deviation:", np.std(arr))Variance is the square of the standard deviation.
arr = np.array([1, 2, 3, 4, 5])
# Variance
print("Variance:", np.var(arr))Exercise: Given a set of student grades, compute the mean, median, mode, standard deviation, and variance.
Sorting and Searching
grades = np.array([85, 90, 78, 92, 88])
sorted_indices = np.argsort(grades)
print(f"Indices to sort grades: {sorted_indices}")max_grade_index = np.argmax(grades)
print(f"Index of highest grade: {max_grade_index}")min_grade_index = np.argmin(grades)
print(f"Index of lowest grade: {min_grade_index}")Aggregation Functions
arr = np.array([[1, 2], [3, 4]])
print("Array:\n", arr)
total_sum = np.sum(arr)
print(f"Sum: {total_sum}")# We sum across dimension 0
col_sum = np.sum(arr, axis=0)
print(f"Sum of columns: {col_sum}")# We sum across dimension 1
row_sum = np.sum(arr, axis=1)
print(f"Sum of rows: {row_sum}")cumulative_sum = np.cumsum(arr)
print(f"Cumulative Sum: {cumulative_sum}")product = np.prod(arr)
print(f"Product: {product}")Practice
Deactivate AI assistant tools, and try the following exercises.
Exercise: Given a “magic square” matrix \(A\):
\[A = \begin{pmatrix} 23 & 28 & 21\\ 22 & 24 & 26\\ 27 & 20 & 25 \end{pmatrix}\]
Normalize its values (subtract mean and divide by standard deviation). We call the new matrix \(B\).
\[B[i,j] = \frac{A[i,j] - \text{mean}(A)}{\text{std}(A)}\]
arr = np.array([[23, 28, 21], [22, 24, 26], [27, 20, 25]])
b = (arr - np.mean(arr)) / np.std(arr)
bExercise: Compute \(B^T\).
np.dot(arr, b.T)Exercise: Compute \(A \cdot B^T\).
Example: Can you find some interesting properties about this magic square matrix \(A\)?
Try to sum its rows, its columns, its diagonal.