Matrices

Matrices

Matrices are used to represent data tables. You can think of the columns as different data features/characteristics and the rows as different entries. You can also use matrices together with vectors to represent linear systems of equations. So, let’s learn about them.

Let’s make a matrix to play with:

import numpy as np
np.random.seed(1234) # This is to ensure reproducibility 
                     # of the random numbers
A = np.random.rand(4, 6)
A
array([[0.19151945, 0.62210877, 0.43772774, 0.78535858, 0.77997581,
        0.27259261],
       [0.27646426, 0.80187218, 0.95813935, 0.87593263, 0.35781727,
        0.50099513],
       [0.68346294, 0.71270203, 0.37025075, 0.56119619, 0.50308317,
        0.01376845],
       [0.77282662, 0.88264119, 0.36488598, 0.61539618, 0.07538124,
        0.36882401]])

Notice that we used an list of rows and each row was a list of numbers. Here are the dimensions of A:

A.ndim
2
A.shape
(4, 6)

You see that A.shape has two entries. The first is the number of matrix rows and the second is the number of matrix columns.

Let’s access some elements of A:

A[0, 0]
0.1915194503788923
A[2, 2]
0.37025075479039493

You can access the 3rd row like this:

A[2]
array([0.68346294, 0.71270203, 0.37025075, 0.56119619, 0.50308317,
       0.01376845])

Here is how you can get a the third column:

A[:, 2]
array([0.43772774, 0.95813935, 0.37025075, 0.36488598])

You can get a submatrix, say the first 3x3 submatrix like this:

A[:3, :3]
array([[0.19151945, 0.62210877, 0.43772774],
       [0.27646426, 0.80187218, 0.95813935],
       [0.68346294, 0.71270203, 0.37025075]])

And so on.

You can multiply matrices with numbers:

2.0 * A
array([[0.3830389 , 1.24421754, 0.87545548, 1.57071717, 1.55995162,
        0.54518521],
       [0.55292851, 1.60374436, 1.91627871, 1.75186527, 0.71563454,
        1.00199025],
       [1.36692587, 1.42540405, 0.74050151, 1.12239237, 1.00616633,
        0.0275369 ],
       [1.54565324, 1.76528238, 0.72977197, 1.23079236, 0.15076248,
        0.73764801]])
A * 3.0
array([[0.57455835, 1.86632631, 1.31318322, 2.35607575, 2.33992742,
        0.81777782],
       [0.82939277, 2.40561653, 2.87441806, 2.6277979 , 1.07345181,
        1.50298538],
       [2.05038881, 2.13810608, 1.11075226, 1.68358856, 1.5092495 ,
        0.04130535],
       [2.31847986, 2.64792357, 1.09465795, 1.84618854, 0.22614372,
        1.10647202]])

You can raise them to a power:

A ** 3
array([[7.02487596e-03, 2.40768115e-01, 8.38710745e-02, 4.84399833e-01,
        4.74507846e-01, 2.02554647e-02],
       [2.11308499e-02, 5.15603000e-01, 8.79601649e-01, 6.72066304e-01,
        4.58124896e-02, 1.25747831e-01],
       [3.19260289e-01, 3.62012846e-01, 5.07560548e-02, 1.76743777e-01,
        1.27326662e-01, 2.61008780e-06],
       [4.61579191e-01, 6.87626449e-01, 4.85815698e-02, 2.33058198e-01,
        4.28341211e-04, 5.01715527e-02]])

You can pass them through functions:

np.exp(A)
array([[1.21108839, 1.86285223, 1.54918307, 2.19319324, 2.18141949,
        1.31336508],
       [1.31845982, 2.22971144, 2.60684155, 2.40111361, 1.43020426,
        1.65036277],
       [1.98072499, 2.03949459, 1.44809769, 1.75276788, 1.6538124 ,
        1.01386367],
       [2.16587973, 2.41727577, 1.44034978, 1.85038954, 1.07829516,
        1.44603309]])

You can add them together:

A + A
array([[0.3830389 , 1.24421754, 0.87545548, 1.57071717, 1.55995162,
        0.54518521],
       [0.55292851, 1.60374436, 1.91627871, 1.75186527, 0.71563454,
        1.00199025],
       [1.36692587, 1.42540405, 0.74050151, 1.12239237, 1.00616633,
        0.0275369 ],
       [1.54565324, 1.76528238, 0.72977197, 1.23079236, 0.15076248,
        0.73764801]])

You can multiply them together (assuming that the dimensions match):

B = np.random.randn(A.shape[1], 6)
B
array([[ 0.19342138,  0.55343891,  1.31815155, -0.46930528,  0.67555409,
        -1.81702723],
       [-0.18310854,  1.05896919, -0.39784023,  0.33743765,  1.04757857,
         1.04593826],
       [ 0.86371729, -0.12209157,  0.12471295, -0.32279481,  0.84167471,
         2.39096052],
       [ 0.07619959, -0.56644593,  0.03614194, -2.0749776 ,  0.2477922 ,
        -0.89715678],
       [-0.13679483,  0.01828919,  0.75541398,  0.21526858,  0.84100879,
        -1.44581008],
       [-1.40197328, -0.1009182 , -0.54824245, -0.14461951,  0.35402033,
        -0.03551303]])
C = A.dot(B)
C
array([[-0.12781667,  0.25323786,  0.52768426, -1.5223738 ,  2.0965895 ,
        -0.49268621],
       [ 0.04962235,  0.34499945,  0.19218844, -1.98141413,  2.52857154,
         1.3062625 ],
       [ 0.27612724,  0.77770417,  1.05631158, -1.25793799,  2.08698996,
        -0.84250615],
       [-0.17747985,  0.93342159,  0.59003913, -1.49668376,  2.10029534,
        -0.28282272]])
C.shape
(4, 6)

You can multiply a matrix with a vector:

a = np.random.randn(A.shape[1])
a
array([ 0.56573831,  1.5456588 , -0.97423633, -0.07034488,  0.30796886,
       -0.20849876])
A.dot(a)
array([0.7715946 , 0.40649538, 1.24012955, 1.34902033])

You can also interchange the rows of a matrix with each columns. Let’s make a new matrix for this:

A.T
array([[0.19151945, 0.27646426, 0.68346294, 0.77282662],
       [0.62210877, 0.80187218, 0.71270203, 0.88264119],
       [0.43772774, 0.95813935, 0.37025075, 0.36488598],
       [0.78535858, 0.87593263, 0.56119619, 0.61539618],
       [0.77997581, 0.35781727, 0.50308317, 0.07538124],
       [0.27259261, 0.50099513, 0.01376845, 0.36882401]])
A.T.shape
(6, 4)

There are some special numpy functions for creating arrays. Here is how to make an array of zeros:

np.zeros((4, 5))
array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])

An array of ones:

np.ones((5, 2))
array([[1., 1.],
       [1., 1.],
       [1., 1.],
       [1., 1.],
       [1., 1.]])

A unit matrix:

np.eye(5)
array([[1., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       [0., 0., 1., 0., 0.],
       [0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 1.]])

There are some useful functions that can be applied to matrices. For example, here is how you can sum all the elements of a matrix:

A.sum()
12.78492251426415

But what if you wanted to sum all the rows for each column? You can do it like this:

A.sum(axis=0)
array([1.92427326, 3.01932417, 2.13100383, 2.83788358, 1.71625749,
       1.15618019])

Let’s convince ourselves that this works as expected by working say on the third column of A. Here is the code to get the third row:

A[:, 2]
array([0.43772774, 0.95813935, 0.37025075, 0.36488598])

Let’s sum it up:

A[:, 2].sum()
2.1310038313825865

Is this the same as the the third element of A.sum(axis=0)?

Okay, so when we do sum(axis=0) we are saying sum over the axis 0 of the matrix, which corresponds to the rows. What if we wanted to sum over the columns. We could do this:

A.sum(axis=1)
array([3.08928296, 3.77122082, 2.84446352, 3.07995522])

Verify this by hand as well. In the code-block below extract the 2nd row of the matrix and sum it up:

# your code here