6. Vectors and Matrices

In this chapter we work with vectors and matrices. We cover the basic calculations that can be done with both. We also discuss how vectors and matrices can be manipulated.

6.1. Definitions of Vectors

A vector is a list of numbers that we can do “math” with. The numbers in the vector are indexed, so that we can access them. Note that vector indexing in Python starts with zero, not with 1. So Python counts the first element in a vector as element 0, the second as element 1, etc. We first need to import some important packages so that Python understands basic numerical procedures and definitions, like vectors and matrices and not with a list. The command np.array from the numpy package does just that.

Here are some examples. Vectors x y year and names are assigned as follows:

import numpy as np

# You can document your script files using the ``#`` symbol.
# This allows you to add commentary to your codes.
xv = np.array([1, 3.3333, 4.8976, 9])
yv = np.array([9.233, 0.3453, 3.29999, 2.8])

print("xv = {}".format(xv))
print("yv = {}".format(yv))
print("xv[0] = {}".format(xv[0]))
print("xv[1] = {}".format(xv[1]))
print("xv[2] = {}".format(xv[2]))
xv = [1.     3.3333 4.8976 9.    ]
yv = [9.233   0.3453  3.29999 2.8    ]
xv[0] = 1.0
xv[1] = 3.3333
xv[2] = 4.8976

Remember that Python starts numbering the elements with 0 and not 1! So if you want to access the first element in a vector x you’d need to type: xv[0].

If you would like to print the vector formatted, you need to set the format option of the numpy object as:

np.set_printoptions(precision=2)
print("xv= {}".format(xv))
print("yv= {}".format(yv))
xv= [1.   3.33 4.9  9.  ]
yv= [9.23 0.35 3.3  2.8 ]

Note that if you like a vector: 2000,2001,2002,2003 you need to write the arange command with the upper limit+1.

year  = np.arange(2000,2004,1)   # np.arange(from, to, stepsize)
print("year= {}".format(year))
year= [2000 2001 2002 2003]

If you would like to generate a certain number of elements between two boundary points you can use np.linspace(). Let us say we would like to generate some numbers between the lower bound of 2000 and the upper bound of 2003. We call this spanning a grid from 2000 to 2003 where we basically break the interval into evenly spaced sub-intervals. This is important for graphing (or plotting) mathematical functions which we do in the next chapter.

Let us say we want to span a grid with 5 gridpoints, so that the intervall from 2000 to 2003 is broken in equally wide sub-intervals, we would write the following:

somev = np.linspace(2000,2003,5) # np.linspace(from, to, nr. of steps)
print("somev= {}".format(somev))
somev= [2000.   2000.75 2001.5  2002.25 2003.  ]

Remember from the previous chapter how arrays are very similar to lists and tuples. An array is basically a list of numbers, and numbers only! This means that indexing will work on an array, just as it worked on the list object. Same with slicing if you remember that from the previous chapter.

Here is again a list example. Let us define it first and then extract some objects from the list (which is itself an object – remember in Python everything is an object).

# A list of strings is simply:
names_list = ["Tom", "Dick", "Harry", "Patrick"]

# A tuple of strings is simply:
names_tuple = ("Tom", "Dick", "Harry", "Patrick")

print("names_list= {}".format(names_list))
print("names_tuple= {}".format(names_tuple))

print("names_list[1]= {}".format(names_list[1]))
print("names_list[3]= {}".format(names_list[3]))
names_list= ['Tom', 'Dick', 'Harry', 'Patrick']
names_tuple= ('Tom', 'Dick', 'Harry', 'Patrick')
names_list[1]= Dick
names_list[3]= Patrick

6.2. Simple Calculations with Vectors

Let us do some simple math with arrays (or vectors, which are one dimensional arrays).

import numpy as np
# Element-by-element operations
x1 = np.array([1,3,4,9])
x2 = np.array([2,5,6,3])
#
print(" --- OUTPUT: --- ")
print("x1= {}".format(x1))
print("x2= {}".format(x2))
print(" ---------------- ")
print("x1+x2= {}".format(x1+x2))
print("x1*x2= {}".format(x1*x2))
 --- OUTPUT: ---
x1= [1 3 4 9]
x2= [2 5 6 3]
 ----------------
x1+x2= [ 3  8 10 12]
x1*x2= [ 2 15 24 27]

As you can see, the numbers are added element-by-element and they are multiplied element-by-element.

This is different behavior from what we have seen so far from lists. Let us repeat the above example, but instead of numpy-arrays we use lists instead. We would then have:

x1_list = [1,3,4,9]
x2_list = [2,5,6,3]
#
print(" --- OUTPUT: --- ")
print("x1_list = {}".format(x1_list ))
print("x2_list = {}".format(x2_list ))
print(" ---------------- ")
print("x1_list + x2_list = {}".format(x1_list +x2_list ))
 --- OUTPUT: ---
x1_list = [1, 3, 4, 9]
x2_list = [2, 5, 6, 3]
 ----------------
x1_list + x2_list = [1, 3, 4, 9, 2, 5, 6, 3]

This just joins the lists together. It does not add the numbers in the list.

If, on the other hand, we try to multiply the lists we get:

print("x1_list*x2_list= {}".format(x1_list*x2_list))
---------------------------------------------------------------------------TypeError
Traceback (most recent call last)/tmp/ipykernel_29020/814410473.py in
<module>
----> 1 print("x1_list*x2_list= {}".format(x1_list*x2_list))
TypeError: can't multiply sequence by non-int of type 'list'

This results in an error message. You cannot really do “math” on lists because the content of lists can be numbers, words, other weird objects and it is not guaranteed that doing math with these is possible. Hence, Python will not let you do “math” with lists. You can only do “list things” such as adding stuff to it, printing it, extracting info from it, etc.

6.3. Simple Calculations with Matrices

Matrices are “two dimensional” vectors. Think of a speadsheet with rows and columns that can only be filled with numbers. In Python we define as matrix again with the array() function. Here is a simple Matrix with 2 rows and 2 columns. We call this a 2-by-2 or \(2\times2\) matrix.

import numpy as np
A = np.array([[2,3],[4,5]])
B = np.array([[2,6],[1,3]])
#
print(" --- OUTPUT: --- ")
print("A= \n {}".format(A))
print("B= \n {}".format(B))
print(" ---------------- ")
print("A*B= \n {}".format(A*B))
print("A-B= \n {}".format(A-B))
print(" ---------------- ")
 --- OUTPUT: ---
A=
 [[2 3]
 [4 5]]
B=
 [[2 6]
 [1 3]]
 ----------------
A*B=
 [[ 4 18]
 [ 4 15]]
A-B=
 [[ 0 -3]
 [ 3  2]]
 ----------------

You can again see that when you add or multiply matrices, the operation is done element-by-element. This means that the number in position 0,0 (row 1, column 1) in matrix A is multiplied with the number in the same position 0,0 (row 1, column 1) in matrix B.

Python has some built in functions to help you generate often used matrices automatically, so you do not always have to type them in.

If you want to generate matrices filled with either zeros or ones of a particular size in Python you can use the following:

import numpy as np
A = np.zeros((3,5), float)
B = np.ones((4,3), float)

print(" --- OUTPUT: --- ")
print("A= \n {}".format(A))
print(" ---------------- ")
print("B= \n {}".format(B))
 --- OUTPUT: ---
A=
 [[0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0.]]
 ----------------
B=
 [[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]

If you want a matrix with integer numbers the simply define the output as int so that

import numpy as np
A = np.zeros((3,5), int)
b = np.ones((4,3), int)

print(" --- OUTPUT: --- ")
print("A= \n {}".format(A))
print(" ---------------- ")
print("B= \n {}".format(B))
 --- OUTPUT: ---
A=
 [[0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]]
 ----------------
B=
 [[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]

If you want to create an identity matrix with the value one in the main diagonal and zeros everywhere else you can:

import numpy as np
C = np.identity(3)

print(" --- OUTPUT: --- ")
print("C= \n {}".format(C))
 --- OUTPUT: ---
C=
 [[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

6.4. Vector Manipulation

6.4.1. Transposing Vectors

A vector of order \(n>0\) is a set of ordered numbers. \(a = \left[ \begin{array}{c} 4 \\ 3 \end{array} \right], \: e_{1} = \left[ \begin{array}{c} 1 \\ 0 \\ 0 \end{array} \right], \: ...\)

These are column vectors. Row vectors are transposed column vectors, that is \(a'=[4 \: 3], \: e'_{1}=[1 \: 0 \: 0], \: ...\)

In Python vectors are formed as column vectors by default. We can simply transpose vectors using the transpose function t() as follows:

import numpy as np
import matplotlib.pyplot as plt
import math as m
from scipy import stats as st
import time  # Imports system time module to time your script

plt.close('all')  # close all open figures
a = np.array([4,3])      # column vector
aprime = a.transpose()   # row vector
print("a= {}".format(a))
print("a'= {}".format(aprime))
a= [4 3]
a'= [4 3]

6.4.2. Length of Vectors

A vector (at least a two dimensional one) has a convenient geometric representation. It is an arrow, where the two coordinates indicate the direction and length of this arrow. Vector \(a = \left[ \begin{array}{c} 4 \\ 3 \end{array} \right]\) points to the upper right (i.e. 4 over, 3 up). The length of a vector can be calculated using the Pythagoras theorem for the triangle. The length of vector \(a = \left[ \begin{array}{c} a_1 \\ a_2 \end{array} \right]\) where \(a_1\) and \(a_2\) are simply numbers, can be calculated as \(\|a\| = \sqrt{a_1^2 + a_2^2}.\) For our example, the vector norm for \(a = \left[ \begin{array}{c} 4 \\ 3 \end{array} \right] \text{ is } \rightarrow \|a\| = \sqrt{4^2 + 3^2} = 5.\) In Python we can simply define a vector and calculate its norm (or length) as

a = np.array([4,3])
norma = np.sqrt(np.sum(a**2))
print("norm(a) = ", norma)
print("norm(a) = ", np.linalg.norm(a))   # built in norm() command
norm(a) =  5.0
norm(a) =  5.0

6.4.3. Adding Two Vectors

Adding vectors is simple. We just add all numbers of two vectors ‘element-by-element’. So that \(a + b = \left[ \begin{array}{c} a_1 + b_1 \\ a_2+b_2 \end{array} \right].\) In Python this is done with

a = np.array([4,3])
b = np.array([12,5])
print("a= ", a)
print("b= ", b)
print("a+b=", a+b)   # adding up vectors element-by-element
a=  [4 3]
b=  [12  5]
a+b= [16  8]

6.4.4. Multiplication of Vectors

A vector can be multiplied by a number (we call it a scalar denoted as :math:` lambda` to distinguish it from vectors and the numbers that it contains). The scalar is multiplied with all numbers of the vector. If we multiply the vector with \(\lambda > 1\) then the arrow that the vector symbolizes is becoming longer. If we multiply the vector with \(0 < \lambda < 1\) the arrow gets shorter. If we multiply the vector with a negative number \(\lambda < 0\) then the arrow changes direction. More formally this is \(\lambda * a = \left[ \begin{array}{c} \lambda *a_1 \\ \lambda *a_2 \end{array} \right].\)

In Python it’s simply

a = np.array([4,3])
anorm  = np.sqrt(np.sum(a**2)) # norm of vector a
print("a=", a)
print("anorm=", anorm)
b = 5*a
bnorm = np.sqrt(np.sum(b**2)) # norm of longer vector
print("b=", b)
print("bnorm=", bnorm)
c = 0.5*a
cnorm = np.sqrt(np.sum(c**2)) # norm of shorter vector
print("c=", c)
print("cnorm=", cnorm )
d = -3*a       # vector changes direction
dnorm = np.sqrt(np.sum(d**2))
print("d=", d)
print("dnorm=", dnorm)
a= [4 3]
anorm= 5.0
b= [20 15]
bnorm= 25.0
c= [2.  1.5]
cnorm= 2.5
d= [-12  -9]
dnorm= 15.0

When multiplying two vectors we form a so called ‘inner product’ as follows: \(a \times b = \sum_{i=1}^{n}a_i*b_i.\)

The following example shows this with numbers:

\(a = \left[ \begin{array}{c} 4 \\ 3 \end{array} \right] \text{ and } b = \left[ \begin{array}{c} 12 \\ 5 \end{array} \right]\)

then

\(a \times b = 4*12 + 3*5.\) In Python the inner product is simply:

av = np.array([4,3])
bv = np.array([12,5])
print("a= ", av)
print("b= ", bv)
print("a*b =", np.sum(a*b))   # inner product
a=  [4 3]
b=  [12  5]
a*b = 125

6.5. Matrix Manipulation

6.5.1. Indexing and Accessing Elements of a Matrix

A = np.array([[11,12,13,14],[21,22,23,24],[31,32,33,34]])

print("A=", A)
print("---------------")
print()

# Element row 2, column 3 (remember Python starts indexing at 0!!)
print("A[1,2] = ", A[1,2])
print("---------------")
print("A[0,:] = ", A[0,:])   # First row
print("---------------")
print("A[:,0] = ", A[:,0])   # First column
print("---------------")
print("A[1:,:] = ", A[1:,])  # All, except first row
A= [[11 12 13 14]
 [21 22 23 24]
 [31 32 33 34]]
---------------

A[1,2] =  23
---------------
A[0,:] =  [11 12 13 14]
---------------
A[:,0] =  [11 21 31]
---------------
A[1:,:] =  [[21 22 23 24]
 [31 32 33 34]]

If you want to remove columns or rows you can use the take method and assign the result to a new matrix. Let’s say you would like to eliminate the second column from a matrix A.

print("A=", A)
print("---------------")

# Remove column 2 (or take column 1, 3, and 4)
B = A.take([0,2,3], axis=1)
print("B =", B )
A= [[11 12 13 14]
 [21 22 23 24]
 [31 32 33 34]]
---------------
B = [[11 13 14]
 [21 23 24]
 [31 33 34]]

If you want to replace matrix elements based on certain criteria you can use the criteria as a logical statement in combination with the choose method. In the next example we replace all the elements of the matrix that are larger than 90 with the number 90. We start by setting some elements of the matrix equal to 99.

A[:,0] = 99 # Fill first column with 99
print("A = ", A)
print("---------------")

# Replace all elements > 90 with number 90
B = (A>90).choose(A,90)
print("(A>90).choose(A,90) = ", B)
A =  [[99 12 13 14]
 [99 22 23 24]
 [99 32 33 34]]
---------------
(A>90).choose(A,90) =  [[90 12 13 14]
 [90 22 23 24]
 [90 32 33 34]]

If you want to run a loop “through” a matrix you can first find out the dimensions of the matrix using the shape method and then write nested loops, one for each dimension. Note the indentation of the various print statements in the next example!

A = np.array([[11,12,13,14],[21,22,23,24],[31,32,33,34]])
print("A=", A)
print("---------------")
print(" Start loops ")
print("---------------")

nrRows, nrCols = A.shape
for i in range(nrRows):
    print("Row {}".format(i+1))
    print("---------------")

    for j in range(nrCols):
        print("The element in row {} and column {} is: {}".format(i+1, j+1, A[i,j]))

    print("Done with row {}".format(i+1))
    print("---------------")

print("---------------")
print(" All Done ")
print("---------------")
A= [[11 12 13 14]
 [21 22 23 24]
 [31 32 33 34]]
---------------
 Start loops
---------------
Row 1
---------------
The element in row 1 and column 1 is: 11
The element in row 1 and column 2 is: 12
The element in row 1 and column 3 is: 13
The element in row 1 and column 4 is: 14
Done with row 1
---------------
Row 2
---------------
The element in row 2 and column 1 is: 21
The element in row 2 and column 2 is: 22
The element in row 2 and column 3 is: 23
The element in row 2 and column 4 is: 24
Done with row 2
---------------
Row 3
---------------
The element in row 3 and column 1 is: 31
The element in row 3 and column 2 is: 32
The element in row 3 and column 3 is: 33
The element in row 3 and column 4 is: 34
Done with row 3
---------------
---------------
 All Done
---------------

6.5.2. Transposing Matrices

Matrices are “two dimensional” vectors. In Python we define a matrix as

A = np.array([[2,3],[4,5]])
B = np.array([[2,6],[1,3]])
print("A=", A)
print("B=", B)
A= [[2 3]
 [4 5]]
B= [[2 6]
 [1 3]]

Transposing matrices requires again the t() command. It writes each column as row of a new matrix. So that the transpose of matrix \(A\) in the above example becomes:

A = np.array([[2,3],[4,5]])
B = np.array([[2,6],[1,3]])
Atrans = A.transpose()
print("A=", A)
print("A'=", Atrans)
A= [[2 3]
 [4 5]]
A'= [[2 4]
 [3 5]]

If we transpose the transpose of matrix A, we get the original matrix A back.

6.5.3. Adding Matrices

When adding two matrices \(A\) and \(B\) we simply add all the elements of each matrix ‘element-by-element’. Note that the dimensions (i.e. the number of rows and columns) of the two matrices have to be identical. So if matrix \(A\) has dimension \(m \times n\), that is \(m\) rows and \(n\) columns, then matrix \(B\) needs to be of dimensions \(m \times n\) as well.

A = np.array([[2,3],[4,5]])
B = np.array([[2,6],[1,3]])
C =  A+B
print("A=", A)
print("B=", B)
print("C=", C)
A= [[2 3]
 [4 5]]
B= [[2 6]
 [1 3]]
C= [[4 9]
 [5 8]]

6.5.4. Multiplying Matrices

When multiplying two matrices \(A\) and \(B\) we need to make sure that the number of columns of matrix \(A\) is equal the number of rows of matrix \(B\). So if \(A\) has dimension \(m \times n\) then \(B\) needs to have dimension \(n \times r\) since matrix multiplication implies that we form the ‘inner product’ of each row of \(A\) with each column of \(B\). This results in a new matrix of dimension \(m \times r\). Here is an example. Given matrices \(A = \left[ \begin{array}{ccc} 12 & 3 &6 \\ 9 &-1 & -4 \end{array} \right] \text{ and } B = \left[ \begin{array}{cc} 7 & 8 \\ -2 &0 \\ 1 & 11 \end{array} \right]\)

the product of \(A\) \((2 \times 3)\) and \(B\) of dimension \((3 \times 2)\) is a matrix \(C\) with dimension \((2 \times 2)\):

\(AB = \left[ \begin{array}{cc} 12*7+3*(-2)+6*1 & 12*8+3*0+6*11 \\ 9*7+(-1)*(-2)+(-4)*1 & 9*8+(-1)*0+(-4)*11 \end{array} \right].\)

In Python matrix multiplication is achieved using the command dot() or the @ operator. This last operator is new and will only work with Python 3.5 and newer.

A = np.array([[12,3,6],[3,-1,-4]])
B = np.array([[7,8],[-2,0],[1,11]])
C = np.dot(A,B) # matrix multiplication
#D = A @ B # new matrix multiplication operator

print("A=", A)
print("B=", B)
print("C=", C)
#print("D=", D)
A= [[12  3  6]
 [ 3 -1 -4]]
B= [[ 7  8]
 [-2  0]
 [ 1 11]]
C= [[ 84 162]
 [ 19 -20]]

6.5.5. Multiplying a Matrix with a Vector

Vectors are simply \(n \times 1\) or \(1 \times n\) dimensional matrices so that the same rules as above apply for multiplying a matrix with a vector.

A = np.array([[12,3,6],[3,-1,-4]])
b = np.array([7,-2,1])
C = np.dot(A,b) # matrix multiplied by vector: (m x n) x (n x 1) = (m x 1)
print("A=", A)
print("b=", b)
print("C=", C)
A= [[12  3  6]
 [ 3 -1 -4]]
b= [ 7 -2  1]
C= [84 19]

6.6. Key Concepts and Summary

Note

  • A vector is a list of numbers.

  • A matrix is a 2 dimensional vector.

  • A numpy array is a higher dimensional matrix.

  • An array is a higher dimensional list i.e., contents do not have to be numbers

  • Vectors, matrices, and arrays can be indexed and their content can be overwritten

6.7. Self-Check Questions

Todo

  1. Generate a vector with entries from 1 to 20, stepsize 0.5

  2. Divide the first 10 entries by 5

  3. Replace the last entry with the value from the first entry

  4. Sort the vector from largest to smallest element