#! pip install numpy scipy matplotlib scikit-learn
import numpy as np
import scipy.linalg as linalg
import matplotlib.pyplot as plt
Introduction to this course
This course provides a detailed foundation in linear algebra with practical Python and R code examples. Linear Algebra is a fundamental mathematical discipline that plays a crucial role in various fields, including machine learning and data science. This course covers essential concepts such as vectors, matrices, systems of linear equations, eigenvalues, eigenvectors, singular value decomposition (SVD), and principal component analysis (PCA). By the end of this course, you will have a solid understanding of linear algebra and its applications in data science and machine learning.
What is Linear Algebra?
Definition
Linear algebra is a branch of mathematics that deals with vector spaces and linear mappings between these spaces. It provides a framework for representing and solving complex problems using vectors, matrices, and linear transformations.
Significance and Applications in various fields
Learning linear algebra is essential for understanding various data science and machine learning concepts, such as dimensionality reduction, regression, and support vector machines (SVM). Linear algebra has numerous applications in different fields, including: Physics, Engineering, Economics, Finance, Cryptography, Data Analysis, Operations Research, Quantum Computing, Statistics, and Natural Language Processing.
Programming Language Setup
Installing and Loading Required Packages
- NumPy (for numerical operations)
- Scipy (for scientific computing)
- Matplotlib (for visualization)
Installing and Loading Required Packages
- Matrix (for advanced matrix operations)
- pracma (for additional mathematical functions)
- ggplot2 (for visualization)
# install.packages(c("Matrix", "pracma", "ggplot2"))
library(Matrix)
library(pracma)
library(ggplot2)
Vectors
Creating vectors
Vector addition, subtraction, and scalar multiplication
# Creating vectors
= np.array([1, 2, 3])
v1 = np.array([4, 5, 6])
v2
# Vector addition
= v1 + v2
v_add
# Vector subtraction
= v1 - v2
v_sub
# Scalar multiplication
= 3
scalar = scalar * v1
v_scalar
print("Vector Addition:", v_add)
Vector Addition: [5 7 9]
print("Vector Subtraction:", v_sub)
Vector Subtraction: [-3 -3 -3]
print("Scalar Multiplication:", v_scalar)
Scalar Multiplication: [3 6 9]
# Creating vectors
<- c(1, 2, 3)
v1 <- c(4, 5, 6)
v2
# Vector addition
<- v1 + v2
v_add
# Vector subtraction
<- v1 - v2
v_sub
# Scalar multiplication
<- 3
scalar <- scalar * v1
v_scalar
print(paste("Vector Addition:", paste(v_add, collapse = ", ")))
[1] "Vector Addition: 5, 7, 9"
print(paste("Vector Subtraction:", paste(v_sub, collapse = ", ")))
[1] "Vector Subtraction: -3, -3, -3"
print(paste("Scalar Multiplication:", paste(v_scalar, collapse = ", ")))
[1] "Scalar Multiplication: 3, 6, 9"
Dot Product
= np.dot(v1, v2)
dot_product print("Dot Product:", dot_product)
Dot Product: 32
<- sum(v1 * v2)
dot_product print(paste("Dot Product:", dot_product))
[1] "Dot Product: 32"
Norm of a Vector
= np.linalg.norm(v1)
norm_v1 print("Norm of v1:", norm_v1)
Norm of v1: 3.7416573867739413
<- norm(v1, type = "2")
norm_v1 print(paste("Norm of v1:", norm_v1))
[1] "Norm of v1: 3.74165738677394"
Matrices
Creating matrices
Matrix addition, subtraction, and scalar multiplication
# Creating matrices
= np.array([[1, 2], [3, 4]])
A = np.array([[5, 6], [7, 8]])
B
# Matrix addition
= A + B
A_add_B
# Matrix subtraction
= A - B
A_sub_B
# Scalar multiplication
= 2
scalar = scalar * A
A_scalar
print("Matrix Addition:\n", A_add_B)
Matrix Addition:
[[ 6 8]
[10 12]]
print("Matrix Subtraction:\n", A_sub_B)
Matrix Subtraction:
[[-4 -4]
[-4 -4]]
print("Scalar Multiplication:\n", A_scalar)
Scalar Multiplication:
[[2 4]
[6 8]]
# Creating matrices
<- matrix(c(1, 3, 2, 4), nrow = 2, byrow = TRUE)
A <- matrix(c(5, 7, 6, 8), nrow = 2, byrow = TRUE)
B # Matrix addition
<- A + B
A_add_B
# Matrix subtraction
<- A - B
A_sub_B
# Scalar multiplication
<- 2
scalar <- scalar * A
A_scalar
print("Matrix Addition:")
[1] "Matrix Addition:"
print(A_add_B)
[,1] [,2]
[1,] 6 10
[2,] 8 12
print("Matrix Subtraction:")
[1] "Matrix Subtraction:"
print(A_sub_B)
[,1] [,2]
[1,] -4 -4
[2,] -4 -4
print("Scalar Multiplication:")
[1] "Scalar Multiplication:"
print(A_scalar)
[,1] [,2]
[1,] 2 6
[2,] 4 8
Matrix Multiplication
= np.dot(A, B)
A_mul_B print("Matrix Multiplication:\n", A_mul_B)
Matrix Multiplication:
[[19 22]
[43 50]]
<- A %*% B
A_mul_B print("Matrix Multiplication:")
[1] "Matrix Multiplication:"
print(A_mul_B)
[,1] [,2]
[1,] 23 31
[2,] 34 46
Transpose of a Matrix
= A.T
A_transpose print("Transpose of A:\n", A_transpose)
Transpose of A:
[[1 3]
[2 4]]
<- t(A)
A_transpose print("Transpose of A:")
[1] "Transpose of A:"
print(A_transpose)
[,1] [,2]
[1,] 1 2
[2,] 3 4
Determinant and Inverse
= np.linalg.det(A)
det_A = np.linalg.inv(A)
inv_A
print("Determinant of A:", det_A)
Determinant of A: -2.0000000000000004
print("Inverse of A:\n", inv_A)
Inverse of A:
[[-2. 1. ]
[ 1.5 -0.5]]
<- det(A)
det_A <- solve(A)
inv_A
print(paste("Determinant of A:", det_A))
[1] "Determinant of A: -2"
print("Inverse of A:")
[1] "Inverse of A:"
print(inv_A)
[,1] [,2]
[1,] -2 1.5
[2,] 1 -0.5
Systems of Linear Equations
Solving Systems of Equations
# Example system: A * x = b
= np.array([[3, 2], [1, 2]])
A = np.array([5, 5])
b
# Solving for x
= np.linalg.solve(A, b)
x print("Solution of the system:")
Solution of the system:
print(x)
[2.96059473e-16 2.50000000e+00]
# Example system: A * x = b
<- matrix(c(3, 1, 2, 2), nrow = 2)
A <- c(5, 5)
b
# Solving for x
<- solve(A, b)
x print("Solution of the system:")
[1] "Solution of the system:"
print(x)
[1] 0.0 2.5
Eigenvalues and Eigenvectors
Calculating Eigenvalues and Eigenvectors
= np.linalg.eig(A)
eigenvalues, eigenvectors
print("Eigenvalues:")
Eigenvalues:
print(eigenvalues)
[4. 1.]
print("Eigenvectors:")
Eigenvectors:
print(eigenvectors)
[[ 0.89442719 -0.70710678]
[ 0.4472136 0.70710678]]
<- eigen(A)
eigen_decomp <- eigen_decomp$values
eigenvalues <- eigen_decomp$vectors
eigenvectors
print("Eigenvalues:")
[1] "Eigenvalues:"
print(eigenvalues)
[1] 4 1
print("Eigenvectors:")
[1] "Eigenvectors:"
print(eigenvectors)
[,1] [,2]
[1,] 0.8944272 -0.7071068
[2,] 0.4472136 0.7071068
Singular Value Decomposition (SVD)
Understanding SVD
= np.linalg.svd(A)
U, S, Vt print("U:\n", U)
U:
[[-0.86491009 -0.50192682]
[-0.50192682 0.86491009]]
print("S (Singular values):\n", S)
S (Singular values):
[4.13064859 0.96837093]
print("Vt:\n", Vt)
Vt:
[[-0.74967818 -0.66180256]
[-0.66180256 0.74967818]]
<- svd(A)
svd_decomp <- svd_decomp$u
U <- svd_decomp$d
S <- svd_decomp$v
Vt
print("U:")
[1] "U:"
print(U)
[,1] [,2]
[1,] -0.8649101 -0.5019268
[2,] -0.5019268 0.8649101
print("Singular values (S):")
[1] "Singular values (S):"
print(S)
[1] 4.1306486 0.9683709
print("Vt:")
[1] "Vt:"
print(Vt)
[,1] [,2]
[1,] -0.7496782 -0.6618026
[2,] -0.6618026 0.7496782
Principal Component Analysis (PCA)
Applying PCA for Dimensionality Reduction
from sklearn.decomposition import PCA
# Example data: 3-dimensional points
= np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
X
# Apply PCA
= PCA(n_components=2)
pca = pca.fit_transform(X)
X_reduced print("Reduced Data:\n", X_reduced)
Reduced Data:
[[-5.19615242e+00 5.55006974e-17]
[ 0.00000000e+00 -0.00000000e+00]
[ 5.19615242e+00 5.55006974e-17]]
# Example data: 3-dimensional points
<- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), nrow = 3, byrow = TRUE)
X
# Apply PCA
<- prcomp(X, center = TRUE, scale. = TRUE)
pca <- pca$x[, 1:2] # Reduced to 2 dimensions
X_reduced
print("Reduced Data:")
[1] "Reduced Data:"
print(X_reduced)
PC1 PC2
[1,] -1.732051 0
[2,] 0.000000 0
[3,] 1.732051 0
Linear Transformations and Projections
Visualizing Linear Transformations
# Define a transformation matrix
= np.array([[2, 0], [0, 0.5]])
T
# Define points to transform
= np.array([[1, 2, 3], [1, 2, 3]])
points
# Apply transformation
= np.dot(T, points)
transformed_points
# Plot original and transformed points
0], points[1], label='Original')
plt.scatter(points[0], transformed_points[1], label='Transformed')
plt.scatter(transformed_points[
plt.legend()"Linear Transformation")
plt.title("x")
plt.xlabel("y")
plt.ylabel( plt.show()
# Define a transformation matrix
<- matrix(c(2, 0, 0, 0.5), nrow = 2)
T
# Define points to transform
<- matrix(c(1, 1, 2, 2, 3, 3), nrow = 2)
points
# Apply transformation
<- T %*% points
transformed_points
# Plot original and transformed points
<- data.frame(x = c(points[1, ], transformed_points[1, ]),
df y = c(points[2, ], transformed_points[2, ]),
type = rep(c("Original", "Transformed"), each = 3))
ggplot(df, aes(x = x, y = y, color = type)) +
geom_point(size = 3) +
labs(title = "Linear Transformation", x = "x", y = "y") +
theme_minimal()
Applications of Linear Algebra
Machine Learning
Linear Regression
from sklearn.linear_model import LinearRegression
# Sample data
= np.array([[1], [2], [3], [4], [5]])
X = np.array([1.1, 2.0, 2.9, 4.1, 5.0])
y
# Train model
= LinearRegression()
model model.fit(X, y)
LinearRegression()In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LinearRegression()
# Predictions
= model.predict(X)
predictions
print("Predictions:", predictions)
Predictions: [1.04 2.03 3.02 4.01 5. ]
# Sample data
<- cbind(1, c(1, 2, 3, 4, 5)) # Adding intercept term
X <- c(1.1, 2.0, 2.9, 4.1, 5.0)
y
# Train linear regression model
<- lm(y ~ X - 1) # -1 to omit intercept term as it is included in X
model
# Predictions
<- predict(model, newdata = data.frame(X))
predictions
print("Predictions:")
[1] "Predictions:"
print(predictions)
1 2 3 4 5
1.04 2.03 3.02 4.01 5.00
Advanced Topics (Optional)
Matrix Factorizations
LU Decomposition
QR Decomposition
# LU Decomposition
= linalg.lu(A)
P, L, U print("P:\n", P)
P:
[[1. 0.]
[0. 1.]]
print("L:\n", L)
L:
[[1. 0. ]
[0.33333333 1. ]]
print("U:\n", U)
U:
[[3. 2. ]
[0. 1.33333333]]
# QR Decomposition
= np.linalg.qr(A)
Q, R print("Q:\n", Q)
Q:
[[-0.9486833 -0.31622777]
[-0.31622777 0.9486833 ]]
print("R:\n", R)
R:
[[-3.16227766 -2.52982213]
[ 0. 1.26491106]]
# LU Decomposition
<- lu(A)
lu_decomp <- lu_decomp$P
P <- lu_decomp$L
L <- lu_decomp$U
U
print("P:")
[1] "P:"
print(P)
NULL
print("L:")
[1] "L:"
print(L)
[,1] [,2]
[1,] 1.0000000 0
[2,] 0.3333333 1
print("U:")
[1] "U:"
print(U)
[,1] [,2]
[1,] 3 2.000000
[2,] 0 1.333333
# QR Decomposition
<- qr(A)
qr_decomp <- qr.Q(qr_decomp)
Q <- qr.R(qr_decomp)
R
print("Q:")
[1] "Q:"
print(Q)
[,1] [,2]
[1,] -0.9486833 -0.3162278
[2,] -0.3162278 0.9486833
print("R:")
[1] "R:"
print(R)
[,1] [,2]
[1,] -3.162278 -2.529822
[2,] 0.000000 1.264911
References
- Mathematics for Machine Learning, Marc Peter Deisenroth, A Aldo Faisal, and Cheng Soon Ong, Cambridge University Press, 2020.