Part 1. Numpy for numerical computations#
1.1 Introduction to Numpy#
Numpy is a powerful library for numerical computations in Python. It provides support for arrays, matrices, and a wide range of mathematical functions to operate on these data structures. The full official documentation for Numpy can be found here.
1.2 Numpy Objects: N-dimensional Arrays#
Numpy uses objects called arrays. They are similar to Python lists but offer more functionality and are more efficient for numerical operations. They are also significantly faster to use in computations than Python lists. The speed-up is that numpy arrays must contain elements of the same data type (homogeneous data structures), as opposed to python lists which can contain multiple different data types (heterogeneous data structures).
Most NumPy arrays have some restrictions. For instance:
All elements of the array must be of the same type of data.
The shape must be “rectangular”, not “jagged”; e.g., each row of a two-dimensional array must have the same number of columns.
We will use the word “array” to refer to an instance of ndarray.
Then, we can create and manipulate arrays of any dimension by using the np.array() function:
Syntax:
arr = np.array(data, dtype)
import numpy as np
# Creating a 1D array from a list
arr1d = np.array([1, 2, 3, 4, 5])
print("\nArray 1D:\n ", arr1d)
# Creating a 2D array from a list of lists
arr2d = np.array([[1, 2, 3], [4, 5, 6]])
print("\nArray 2D:\n",arr2d)
# Creating a 2D array from a list of lists with a specified data type
arr2d = np.array([[1, 2, 3], [4, 5, 6]], dtype=float)
print("\nArray 2D:\n", arr2d)
NumPy also supports 0-dimensional arrays, however they are not very useful.
# Creating a 0D array
arr0d = np.array(42)
print(arr0d)
print(arr0d.ndim)
Short exercise: Create a 1D array, a 2D array, and a 3D array with different data types. 1D array with integers, 2D array with floats, and 3D array with complex numbers.
Example here!
import numpy as np
# Creating a 1D array with integers
arr1d = np.array([1, 2, 3, 4, 5], dtype=int)
# Creating a 2D array with floats
arr2d = np.array([[1.1, 2.2, 3.3], [4.4, 5.5, 6.6]], dtype=float)
# Creating a 3D array with complex numbers
arr3d = np.array([[[1+2j, 3+4j], [5+6j, 7+8j]], [[9+10j, 11+12j], [13+14j, 15+16j]]], dtype=complex)
In some cases it can be useful to create arrays with specific type of floating point numbers. Numpy supports several floating point types, each with different precision and memory requirements. The most commonly data types in Numpy are:
Data Type |
Description |
|---|---|
int8 |
8-bit signed integer |
int16 |
16-bit signed integer |
int32 |
32-bit signed integer |
int64 |
64-bit signed integer |
float16 |
Half precision float: sign bit, 5 bits exponent, 10 bits fraction |
float32 |
Single precision float: sign bit, 8 bits exponent, 23 bits fraction |
float64 |
Double precision float: sign bit, 11 bits exponent, 52 bits fraction |
1.2.1 Array Attributes#
The most important attributes of a Numpy array are:
ndarray.ndim: Returns the number of dimensions (axes) of the array.ndarray.shape: Returns a tuple representing the dimensions of the array.ndarray.size: Returns the total number of elements in the array.ndarray.dtype: Returns the data type of the elements in the array.
Short exercise: From the previous exercise, print the attributes of the 1D, 2D and 3D array you created.
Example here!
# Printing attributes of the 1D array
print("1D Array Attributes:\n")
print("\nData type:\n", arr1d.dtype) # This is just a nice way to print with a title
print("\nShape:\n", arr1d.shape)
print("\nDimension:\n", arr1d.ndim)
print("\nSize:\n", arr1d.size)
# Printing attributes of the 2D array
print(ºn"2D Array Attributes:\")
print("\nData type:\n", arr2d.dtype)
print("\nShape:\n", arr2d.shape)
print("\nDimension:\n", arr2d.ndim)
print("\nSize:\n", arr2d.size)
# Printing attributes of the 3D array
print("\n3D Array Attributes:\n")
print("\nData type:\n", arr3d.dtype)
print("\nShape:\n", arr3d.shape)
print("\nDimension:\n", arr3d.ndim)
print("\nSize:\n", arr3d.size)
1.2.2 Array Indexing and Slicing#
There are some very useful methods to access and manage elements in a Numpy arrays. Some of the most commonly used techniques are:
Indexing: You can access individual elements of an array using their indices. Remember that Numpy uses zero-based indexing, so the first element has an index of 0. Indexes always are enclosed in square brackets
[].
General syntax for indexing:
arr[i] # 1D array
arr[i, j] # 2D array
arr[i, j, k] # 3D array, where i, j, k are the indices of the elements you want to access.
Short exercise: From the previous arrays you created, access the first element of the 1D array, the element in the first row and second column of the 2D array, and the element in the second block, first row, and second column of the 3D array.
Example here!
# Accessing elements from the arrays created in the previous exercise
first_element_1d = arr1d[0]
element_2d = arr2d[0, 1]
element_3d = arr3d[1, 0, 1]
print("First element of 1D array:", first_element_1d)
print("Element in first row and second column of 2D array:", element_2d)
print("Element in second block, first row, and second column of 3D array:", element_3d)
Slicing: You can extract subarrays using slicing. For example,
arr[1:4]extracts elements from index 1 to 3 in a 1D array, andarr[0:2, 1:3]extracts a subarray from the first two rows and columns 1 to 2 in a 2D array.
General syntax for slicing:
arr[start:stop:step] # 1D array
arr[start_row:stop_row, start_col:stop_col] # 2D array
arr[start_block:stop_block, start_row:stop_row, start_col:stop_col] # 3D array
Short exercise: From the previous arrays you created, slice the first three elements of the 1D array, the first two rows and first two columns of the 2D array, and the first block, first two rows, and first two columns of the 3D array.
Example here!
# Slicing elements from the arrays created in the previous exercise
slice_1d = arr1d[0:3]
slice_2d = arr2d[0:2, 0:2]
slice_3d = arr3d[0:1, 0:2, 0:2]
print("Sliced first three elements of 1D array:\n", slice_1d)
print("Sliced first two rows and first two columns of 2D array:\n", slice_2d)
print("Sliced first block, first two rows, and first two columns of 3D array:\n", slice_3d)
Boolean Indexing: We can also use boolean arrays to index another array to match conditions. For example,
arr[arr > 5]returns all elements inarrthat are greater than 5.
Short exercise: From any of previous arrays, use boolean indexing to get all elements greater than an arbitrary value.
Example here!
# Using boolean indexing to get elements greater than 3 from the 2D array
bool_indexed_elements_2d = arr2d[arr2d > 3]
print("Original 2D array:\n", arr2d)
print("\nElements greater than 3:\n", bool_indexed_elements_2d)
Fancy Indexing: You can use arrays of indices to access multiple elements at once. For example,
arr[[0, 2, 4]]returns the elements at indices 0, 2, and 4.
Short exercise: Try creating a 1D array and using fancy indexing to access first, third, and fourth elements.
Example here!
# Creating a 1D array
arr1d = np.array([10, 20, 30, 40, 50])
# Using fancy indexing to access specific elements
fancy_indexed_elements_1d = arr1d[[0, 2, 3]]
print("Original 1D array:\n", arr1d)
print("Elements at indices 0, 2, and 3:\n", fancy_indexed_elements_1d)
1.2.3 Array Creation Functions#
Numpy provides several functions to create arrays with specific values or patterns. Some of the most commonly used functions are:
Function |
Description |
|---|---|
|
Creates an array from an object (e.g., list, tuple). |
|
Creates an array filled with zeros. |
|
Creates an array filled with ones. |
|
Creates an array filled with a specified value. |
|
Creates an identity matrix. |
|
Creates an array with evenly spaced values within a given interval. |
|
Creates an array with a specified number of evenly spaced values between two endpoints. |
|
Creates an array with random values from a uniform distribution over [0, 1). |
|
Sets the seed for the random number generator to ensure reproducibility. |
|
Converts an input to a Numpy array. |
Short exercise: With the methods above, create a 3x3 identity matrix, a 2x4 array filled with ones, and a 1D array with 10 evenly spaced values between 0 and 1.
Example here!
# Creating a 3x3 identity matrix
identity_matrix = np.eye(3)
print("3x3 Identity Matrix:\n", identity_matrix)
# Creating a 2x4 array filled with ones
ones_array = np.ones((2, 4))
print("\n2x4 Array filled with ones:\n", ones_array)
# Creating a 1D array with 10 evenly spaced values between 0 and 1
evenly_spaced_array = np.linspace(0, 1, 10)
print("\n1D Array with 10 evenly spaced values between 0 and 1:\n", evenly_spaced_array)
1.2.4 Array Operations#
Numpy supports a wide range of operations on arrays, including arithmetic operations, statistical functions, and linear algebra operations. Some common operations include:
Arithmetic operations:#
You can perform any arithmetic operations on arrays of the same shape.
Operation |
Description |
Example |
|---|---|---|
Addition (+) |
Adds corresponding elements of two arrays. |
|
Subtraction (-) |
Subtracts corresponding elements of two arrays. |
|
Multiplication (*) |
Multiplies corresponding elements of two arrays. |
|
Division (/) |
Divides corresponding elements of two arrays. |
|
Floor Division (//) |
Performs floor division on corresponding elements of two arrays. |
|
Modulus (%) |
Computes the modulus of corresponding elements of two arrays. |
|
Exponentiation (**) |
Raises elements of the first array to the power of corresponding elements of the second array. |
|
Short exercise using arithmetic operations: Create two 2D arrays of the same shape and perform addition, subtraction, multiplication, and division on them.
Example here!
# Creating two 2D arrays of the same shape
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
# Performing arithmetic operations
addition = arr1 + arr2
subtraction = arr1 - arr2
multiplication = arr1 * arr2
division = arr1 / arr2
print("Array 1:\n", arr1)
print("\nArray 2:\n", arr2)
print("\nAddition:\n", addition)
print("\nSubtraction:\n", subtraction)
print("\nMultiplication:\n", multiplication)
print("\nDivision:\n", division)
Statistical functions#
Numpy provides functions to compute statistics such as mean, median, standard deviation, variance, etc.
Function |
Description |
Example |
|---|---|---|
|
Computes the mean of the array elements along the specified axis. |
|
|
Computes the median of the array elements along the specified axis. |
|
|
Computes the standard deviation of the array elements along the specified axis. |
|
|
Computes the variance of the array elements along the specified axis. |
|
|
Returns the minimum value in the array along the specified axis. |
|
|
Returns the maximum value in the array along the specified axis. |
|
|
Computes the sum of the array elements along the specified axis. |
Linear algebra operations#
Numpy includes functions for matrix multiplication, dot products, eigenvalues, eigenvectors, etc.
Function |
Description |
Example |
|---|---|---|
|
Computes the dot product of two arrays. |
|
|
Performs matrix multiplication of two arrays. |
|
|
Computes the inverse of a square matrix. |
|
|
Computes the determinant of a square matrix. |
|
|
Computes the eigenvalues and right eigenvectors of a square matrix. |
|
|
Performs Singular Value Decomposition (SVD) on a matrix. |
|
Short exercise: Create a 2x2 matrix and compute its inverse, determinant, and eigenvalues.
Example here!
# Creating a 2x2 matrix
matrix = np.array([[1, 2], [3, 4]])
# Computing the inverse
inverse = np.linalg.inv(matrix)
# Computing the determinant
determinant = np.linalg.det(matrix)
# Computing the eigenvalues
eigenvalues, _ = np.linalg.eig(matrix)
print("Matrix:\n", matrix)
print("Inverse:\n", inverse)
print("Determinant:\n", determinant)
print("Eigenvalues:\n", eigenvalues)
Reshaping and Transposing#
We can manipulate shapes of an array using functions like reshape() and transpose().
Function |
Description |
Example |
|---|---|---|
|
Reshapes the array to the specified new shape. |
|
|
Returns the transpose of the array. |
|
|
Flattens a multi-dimensional array into a 1D array. |
|
|
Returns a flattened array (1D) without making a copy if possible. |
|
|
Resizes the array to the specified new shape. |
|
Stacking and Splitting#
You can combine or split arrays using functions like hstack(), vstack(), split(), etc.
Function |
Description |
Example |
|---|---|---|
|
Stacks arrays in sequence horizontally (column-wise). |
|
|
Stacks arrays in sequence vertically (row-wise). |
|
|
Concatenates a sequence of arrays along the specified axis. |
|
|
Splits an array into multiple sub-arrays along the specified axis. |
|
|
Splits an array into multiple sub-arrays horizontally (column-wise). |
|
|
Splits an array into multiple sub-arrays vertically (row-wise). |
|
Short exercise: Create two 2D arrays and stack them both horizontally and vertically. Then, split one of the stacked arrays into two sub-arrays.
Example here!
# Creating two 2D arrays
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
# Stacking arrays horizontally
hstacked = np.hstack((arr1, arr2))
# Stacking arrays vertically
vstacked = np.vstack((arr1, arr2))
# Splitting the horizontally stacked array into two sub-arrays
split_arrays = np.hsplit(hstacked, 2)
print("Array 1:\n", arr1)
print("\nArray 2:\n", arr2)
print("\nHorizontally Stacked Array:\n", hstacked)
print("\nVertically Stacked Array:\n", vstacked)
print("\nSplit Arrays from Horizontally Stacked Array:\n", split_arrays)
Sorting and Searching#
Numpy provides functions to sort arrays and search for specific values.
Function |
Description |
Example |
|---|---|---|
|
Returns a sorted copy of the array along the specified axis. |
|
|
Returns the indices that would sort the array along the specified axis. |
|
|
Returns elements chosen from |
|
Short exercise: Create a 1D array with random integers, sort it, and find the indices that would sort the array. Then, use np.where to create a new array where all negative values are replaced with zero.
Example here!
# Creating a 1D array with random integers
arr = np.random.randint(-10, 10, size=10)
print("Original Array:", arr)
# Sorting the array
sorted_arr = np.sort(arr)
print("Sorted Array:", sorted_arr)
# Finding the indices that would sort the array
sorted_indices = arr.argsort()
print("Indices that would sort the array:", sorted_indices)
# Using np.where to replace negative values with zero
replaced_arr = np.where(arr < 0, 0, arr)
print("Array with negative values replaced by zero:", replaced_arr)
Input and Output#
Numpy provides functions to automatically read from and write to files, both in text and binary formats. Note: it only reads and writes numerical data. If you want to read and write text data, you can use Python’s built-in functions or Pandas library (not explored in this course).
Function |
Description |
Example |
|---|---|---|
|
Loads data from a text file. |
|
|
Saves an array to a text file. |
|
|
Loads data from a binary file in NumPy |
|
|
Saves an array to a binary file in NumPy |
|
Example of saving and loading an array to/from a text file:
# Creating an array
arr = np.array([[1, 2, 3], [4, 5, 6]], dtype=float)
arr
# Saving the array to a text file
np.savetxt('data.txt', arr, fmt='%.2f', delimiter=',', header='Column1,Column2', comments='', )#footer='End of data')
# Loading the array from the text file
loaded_arr = np.loadtxt('data.txt', delimiter=',', skiprows=1, encoding='utf-8') # Skip the header row, and use utf-8 encoding
print("Loaded Array from text file:\n", loaded_arr)
Both np.savetxt and np.loadtxt have many parameters to customize the saving and loading process, including headers, delimiters, and data types. You can check:
official load documentation for more details.
Short exercise: Create a 2D array, save it to a text file, and then load it back into a new array. Print both arrays to verify they are the same.
Example here!
## Very similar to the example above
# Creating a 2D array
arr = np.array([[10, 20, 30], [40, 50, 60]], dtype=float)
print("Original Array:\n", arr)
# Saving the array to a text file
np.savetxt('array_data.txt', arr, fmt='%.2f', delimiter=',', header='Col1,Col2,Col3', comments='')
# Loading the array from the text file
loaded_arr = np.loadtxt('array_data.txt', delimiter=',', skiprows=1, encoding='utf-8') # Skip the header row, and use utf-8 encoding
print("Loaded Array from text file:\n", loaded_arr)
# Verifying that both arrays are the same
are_equal = np.array_equal(arr, loaded_arr)
print("Are the original and loaded arrays equal?", are_equal)
Copying and Masking#
You can copy arrays and create masks to filter or modify elements in an array based on certain conditions.
Function |
Description |
Example |
|---|---|---|
|
Creates a copy of the array. |
|
|
Creates a masked array, where certain elements are marked as invalid or missing. |
|
|
Masks elements of the array where the condition is True. |
|
|
Masks elements of the array that are equal to a specified value. |
|
Another method to copy an array can be just assigning the array to a new variable.
# Creating a copy of an array using ndarray.copy()
arr_new = arr_init.copy()
# Creating a copy of an array using assignment
arr_new = arr_init
Short exercise: Create a 1D array, make a copy of it, and then create a masked array where all elements less than a certain value are masked.
Example here!
# Creating a 1D array
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print("Original Array:\n", arr)
# Making a copy of the array
arr_copy = np.copy(arr)
print("\nCopied Array:\n", arr_copy)
# Creating a masked array where elements less than 5 are masked
masked_arr = np.ma.masked_where(arr < 5, arr)
print("\nMasked Array (elements < 5 are masked):\n", masked_arr)
Important methods for working with Matrices and Vectors#
Function |
Description |
Example |
|---|---|---|
|
Returns the sum of the diagonal elements of a 2D array (matrix). |
|
|
Returns the specified diagonal of a 2D array (matrix). |
|
|
Computes the cross product of two vectors. |
|
|
Computes the norm (magnitude) of a vector or matrix. |
|
|
Solves a system of linear equations Ax = b. |
|
|
Performs matrix multiplication of two arrays. |
|
|
Computes the dot product of two arrays. |
|
Short exercise: Create a 2x2 matrix and a 2D vector, then compute the trace of the matrix, the diagonal elements, the cross product of the vector with itself, and the norm of the vector.
Example here!
# Creating a 2x2 matrix
matrix = np.array([[1, 2], [3, 4]])
# Creating a 2D vector
vector = np.array([1, 2])
# Computing the trace of the matrix
trace = np.trace(matrix)
# Computing the diagonal elements of the matrix
diagonal = np.diagonal(matrix)
# Computing the cross product of the vector with itself
# Important Note: for calculating the cross product, the vectors must be 3D. To make it 3D, we can add a third component with value 0.
vector3D = np.array([1, 2, 0]) # Adding a third component with value 0
cross_product = np.cross(vector3D, vector3D)
# Computing the norm of the vector
norm = np.linalg.norm(vector)
print("Matrix:\n", matrix)
print("\nVector:\n", vector)
print("\nTrace of the matrix:\n", trace)
print("\nDiagonal elements of the matrix:\n", diagonal)
print("\nCross product of the vector with itself:\n", cross_product)
print("\nNew 3D Vector:\n", vector3D)
print("\nNorm of the vector:\n", norm)
Numpy constants and trigonometric functions#
Numpy provides several mathematical constants that can be useful in numerical computations.
Constant |
Description |
Value |
|---|---|---|
|
The mathematical constant π (pi). |
3.141592653589793 |
|
The mathematical constant e (Euler’s number). |
|
|
Represents positive infinity. |
inf |
|
Represents “Not a Number” (NaN). |
nan |
Numpy also provides a variety of trigonometric functions that operate on arrays element-wise.
Function |
Description |
Example |
|---|---|---|
|
Computes the sine of x (x in radians). |
|
|
Computes the cosine of x (x in radians). |
|
|
Computes the tangent of x (x in radians). |
|
|
Computes the inverse sine (arcsine) of x. |
|
|
Computes the inverse cosine (arccosine) of x. |
|
|
Computes the inverse tangent (arctangent) of x. |
|
|
Converts angles from radians to degrees. |
|
|
Converts angles from degrees to radians. |
|
1.3 Non-numeric data types in Numpy#
Despite being primarily designed for numerical data, Numpy also supports non-numeric data types, such as strings and booleans. Here are some examples of how to create and manipulate arrays with these data types:
import numpy as np
# Creating a 1D array of strings
str_arr = np.array(['a','bcd'])
print(str_arr)
print(str_arr.dtype)
arr = np.array(['a','bcd','efghijk'])
print(arr)
print(arr.dtype)
# Creating a 1D array of booleans
bool_arr = np.array([True, False, True])
print(bool_arr)
print(bool_arr.dtype)
Boolean arrays can be very useful for indexing and filtering data in Numpy. For example, a boolean array to select elements from another array that meet a certain condition.
Short exercise: Use boolean arrays to represent the state of qubits (e.g., True for |1⟩ and False for |0⟩) and perform operations based on their states.
Example here!
import numpy as np
# Creating a 1D array of integers
int_arr = np.array([1, 2, 3, 4, 5])
# Creating a boolean array based on a condition
bool_arr = int_arr > 3
print(bool_arr)
# Using the boolean array to filter the integer array
filtered_arr = int_arr[bool_arr]
print("Original array:", int_arr)
print("Boolean array:", bool_arr)
print("Filtered array:", filtered_arr)
1.4 Linear algebra with Numpy#
Numpy provides a comprehensive set of functions for performing linear algebra operations.
1.4.1 Scalar Array/Matrix Operations#
You can perform scalar operations on arrays and matrices, such as addition, subtraction, multiplication, and division. These operations are applied element-wise.
import numpy as np
# Creating a 2D array (matrix)
A = np.array([[1,2],[3,4]], dtype='float')
# Displaying the original matrix
print(A, '')
# Performing scalar operations
print(2*A,'')
# Element-wise exponentiation
print(A**2,'')
# Element-wise division
print(A/2,'')
# Element-wise floor division
print(A//2,'')
# Element-wise addition
print(A+2,'')
# Element-wise subtraction
print(A-2,'')
# Element-wise modulus
print(A%2,'')
1.4.2 Array-Array Operations#
As we have seen, you can perform element-wise operations between two arrays of the same shape.
import numpy as np
# Creating two 2D arrays (matrices) of the same shape
arr1 = np.array([1,2,3,4,5])
arr2 = np.array([6,7,8,9,10], dtype='float')
print('arr1 + arr2 = ', arr1+arr2)
print('arr1 - arr2 = ', arr1-arr2)
print('arr1 * arr2 = ', arr1*arr2)
print('arr1 / arr2 = ', arr1/arr2)
print('arr1 ** arr2 = ', arr1**arr2)
print('arr1 // arr2 = ', arr1//arr2)
print('arr1 % arr2 = ', arr1%arr2)
While the above arrays are vectors we can perform the same operations higher-dimensional arrays (matrices) in the same way.
import numpy as np
# Creating two 2D arrays (matrices) of the same shape
A = np.array([[1,2],[3,4]], dtype='float')
B = np.array([[5,6],[7,8]], dtype='float')
print('A + B = ', A + B)
print('A - B = ', A - B)
print('A * B = ', A * B)
print('A / B = ', A / B)
print('A ** B = ', A ** B)
print('A // B = ', A // B)
print('A % B = ', A % B)
Short exercise: Create two 3x3 matrices and perform element-wise addition, subtraction, multiplication, and division on them.
Example here!
import numpy as np
# Creating two 3x3 matrices
matrix1 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype='float')
matrix2 = np.array([[9, 8, 7], [6, 5, 4], [3, 2, 1]], dtype='float')
# Performing element-wise operations
addition = matrix1 + matrix2
subtraction = matrix1 - matrix2
multiplication = matrix1 * matrix2
division = matrix1 / matrix2
print("Matrix 1:", matrix1)
print("Matrix 2:", matrix2)
print("Element-wise Addition:", addition)
print("Element-wise Subtraction:", subtraction)
print("Element-wise Multiplication:", multiplication)
print("Element-wise Division:", division)
1.4.3 Matrix Multiplication and Dot Product#
For matrix multiplication, you can use the @ operator or the np.matmul() function.
import numpy as np
# Creating two 2D arrays (matrices)
A = np.array([[1,2],[3,4]], dtype='float')
B = np.array([[5,6],[7,8]], dtype='float')
# Performing matrix multiplication using the @ operator
C = A @ B
print('Matrix multiplication using @ operator:', C)
# Performing matrix multiplication using np.matmul()
D = np.matmul(A, B)
print('Matrix multiplication using np.matmul():', D)
You can also compute the dot product of two vectors using the np.dot() function or the @ operator.
import numpy as np
# Creating two 1D arrays (vectors)
vec1 = np.array([1, 2, 3], dtype='float')
vec2 = np.array([4, 5, 6], dtype='float')
# Computing the dot product using np.dot()
dot_product1 = np.dot(vec1, vec2)
print('Dot product using np.dot():', dot_product1)
# Computing the dot product using the @ operator
dot_product2 = vec1 @ vec2
print('Dot product using @ operator:', dot_product2)
Short exercise: Create two 2x2 matrices and compute their matrix product using both the @ operator and the np.matmul() function. Then, create two 1D arrays (vectors) and compute their dot product using both the np.dot() function and the @ operator.
Example here!
import numpy as np
# Creating two 2x2 matrices
matrix1 = np.array([[1, 2], [3, 4]], dtype='float)
matrix2 = np.array([[5, 6], [7, 8]], dtype='float)
# Computing matrix product using the @ operator
matrix_product1 = matrix1 @ matrix2
# Computing matrix product using np.matmul()
matrix_product2 = np.matmul(matrix1, matrix2)
print("Matrix 1:\n", matrix1)
print("Matrix 2:\n", matrix2)
print("Matrix product using @ operator:\n", matrix_product1)
print("Matrix product using np.matmul():\n", matrix_product2)
# Creating two 1D arrays (vectors)
vector1 = np.array([1, 2, 3], dtype='float)
vector2 = np.array([4, 5, 6], dtype='float)
# Computing dot product using np.dot()
dot_product1 = np.dot(vector1, vector2)
# Computing dot product using the @ operator
dot_product2 = vector1 @ vector2
print("Vector 1:", vector1)
print("Vector 2:", vector2)
print("Dot product using np.dot():", dot_product1)
print("Dot product using @ operator:", dot_product2)
1.4.4 Other Linear Algebra Operations#
Numpy provides several other linear algebra operations, such as computing the transpose, inverse, determinant, and eigenvalues/eigenvectors of matrices.
import numpy as np
# Creating a 2D array (matrix)
A = np.array([[1,2],[3,4]], dtype='float')
# Computing the transpose of the matrix
A_T = A.T
print('Transpose of A:', A_T)
# Computing the inverse of the matrix
A_inv = np.linalg.inv(A)
print('Inverse of A:', A_inv)
# Computing the determinant of the matrix
A_det = np.linalg.det(A)
print('Determinant of A:', A_det)
# Computing the eigenvalues and eigenvectors of the matrix
eigenvalues, eigenvectors = np.linalg.eig(A)
print('Eigenvalues of A:', eigenvalues)
print('Eigenvectors of A:', eigenvectors)
The conjugate transpose, also known as Hermitian transpose, of a matrix can be computed using the .conj().T method.
import numpy as np
# Creating a 2D array (matrix) with complex numbers
A = np.array([[1+2j, 3+4j], [5+6j, 7+8j]], dtype='complex')
# Computing the conjugate transpose of the matrix
A_H = A.conj().T
print('Conjugate Transpose of A:', A_H)
Another example using complex numbers:
import numpy as np
# Creating a 2D array (matrix) with complex numbers
A = np.array([[1+2j, 3+4j], [5+6j, 7+8j]], dtype='complex')
# Computing the conjugate transpose of the matrix
A_H = A.conj().T
print('Original Matrix A:\n', A)
print('Conjugate Transpose of A:\n', A_H)
# Verifying that the conjugate transpose is correct
print('A * A_H:\n', A @ A_H)
Short exercise: Create a 2x2 matrix and compute its transpose, inverse, determinant, and eigenvalues/eigenvectors.
Example here!
# Creating a 2x2 matrix
matrix = np.array([[2, 3], [1, 4]], dtype='float')
# Computing the transpose of the matrix
matrix_T = matrix.T
# Computing the inverse of the matrix
matrix_inv = np.linalg.inv(matrix)
# Computing the determinant of the matrix
matrix_det = np.linalg.det(matrix)
# Computing the eigenvalues and eigenvectors of the matrix
eigenvalues, eigenvectors = np.linalg.eig(matrix)
print("Matrix:\n", matrix)
print("Transpose of the matrix:\n", matrix_T)
print("Inverse of the matrix:\n", matrix_inv)
print("Determinant of the matrix:\n", matrix_det)
print("Eigenvalues of the matrix:\n", eigenvalues)
print("Eigenvectors of the matrix:\n", eigenvectors)
1.5 Some useful Numpy tools#
1.5.1 Random Number Generation#
Numpy provides a module called numpy.random that contains functions for generating random numbers and performing random sampling. Some commonly used functions include:
Function |
Description |
Example |
|---|---|---|
|
Generates an array of the given shape and populates it with random samples from a uniform distribution over [0, 1). |
|
|
Generates an array of the given shape and populates it with random samples from a standard normal distribution (mean 0, variance 1). |
|
|
Generates random integers from |
|
|
Generates a random sample from a given 1D array. |
|
|
Generates random samples from a normal (Gaussian) distribution with specified mean ( |
|
1.6 Polynomial Fitting with Numpy#
Numpy provides a convenient function np.polyfit() to fit a polynomial of a specified degree to a set of data points. The function takes in the x and y coordinates of the data points, the degree of the polynomial, and returns the coefficients of the fitted polynomial.
where \(c[i]\) is the i-th coefficient.
In Numpy we can perform many operations with polynomials.
Some useful polynomial functions in Numpy:
Function |
Description |
Example |
|---|---|---|
|
Fits a polynomial of degree |
|
|
Evaluates a polynomial at specific values of |
|
|
Computes the derivative of a polynomial. |
|
|
Computes the integral of a polynomial. |
|
|
Computes the roots of a polynomial. |
|
import numpy as np
# Generating some sample data
x = np.linspace(-5, 5, 100)
y = 2*x**3 - 4*x**2 + 3*x + np.random.normal(0, 10, size=x.shape)
# Fitting a polynomial of degree 3 to the data
degree = 3
coefficients = np.polyfit(x, y, degree)
# Evaluating the fitted polynomial at the x values
y_fit = np.polyval(coefficients, x)
# Printing the coefficients of the fitted polynomial
print("Coefficients of the fitted polynomial:", coefficients)
Short exercise: Generate some sample data points that follow a quadratic relationship with some added noise. Fit a polynomial of degree 2 to the data and plot both the original data points and the fitted polynomial curve.
Example here!
import numpy as np
import matplotlib.pyplot as plt
# Generating some sample data
x = np.linspace(-10, 10, 100)
y = 3*x**2 + 2*x + 1 + np.random.normal(0, 20, size=x.shape)
# Fitting a polynomial of degree 2 to the data
degree = 2
coefficients = np.polyfit(x, y, degree)
# Evaluating the fitted polynomial at the x values
y_fit = np.polyval(coefficients, x)
# Printing the coefficients of the fitted polynomial
print("Coefficients of the fitted polynomial:", coefficients)
# Evaluating quality of the fit
residuals = y - y_fit
ss_res = np.sum(residuals**2)
ss_tot = np.sum((y - np.mean(y))**2)
r_squared = 1 - (ss_res / ss_tot)
print("R-squared value of the fit:\n", r_squared)
## Extra
# Plotting the original data points and the fitted polynomial curve
plt.scatter(x, y, label='Data Points', color='blue', alpha=0.5)
plt.plot(x, y_fit, label='Fitted Polynomial', color='red')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Polynomial Fitting with Numpy')
plt.legend()
plt.show()