# Matrices in Python

## Matrices in Python

A matrix is a two-dimensional data structure in which numbers are organized in rows and columns. For example:

$$A= \begin{pmatrix} 3 & 1 & 5 \\ 9 & 8 & -1 \\ 10 & 12 & 2 \end{pmatrix}$$

This matrix is a 3x3 matrix because it has 3 rows and 3 columns.

#### Matrices in Python

Python has no built-in type for matrices. However, we can treat a list list as a matrix. For example:

###### Example 1 :
                                M = [[3, 1, 5], [9, 8, -1], [10, 12, 2]]


#### Nested list

Let's see how to work with a nested list.

###### Example 1 :
                                M = [ [3, 1, 5], [9, 8, -1], [10, 12, 2] ]
print("M =", M)
print("M =", M) # 2nd row
print("M =", M) # 3rd element in the second row
print("A[-1] =", M[-1])   # last element in first row

col3 = [];        # empty list
for ligne in M:
col3.append(ligne)  # add the third element

print("col3 : ",col3)

M = [[3, 1, 5], [9, 8, -1], [10, 12, 2]]
M = [9, 8, -1]
M = -1
A[-1] = 5
col3 : [5, -1, 2

The sum of two matrices of size (m, n), $$(A_ {i, j})$$ and $$(B_ {i, j})$$ denoted $$A + B$$ is again a matrix \ ((C_ {i, j})\) of size (m, n), obtained by adding the corresponding elements: $$\forall i,j$$ : $$c_ {i, j} = a_ {i, j} + b_ {i, j}$$

###### Method 1 :
                                A = [ [3, 1, 5], [9, 8, -1], [10, 12, 2] ]
B = [ [8, -1, 8], [2, 1, 3], [18, 2, 32] ]

n=len(A) # number of rows
m=len(A) # nnumber of columns

C = [[0 for i in range(m)] for i in range(n)] # initialize a matrix with the same size as A

# for each row
for i in range(n):
# for each column
for j in range(m):
C[i][j]= A[i][j] + B[i][j]

print("A : ", A)
print("B : ", B)
print("A + B : ", C)

A : [[3, 1, 5], [9, 8, -1], [10, 12, 2]]
B : [[8, -1, 8], [2, 1, 3], [18, 2, 32]]
A + B : [[11, 0, 13], [11, 9, 2], [28, 14, 34]]

In this program, we used nested for loops to cycle through each row and each column. At each point, we add the corresponding elements in the two matrices and store them in C.

###### Method 2 :
                                A = [ [3, 1, 5], [9, 8, -1], [10, 12, 2] ]
B = [ [8, -1, 8], [2, 1, 3], [18, 2, 32] ]

n=len(A) # number of rows
m=len(A) # number of columns

C = [[A[i][j] + B[i][j] for j in range(m)] for i in range(n)]

print("A : ", A)
print("B : ", B)
print("A + B : ", C)

A : [[3, 1, 5], [9, 8, -1], [10, 12, 2]]
B : [[8, -1, 8], [2, 1, 3], [18, 2, 32]]
A + B : [[11, 0, 13], [11, 9, 2], [28, 14, 34]]

#### Transpose of a matrix

In mathematics, the matrix transposed from a matrix A of size (m, n) is a matrix denoted $$A^t$$ of size (n, m), obtained by exchanging the rows and columns of A.
If we denote B the transposed matrix of A, we have $$b_ {i, j} =a_ {j, i}$$

###### Method 1 :
                                A = [ [3, 1, 5], [9, 8, -1], [10, 12, 2] ]

n=len(A) # number of rows
m=len(A) # number of columns

C = [[0 for i in range(m)] for i in range(n)] # initialize a matrix with the same size as A

# for each row
for i in range(n):
# for each column
for j in range(m):
C[j][i]= A[i][j]

print("A : ", A)
print("transpose of A is : ", C)

A : [[3, 1, 5], [9, 8, -1], [10, 12, 2]]
transpose of A is : [[3, 9, 10], [1, 8, 12], [5, -1, 2]]
###### Method 2 :
                                A = [ [3, 1, 5], [9, 8, -1], [10, 12, 2] ]
B = [ [8, -1, 8], [2, 1, 3], [18, 2, 32] ]

n=len(A) # number of rows
m=len(A) # number of columns

C = [[A[j][i] for j in range(n)] for i in range(m)]

print("A : ", A)
print("transpose of A is : ", C)

A : [[3, 1, 5], [9, 8, -1], [10, 12, 2]]
transpose of A is : [[3, 9, 10], [1, 8, 12], [5, -1, 2]]

#### Multiply two matrices

Two matrices can only be multiplied if the number of columns in the first matrix is the same as the number of rows in the second matrix.

If $$A = (a_ {i, j})$$ is a matrix of size (m, n), and $$B = (b_ {i, j})$$ is a matrix of size (n, p), then their product, noted $$AB = (c_ {i, j})$$ is a matrix of size (m, p) defined by:

$$\forall i,j : c_{i,j}=\sum_{k=1}^{n} a_{ik}*b_{kj}$$

                                A = [ [3, 1, 5], [9, 8, -1], [10, 12, 2] ]
B = [ [8, -1, 8], [2, 1, 3], [18, 2, 32] ]

n=len(A) # number of rows in A
m=len(B) # number of columns in B
p=len(B) # number of rows in B

C = [[0 for i in range(m)] for i in range(n)] # matrix of m cols and n rows

# for each row in A
for i in range(n):
# for each cols in B
for j in range(m):
# each row of B
for k in range(p):
C[i][j] += A[i][k] * B[k][j]

print("A : ", A)
print("B : ", B)
print("A * B : ", C)

A : [[3, 1, 5], [9, 8, -1], [10, 12, 2]]
B : [[8, -1, 8], [2, 1, 3], [18, 2, 32]]
A * B : [[116, 8, 187], [70, -3, 64], [140, 6, 180]]

Using nested lists as a matrix works for simple calculation tasks. However, there is a better way to work with matrices in Python using the NumPy library.

#### NumPy Arrays

NumPy is a package for scientific computing that supports a powerful N-dimensional array object. Before you can use NumPy, you need to install it.

NumPy provides a multidimensional array of numbers (which is actually an object). Let's take an example:

###### Example 1 :
                                import numpy as np
a = np.array([1, 2, 3])
print(a)
print(type(a))

[1 2 3]
<c lass 'numpy.ndarray'>

As you can see, the array class of NumPy is called ndarray.

#### How to create a NumPy array?

There are several ways to create NumPy arrays.

Array of integers, reals and complex numbers

###### Example 2 :
                                import numpy as np

A = np.array([[1, 2, 3], [3, 4, 5]]) # array of integers
print("A : " , A)

B = np.array([[1.1, 2, 3], [3, 4, 5]]) # array of reals
print("B : ", B)

C = np.array([[1, 2, 3], [3, 4, 5]], dtype = complex) # array of complex numbers
print("C : ", C)


A : [ [1 2 3] [3 4 5] ] B : [ [1.1 2. 3. ] [3. 4. 5. ] ] C : [ [1.+0.j 2.+0.j 3.+0.j] [3.+0.j 4.+0.j 5.+0.j] ]

Array of zeros and ones

###### Example 3 :
                                import numpy as np

A = np.zeros( (2, 3) ) # array of zeros
print("A : " , A)

B =  np.ones( (4, 2) )# array of ones
print("B : ", B)

A : [ [0. 0. 0.] [0. 0. 0.] ] B : [ [1. 1.] [1. 1.] [1. 1.] [1. 1.] ]

Use arange() and shape()

###### Example 4 :
                                import numpy as np

A = np.arange(6)
print("A : " , A)

B =  A.reshape(2, 6)
print("B : ", B)

A : [0 1 2 3 4 5] B : [ [0 1 2] [3 4 5] ]

Use linspace(): linspace() will create arrays with a specified number of elements and spaced equally between the specified start and end values. For example:

###### Example 5 :
                                import numpy as np

A=np.linspace(1., 4., 6)
print("A : ", A)

A : [1. 1.6 2.2 2.8 3.4 4. ]

#### Matrix operations

Above, we have given you 3 examples: adding two matrices, multiplying two matrices and transposing a matrix. We used nested lists to write these programs. Let's see how we can do the same task using the NumPy array.

We use the operator + to add the corresponding elements of two NumPy matrices.

###### Example 1 :
                                import numpy as np

A = np.array([ [3, 1, 5], [9, 8, -1], [10, 12, 2] ])
B = np.array([ [8, -1, 8], [2, 1, 3], [18, 2, 32] ])

C= A + B
print("A : ", A)
print("B : ", B)
print("A + B : ", C)

A : [[3, 1, 5], [9, 8, -1], [10, 12, 2]]
B : [[8, -1, 8], [2, 1, 3], [18, 2, 32]]
A + B : [[11, 0, 13], [11, 9, 2], [28, 14, 34]]

#### Multiply two matrices

To multiply two matrices, we use the dot() method.

                                import numpy as np

A = np.array([ [3, 1, 5], [9, 8, -1], [10, 12, 2] ])
B = np.array([ [8, -1, 8], [2, 1, 3], [18, 2, 32] ])

C = A.dot(B)

print("A : ", A)
print("B : ", B)
print("A * B : ", C)

A : [[3, 1, 5], [9, 8, -1], [10, 12, 2]]
B : [[8, -1, 8], [2, 1, 3], [18, 2, 32]]
A * B : [[116, 8, 187], [70, -3, 64], [140, 6, 180]]
Notes ! * is used for the multiplication of arrays (multiplication of corresponding elements of two arrays) and not of matrices.
                                import numpy as np

A = np.array([ [3, 1, 5], [10, 12, 2] ])

C = A*2

print("A : ", A)
print("A * 2 : ", C)

A : [ [ 3 1 5] [10 12 2] ] A * 2 : [ [ 6 2 10] [20 24 4] ]

#### Transpose of a matrix

We use the transpose() method to calculate the transpose of a matrix.

                                import numpy as np

A = np.array([ [3, 1, 5], [9, 8, -1], [10, 12, 2] ])

C = A.transpose()

print("A : ", A)
print("Transposé de A : ", C)

A : [[ 3 1 5] [ 9 8 -1] [10 12 2]] Transposé de A : [[ 3 9 10] [ 1 8 12] [ 5 -1 2]]

#### Access to the elements of the matrix, the rows, and the columns

As with lists, we can access the elements of the matrix using the index. Let's start with a one-dimensional NumPy array.

###### Example 1 :
                                import numpy as np
A = np.array([2, 4, 6, 8, 10])

print("A =", A)     # 1st element
print("A =", A)     # 3rd element
print("A[-1] =", A[-1])   # last element

A = 2
A = 6
A[-1] = 10

Now let's see how to access the elements of a two-dimensional array (matrix).

###### Example 1 :
                                import numpy as np
A = np.array([[1, 4, 5, 12], [-5, 8, 9, 0], [-6, 7, 11, 19]])

#  1st element of 1st row
print("A =", A)

# 3rd element of 2nd row
print("A =", A)

# last element of last row
print("A[-1][-1] =", A[-1][-1])

A = 1
A = 9
A[-1][-1] = 19

###### Example 1 :
                                import numpy as np
A = np.array([ [1, 4, 5, 12], [-5, 8, 9, 0], [-6, 7, 11, 19] ])

print("A =", A) # 1st row
print("A =", A) # 3rd row
print("A[-1] =", A[-1]) # last row

A = [ 1 4 5 12]
A = [-6 7 11 19]
A[-1] = [-6 7 11 19]

###### Example 1 :
                                    import numpy as np
A = np.array([ [1, 4, 5, 12], [-5, 8, 9, 0], [-6, 7, 11, 19] ])

print("A[:,0] =",A[:,0]) # 1st column
print("A[:,2] =", A[:,2]) # 3rd column
print("A[:,-1] =", A[:,-1]) # last column

A[:,0] = [ 1 -5 -6]
A[:,2] = [ 5 9 11]
A[:,-1] = [12 0 19]

#### Splitting a matrix

Splitting a one-dimensional NumPy array is similar to a list.

###### Example 1 :
                                import numpy as np
A = np.array([1, 3, 5, 7, 9, 7, 5])

# 3rd to 5th element
print("A[2:5] : ", A[2:5])

# 1st to 4th element
print("A[:-5] : ", A[:-5])

# 6th to last element included
print("A[5:] : ", A[5:])

# the whole matrix
print("A[:] : ", A[:])

# reverse a list
print("A[::-1] : ", A[::-1])

A[2:5] : [5 7 9]
A[:-5] : [1 3]
A[5:] : [7 5]
A[:] : [1 3 5 7 9 7 5]
A[::-1] : [5 7 9 7 5 3 1]

Now let's see how to split a matrix.

###### Example 1 :
                                import numpy as np
A = np.array([[1, 4, 5, 12, 14], [-5, 8, 9, 0, 17],[-6, 7, 11, 19, 21]])

print("A[:2, :4] : ", A[:2, :4])  # first two rows and four columns
print("A[:1,] : ", A[:1,])  # first row, all columns
print("A[:,2] : ", A[:,2])  # all rows, the second column
print("A[:, 2:5] : ", A[:, 2:5])  # all rows, third to fifth column

A[:2, :4] : [[ 1 4 5 12] [-5 8 9 0]]
A[:1,] : [[ 1 4 5 12 14]]
A[:,2] : [ 5 9 11]
A[:, 2:5] : [[ 5 12 14][ 9 0 17] [11 19 21]]

As you can see, using NumPy (instead of nested lists) makes it much easier to work with matrices 