Covariant-matrix of a matrix¶
Types of variance and covariance¶
- Population variance: Used when you have data for the entire population. $$\sigma^2 = \frac{1}{N} \sum_{i=1}^{N} (x_i - \mu)^2$$
- Sample variance: Used when you only have a sample of the population. To avoid bias, divide by $n-1$ instead of $n$. $$s^2 = \frac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})^2$$ The same thing goes for covariance.
- Population covariance $$\text{Cov}(X, Y) = \frac{1}{N} \sum_{i=1}^{N} (x_i - \mu_X)(y_i - \mu_Y)$$
- Sample covariance $$\text{Cov}(X, Y) = \frac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})$$ When estimating variance or covariance from a sample, using $n$ underestimates the true variability (bias). So instead of dividing by $n$, we divide by $n-1$. This is called Bessel's correction.
There are 2 popular ways of finding the covariant-matrix of a matrix:
1. The direct method¶
If we want to find the covariant matrix w.r.t the column: $$ \begin{bmatrix} Var(r_1) & Cov(r_1, r_2) & \cdots & Cov(r_1, r_n) \\ Cov(r_2, r_1) & Var(r_2) & \cdots & Cov(r_2, r_n) \\ \vdots & \vdots & \ddots & \vdots \\ Cov(r_n, r_1) & Cov(r_n, r_2) & \cdots & Var(r_n) \end{bmatrix} $$ If we want to find the covariant matrix w.r.t the row, we just transpose the input matrix and do the same operation.
2. The matrix formulation method¶
- If we want to find the covariant matrix w.r.t the column, calculate the mean of each column and subtract that value from each of the columns. Let the resultant matrix be $X$. $$\text{Covariance matrix with Bessel's correction}=\frac{1}{N-1}X \ X^T$$
- If we want to find the covariant matrix w.r.t the row, calculate the mean of each row and subtract that value from each of the rows. Let the resultant matrix be $X$. $$\text{Covariance matrix with Bessel's correction}=\frac{1}{n-1}X^T \ X$$
In [1]:
matrix = [
[2, 5, -7, 8],
[-5, 9, -1, -3],
[-1, 2, -2, 3],
[-8, -6, 7, 10]
]
Function to find covariance between 2 arrays¶
In [2]:
from typing import List
def covariance(arr1: List[float], arr2: List[float], ddof: int = 1) -> float:
"""
Calculate the covariance between two arrays using only standard Python.
Parameters:
arr1, arr2 : list
Input arrays of the same length
Returns:
float
Covariance between arr1 and arr2
"""
if len(arr1) != len(arr2):
raise ValueError("Arrays must have the same length")
if len(arr1) == 0:
raise ValueError("Arrays cannot be empty")
n = len(arr1)
mean1 = sum(arr1) / n
mean2 = sum(arr2) / n
# Calculate covariance: sum((x_i - mean1) * (y_i - mean2)) / (n - 1)
return sum((x - mean1) * (y - mean2) for x, y in zip(arr1, arr2)) / (n - ddof)
Function to multiply 2 matrices¶
In [3]:
def matrix_multiply(A: List[List[float]], B: List[List[float]]) -> List[List[float]]:
"""
Multiply two matrices A and B using standard Python.
Parameters:
A : list of lists
First matrix (m x n)
B : list of lists
Second matrix (n x p)
Returns:
list of lists
Resulting matrix (m x p)
"""
# Check if matrices can be multiplied
if not A or not B or len(A[0]) != len(B):
raise ValueError("Invalid matrix dimensions for multiplication")
rows_A = len(A)
cols_A = len(A[0])
cols_B = len(B[0])
# Initialize result matrix with zeros
result = [[0 for _ in range(cols_B)] for _ in range(rows_A)]
# Perform matrix multiplication
for i in range(rows_A):
for j in range(cols_B):
for k in range(cols_A):
result[i][j] += A[i][k] * B[k][j]
return result
Function to multiply a scalar to a matrix¶
In [4]:
def scalar_matrix_multiply(scalar, matrix):
"""
Multiply a scalar by each element of a matrix.
Parameters:
scalar : number (int or float)
The scalar value to multiply
matrix : list of lists
Input matrix (m x n)
Returns:
list of lists
Resulting matrix (m x n) with each element multiplied by scalar
"""
if not matrix or not matrix[0]:
raise ValueError("Matrix cannot be empty")
rows = len(matrix)
cols = len(matrix[0])
# Initialize result matrix
result = [[0 for _ in range(cols)] for _ in range(rows)]
# Perform scalar multiplication
for i in range(rows):
for j in range(cols):
result[i][j] = round(scalar * matrix[i][j], 2)
return result
Function to transpose a matrix¶
In [5]:
def matrix_transpose(matrix: List[List[float]]) -> List[List[float]]:
"""
Calculate the transpose of a matrix.
Parameters:
matrix : list of lists
Input matrix (m x n)
Returns:
list of lists
Transposed matrix (n x m)
"""
if not matrix or not matrix[0]:
raise ValueError("Matrix cannot be empty")
rows = len(matrix)
cols = len(matrix[0])
# Initialize result matrix with dimensions (cols x rows)
result = [[0 for _ in range(rows)] for _ in range(cols)]
# Compute transpose
for i in range(rows):
for j in range(cols):
result[j][i] = matrix[i][j]
return result
Direct method to find covariance¶
In [6]:
def mean(arr: List[int]):
return sum(arr) / len(arr)
def variance(arr: List[int], ddof: int = 1):
n = len(arr)
mean_ = sum(arr) / n
return sum([(mean_ - i) ** 2 for i in arr]) / (n - ddof)
def covariance_matrix_direct(matrix_: List[List[float]], ddof_ = 1, axis = 0) -> List[List[float]]:
""" Parameters
ddof_: 0 if bessel's correction is to be applied, otherwise 1
axis: 0 if we want the correlation between rows, otherwise (for column) 1
"""
n = len(matrix)
m = len(matrix_[0])
if not n == m:
raise ValueError("Number of rwos and column of matrix must be equal")
if not (axis == 0 or axis == 1):
raise ValueError("Axis must be either 0 or 1")
matrix_t = (matrix_transpose(matrix_) if axis == 1 else matrix_)
covariance_matrix = []
for i in range(n):
arr = []
for j in range(n):
if i == j:
arr.append(round(variance(matrix_t[i], ddof = ddof_), 2))
else:
arr.append(round(covariance(matrix_t[i], matrix_t[j], ddof = ddof_), 2))
covariance_matrix.append(arr[:])
return covariance_matrix
Matrix formulation method to find covariance¶
In [7]:
def covariance_matrix_formulation(matrix_: List[List[float]], ddof_: int = 1, axis = 0) -> List[List[float]]:
""" Parameters
ddof_: 0 if bessel's correction is to be applied, otherwise 1
axis: 0 if we want the correlation between rows, otherwise (for column) 1
"""
n = len(matrix_)
m = len(matrix_[0])
if not n == m:
raise ValueError("Number of rwos and column of matrix must be equal")
if not (axis == 0 or axis == 1):
raise ValueError("Axis must be either 0 or 1")
mean_vector = (
[mean([matrix_[i][j] for i in range(n)]) for j in range(n)] if axis == 1
else [mean([matrix_[i][j] for j in range(n)]) for i in range(n)]
)
matrix_t = [row[:] for row in matrix] # deep-copy of the matrix
for row in range(n):
for column in range(n):
matrix_t[row][column] -= mean_vector[(column if axis == 1 else row)]
if axis == 1:
covariance_matrix = matrix_multiply(matrix_transpose(matrix_t), matrix_t)
else:
covariance_matrix = matrix_multiply(matrix_t, matrix_transpose(matrix_t))
return scalar_matrix_multiply(1.0 / (n - ddof_), covariance_matrix)
In [8]:
covariance_matrix_direct(matrix, axis = 1)
Out[8]:
[[19.33, 13.67, -24.0, 0.67], [13.67, 40.33, -27.5, -28.67], [-24.0, -27.5, 33.58, 8.17], [0.67, -28.67, 8.17, 33.67]]
W.r.t row¶
In [9]:
covariance_matrix_direct(matrix, axis = 0)
Out[9]:
[[42.0, 6.0, 14.0, -7.0], [6.0, 38.67, 5.33, -17.0], [14.0, 5.33, 5.67, 3.5], [-7.0, -17.0, 3.5, 82.25]]
In [10]:
covariance_matrix_direct(matrix, ddof_ = 0, axis = 1)
Out[10]:
[[14.5, 10.25, -18.0, 0.5], [10.25, 30.25, -20.62, -21.5], [-18.0, -20.62, 25.19, 6.12], [0.5, -21.5, 6.12, 25.25]]
W.r.t row¶
In [11]:
covariance_matrix_direct(matrix, ddof_ = 0, axis = 0)
Out[11]:
[[31.5, 4.5, 10.5, -5.25], [4.5, 29.0, 4.0, -12.75], [10.5, 4.0, 4.25, 2.62], [-5.25, -12.75, 2.62, 61.69]]
In [12]:
covariance_matrix_formulation(matrix, axis = 1)
Out[12]:
[[19.33, 13.67, -24.0, 0.67], [13.67, 40.33, -27.5, -28.67], [-24.0, -27.5, 33.58, 8.17], [0.67, -28.67, 8.17, 33.67]]
W.r.t row¶
In [13]:
covariance_matrix_formulation(matrix, axis = 0)
Out[13]:
[[42.0, 6.0, 14.0, -7.0], [6.0, 38.67, 5.33, -17.0], [14.0, 5.33, 5.67, 3.5], [-7.0, -17.0, 3.5, 82.25]]
In [14]:
covariance_matrix_formulation(matrix, ddof_ = 0, axis = 1)
Out[14]:
[[14.5, 10.25, -18.0, 0.5], [10.25, 30.25, -20.62, -21.5], [-18.0, -20.62, 25.19, 6.12], [0.5, -21.5, 6.12, 25.25]]
W.r.t row¶
In [15]:
covariance_matrix_formulation(matrix, ddof_ = 0, axis = 0)
Out[15]:
[[31.5, 4.5, 10.5, -5.25], [4.5, 29.0, 4.0, -12.75], [10.5, 4.0, 4.25, 2.62], [-5.25, -12.75, 2.62, 61.69]]
In [16]:
import numpy as np
covariance_matrix = np.cov(matrix, rowvar = False)
covariance_matrix
Out[16]:
array([[ 19.33333333, 13.66666667, -24. , 0.66666667],
[ 13.66666667, 40.33333333, -27.5 , -28.66666667],
[-24. , -27.5 , 33.58333333, 8.16666667],
[ 0.66666667, -28.66666667, 8.16666667, 33.66666667]])
W.r.t row¶
In [17]:
covariance_matrix = np.cov(matrix, rowvar = True)
covariance_matrix
Out[17]:
array([[ 42. , 6. , 14. , -7. ],
[ 6. , 38.66666667, 5.33333333, -17. ],
[ 14. , 5.33333333, 5.66666667, 3.5 ],
[ -7. , -17. , 3.5 , 82.25 ]])
In [18]:
covariance_matrix = np.cov(matrix, ddof = 0, rowvar = False)
covariance_matrix
Out[18]:
array([[ 14.5 , 10.25 , -18. , 0.5 ],
[ 10.25 , 30.25 , -20.625 , -21.5 ],
[-18. , -20.625 , 25.1875, 6.125 ],
[ 0.5 , -21.5 , 6.125 , 25.25 ]])
W.r.t row¶
In [19]:
covariance_matrix = np.cov(matrix, ddof = 0, rowvar = True)
covariance_matrix
Out[19]:
array([[ 31.5 , 4.5 , 10.5 , -5.25 ],
[ 4.5 , 29. , 4. , -12.75 ],
[ 10.5 , 4. , 4.25 , 2.625 ],
[ -5.25 , -12.75 , 2.625 , 61.6875]])