Numpy Arrays

Python's list data structure is very general, but it does not (directly have the capability of representing vectors, matrices, and matrix operations. It is not terribly difficult to take nested lists and interpret them as matrices. NumPy provides a set of methods to easily perform operations on arrays, such as matrix multiplication, inverting a matrix, etc.

Numpy is a package to enhance Python. We have already seen packages like math and random. We could refer to numpy methods using (for example) numpy.array(). However, it is more convenient to import numpy as np and use the shorthand np.array() for example.

Creating a 1-d numpy array

In Ex2.1.1 we construct a one dimensional array (vector) from a list named cvalues. Note that the entries in cvalues (as in all lists) are separated by commas. In line 5 we convert the list to an array using the numpy method np.array(). Notice the difference in the two print outs. the first one prints the list, while the second prints the 1-d array (with spaces, not commas separating elements.

In [4]:
# Ex2.1.1 
import numpy as np
cvalues = [20.1, 20.8, 21.9, 22.5, 22.7, 22.3, 21.8, 21.2, 20.9, 20.1]
print(cvalues, "\n")
C = np.array(cvalues)
print(C)
[20.1, 20.8, 21.9, 22.5, 22.7, 22.3, 21.8, 21.2, 20.9, 20.1] 

[20.1 20.8 21.9 22.5 22.7 22.3 21.8 21.2 20.9 20.1]

Creating a 2-d numpy array (matrix)

In the next example we will create a two dimensional array, using values in a nested list. Each sublist in mvalues represents a row in the 2-d array. Line 5 creates the array from the list. Notice the difference between the two printouts

In [2]:
# Ex2.1.2
import numpy as np
mvalues = [[1,6,5],[3,4,8],[1,12,3]]
print(cvalues, "\n")
M = np.array(mvalues)
print(M)
[[1, 6, 5], [3, 4, 8], [1, 12, 3]] 

[[ 1  6  5]
 [ 3  4  8]
 [ 1 12  3]]

Matrix Operations

In the next few examples, I'll be using one or more of the following matrices.

$A=\begin{bmatrix} 1 & 6 & 5 \\ 3 & 4 & 8 \\ 2 & 12 & 3 \end{bmatrix} B=\begin{bmatrix} 3 & 4 & 6 \\ 5 & 6 & 7 \\ 6 & 56 & 7 \end{bmatrix}$

Matrix Transpose

The matrix transpose applies the method transpose() to the matrix.

In [16]:
# Ex2.1.3
import numpy as np
# three dimensional array a
a = np.array([[1, 6, 5],[3 ,4, 8],[2, 12, 3]]) 
print(a, "\n")
print(a.transpose())
[[ 1  6  5]
 [ 3  4  8]
 [ 2 12  3]] 

[[ 1  3  2]
 [ 6  4 12]
 [ 5  8  3]]

Matrix Multiplication

The method .dot(A,B) will form the matrix product of AB. Note: standard requirements for matrix multiplication apply: the row rank of the first matrix must equal the column rank of the second matrix.

In [4]:
# Ex2.1.4
import numpy as np
# three dimensional array a
A = np.array([[1, 6, 5],[3 ,4, 8],[2, 12, 3]])
B = np.array([[3, 4, 6],[5 ,6, 7],[6, 56, 7]]) 
print(A, "\n")
print(B, "\n")
print(np.dot(A,B))
[[ 1  6  5]
 [ 3  4  8]
 [ 2 12  3]] 

[[ 3  4  6]
 [ 5  6  7]
 [ 6 56  7]] 

[[ 63 320  83]
 [ 77 484 102]
 [ 84 248 117]]

Matirx Inverse

Numpy is segmented into groupings of methods. In order to take the inverse of a matrix, you must refer to the method as np.linalg.inv(). If you leave out the .linalg you will get an error. Note that multiplying A by its inverse will yield the identity matrix (well, almost). The difference between AB in the off diagonal terms is caused by finite representation of floating point arithmetic.

In [7]:
# Ex2.1.5
import numpy as np
A = np.array([[1, 6, 5],[3 ,4, 8],[2, 12, 3]])
print(A, "\n")
B=np.linalg.inv(A)
print(B,"\n")
print(np.dot(A,B))
[[ 1  6  5]
 [ 3  4  8]
 [ 2 12  3]] 

[[-0.85714286  0.42857143  0.28571429]
 [ 0.07142857 -0.07142857  0.07142857]
 [ 0.28571429  0.         -0.14285714]] 

[[ 1.00000000e+00  0.00000000e+00 -5.55111512e-17]
 [ 0.00000000e+00  1.00000000e+00  0.00000000e+00]
 [-1.11022302e-16  0.00000000e+00  1.00000000e+00]]

Product of a matrix and a vector

$\left[ \begin{array}{c} 3 \\ 5 \end{array} \right] = \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} \left[ \begin{array}{c} -1 \\ 2 \end{array} \right]$

Note that we use the same procedure as matrix multiplication. In this case we are multiplying a 2x2 matrix by a 2x1 matrix. That is what multiplying a matrix by a vector is. Notice that even for the V numpy array we use the representation [[-1],[2]]. If we use [-1, 2] this would be a 1x2 row vector.

In [8]:
# Ex2.1.6
import numpy as np
A=np.array([[1,2],[3,4]])
V=np.array([[-1],[2]])
Z=np.dot(A,V)
print(Z)
[[3]
 [5]]

Scalar Product

The scaler product is the product of a row vector and a column vector. In linear algrbra we represent this as

$ \vec x \cdot \vec y$

Note in Ex 2.1.7 the way x (a row vector) and y (a column vector) are represented The result is always a scalar quantity--a 1x1 numpy array with a single value.

In [9]:
# Ex 2.1.7
import numpy as np
x=np.array([2,4])
y=np.array([[-1],[2]])
z=np.dot(x,y)
print(z)
[6]

The determinate of a matrix

In [3]:
# Ex 2.1.8
import numpy as np
A=np.array([[1,2],[3,4]])
z=np.linalg.det(A) 
print(z)r
-2.0000000000000004

The only confusing issue is that in refering to to the method det, you must specify that it is part of the submodule linalg. If we are going to use the Linear Algebra submodule often avoid using np.linalg.det() by doing a sub-import from numpy.

In [34]:
# Ex 2.1.9
import numpy as np
from numpy import linalg as LA
A=np.array([[1,2],[3,4]])
z=LA.det(A) 
print(z)
-2.0000000000000004

Eigenvalues and Eigenvectors

reference

Suppose we had a vector matrix equation

$\vec x = A \vec y$

With an arbitrary A matrix, $\vec x $ and $\vec y $ are highly coupled many values of y map into values of x. Suppose we could find a Transformation of coordinates so that

$z_1=S\vec x$ and $z_2=\vec y$ or $S\vec x = A S \vec y$ furthermore, we carefully chose the transformation matrix S so that:

$z_1 =S^{-1} A S \vec z_2$

Where $S^{-1} A S = D$ a diagonal matrix \ The diagonal elements of D are the eigenvalues of A, while the columns in the S matrix are the eigenvectors.

the assignment w,v = np.linalg.eigh(matrix) returns the eigenvalues in an array w and the eigenvectors in the array v, where the eigenvectors are the columns the v. The order of the eigenvectors corresponds to the order of the eigenvalues in w.

In [5]:
# Ex 2.1.10
import numpy as np
A=np.array([[4,-3,-3],[3,-2,-3],[-1,1,2]])
print(A,"\n")
# Finding the eigenvalues w and eigenvectors v
# The eigenvectors are displayed in array form.  In this case a 3X3 array
w, v = np.linalg.eig(A)
print("eigenvalues","\n",w,"\n")
print("eigenvectors","\n",v,"\n")
# The matrix of eigenvectors is the transformation matrix S
S=v
# Find the inverse of S
Sinv=np.linalg.inv(S)
# transformed matrix Sinv A S should be a diagonal matrix with the eigenvalues on the diagonal
D= np.dot(np.dot(Sinv,A),S)
print("diagonalized matrix","\n")
print(D)
[[ 4 -3 -3]
 [ 3 -2 -3]
 [-1  1  2]] 

eigenvalues 
 [2. 1. 1.] 

eigenvectors 
 [[ 0.6882472  -0.53452248 -0.38157619]
 [ 0.6882472  -0.80178373 -0.81592785]
 [-0.22941573  0.26726124  0.43435166]] 

diagonalized matrix 

[[ 2.00000000e+00  2.78186355e-15  1.87425538e-15]
 [-1.81800542e-15  1.00000000e+00  1.65756067e-15]
 [ 1.07524290e-16 -3.63214665e-16  1.00000000e+00]]

NumPy Math functions

NumPy has a rich set of math functions. Often, programmers use these from numpy rather than the math package introduced earlier. NumPy Mathematical Functions
Note: you can evaluate most functions for a single value or for an array of values. See the left frame in the above reference for detailed documentation of arguments.

In the following example the first print uses a simple evaluation of $\sin(\pi/2)$ while the second example applies the sine function to an array. Note the array contains degrees in angles, so we must convert to radians

In [10]:
# Ex 2.1.11
import numpy as np
print("sine of a floating point number")
print(np.sin(np.pi/2))
print("sine of an array with angles in degrees")
print(np.sin(np.array((0., 30., 45., 60., 90.)) * np.pi / 180. ))
sine of a floating point number
1.0
sine of an array with angles in degrees
[0.         0.5        0.70710678 0.8660254  1.        ]

NumPy Linear Space

There is a very handy NumPy method called linspace. It produces an array of evenly spaced numbers over a specific interval. https://numpy.org/doc/stable/reference/generated/numpy.linspace.html

In this example we have used np.linspace(0,4,20). We want to create a sequence of numbers starting at 0 and ending at 4, with 20 numbers. NOTE: COUNTING FROM 0 TO 4 WITH 20 NUMBERS GIVES AN INTERVAL BETWEEN NUMBERS OF .2105... Count the number of elements in each sequence below.

In [17]:
# Ex 2.1.12
x=np.linspace(0,4,20)
print(x)
x=np.linspace(0,4,21)
print("\n\n",x)
[0.         0.21052632 0.42105263 0.63157895 0.84210526 1.05263158
 1.26315789 1.47368421 1.68421053 1.89473684 2.10526316 2.31578947
 2.52631579 2.73684211 2.94736842 3.15789474 3.36842105 3.57894737
 3.78947368 4.        ]


 [0.  0.2 0.4 0.6 0.8 1.  1.2 1.4 1.6 1.8 2.  2.2 2.4 2.6 2.8 3.  3.2 3.4
 3.6 3.8 4. ]

© Donald R. Falkenburg
Last updated 4/29/23

In [ ]: