Numpy & Scipy - 1.4 Basic Manipulation of Matrices (2)
- Numpy & Scipy - 1.1 Notation of Matrix and Vector, Matrix Input and Output
- Numpy & Scipy - 1.2 Convenient Functions of Matrix
- Numpy & Scipy - 1.3 Basic Manipulation of Matrices (1)
- Numpy & Scipy - 1.4 Basic Manipulation of Matrices (2)
- Numpy & Scipy - 1.5 Basic Manipulation of Matrices (3)
- Numpy & Scipy - 1.6 The Solution of Matrix Equation (General Matrices)
- Numpy & Scipy - 1.7 The Solution of Band Matrix
- Numpy & Scipy - 1.8 The Solution of Toeplitz Matrix and Circulant Matrix And How to Solve AX=B
- Numpy & Scipy - 1.9 Calculate Eigenvector and Eigenvalue of Matrix
hstack / vstack
np.hstack((tuple)),np.vstack((tuple)): Allows combining 2D arrays, 1D arrays, or a mix of both.- Both perform deep copies.
(1) Case of 2D array
2x3 2x2 hstack
1
2
3
4
5
6
7
8
9
10
a = np.array([[1,2,3], [4,5,6]], dtype=np.float64)
b = np.array([[-1,-2], [-3,-4]], dtype=np.float64)
new_mat = np.hstack( (a,b) ) # Note that input is a tuple
new_mat = np.hstack( (a,b,b,b) ) # Multiple combinations possible
prt(new_mat, fmt="%0.1f", delimiter=",")
print()
print(new_mat.shape)
1
2
3
4
5
6
# You can see the matrices are combined horizontally.
1.0, 2.0, 3.0,-1.0,-2.0
4.0, 5.0, 6.0,-3.0,-4.0
# 2x5 matrix
(2, 5)
- 3x3 2x2 hstack
1
2
3
4
5
6
7
8
a = np.array([[1,2,3],[4,5,6]], dtype=np.float64)
b = np.array([[-1,-2,-3],[-4,-5,-6],[-7,-8,-9]], dtype=np.float64)
new_mat = np.hstack( (a,b) ) # Note input is tuple
prt(new_mat, fmt="%0.1f", delimiter=",")
print()
print(new_mat.shape)
1
2
# 2x3 and 3x3 cannot be hstacked (2 rows vs 3 rows)
ValueError: all the input array dimensions except for the concatenation axis must match exactly, but along dimension 0, the array at index 0 has size 2 and the array at index 1 has size 3
- 2x3 2x2 vstack
1
2
3
4
5
6
7
8
a = np.array([[1,2,3], [4,5,6]], dtype=np.float64)
b = np.array([[-1,-2], [-3,-4]], dtype=np.float64)
new_mat = np.vstack( (a,b) ) # Note input is tuple
prt(new_mat, fmt="%0.1f", delimiter=",")
print()
print(new_mat.shape)
1
2
3
# 2x3 and 2x2 cannot be vstacked, causing the following error.
ValueError: all the input array dimensions except for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 3 and the array at index 1 has size 2
- 2x3 3x3 vstack
1
2
3
4
5
6
7
8
a = np.array([[1,2,3],[4,5,6]], dtype=np.float64)
b = np.array([[-1,-2,-3],[-4,-5,-6],[-7,-8,-9]], dtype=np.float64)
new_mat = np.vstack( (a,b) ) # Note input is tuple
prt(new_mat, fmt="%0.1f", delimiter=",")
print()
print(new_mat.shape)
1
2
3
4
5
6
7
1.0, 2.0, 3.0
4.0, 5.0, 6.0
-1.0,-2.0,-3.0
-4.0,-5.0,-6.0
-7.0,-8.0,-9.0
(5, 3)
(2) Case of 1D array
- hstack
- The 1D array vector becomes longer.
1
2
3
4
5
6
7
8
a = np.array([1,2,3], dtype=np.float64)
b = np.array([4,5,6], dtype=np.float64)
new_mat = np.hstack( (a,b) ) # Note input is tuple
prt(new_mat, fmt="%0.1f", delimiter=",")
print()
print(new_mat.shape)
1
2
3
1.0, 2.0, 3.0, 4.0, 5.0, 6.0
(6,)
- vstack
- A matrix is created.
1
2
3
4
5
6
7
8
a = np.array([1,2,3], dtype=np.float64)
b = np.array([4,5,6], dtype=np.float64)
new_mat = np.vstack( (a,b) ) # Note input is tuple
prt(new_mat, fmt="%0.1f", delimiter=",")
print()
print(new_mat.shape)
1
2
3
4
1.0, 2.0, 3.0
4.0, 5.0, 6.0
(2, 3)
(3) Mixed 2D array and 1D array
- hstack
- hstack will always error. Mixed types work only with vstack.
1
2
3
4
5
6
7
8
a = np.array([[1,2,3],[4,5,6]],dtype=np.float64)
b = np.array([7,8,9], dtype=np.float64)
new_mat = np.hstack( (a,b) ) # Note input is tuple
prt(new_mat, fmt="%0.1f", delimiter=",")
print()
print(new_mat.shape)
1
ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 2 dimension(s) and the array at index 1 has 1 dimension(s)
- vstack
- Combining 2x3 2D array and 1x3 1D array
1
2
3
4
5
6
7
8
a = np.array([[1,2,3],[4,5,6]],dtype=np.float64)
b = np.array([7,8,9], dtype=np.float64)
new_mat = np.vstack( (a,b) ) # Note input is tuple
prt(new_mat, fmt="%0.1f", delimiter=",")
print()
print(new_mat.shape)
1
2
3
4
5
1.0, 2.0, 3.0
4.0, 5.0, 6.0
7.0, 8.0, 9.0
(3, 3)
transpose method
- Literally transposes the matrix.
It is a shallow copy.
- (1) transpose property
1
2
3
4
5
6
7
c = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.float64)
d = c.T
prt(c, fmt="%0.1f", delimiter=",")
print()
prt(d, fmt="%0.1f", delimiter=",")
1
2
3
4
5
6
7
8
9
# c matrix
1.0, 2.0, 3.0
4.0, 5.0, 6.0
7.0, 8.0, 9.0
# d matrix (transpose of c)
1.0, 4.0, 7.0
2.0, 5.0, 8.0
3.0, 6.0, 9.0
- shallow copy example
(2) transpose method
If you transpose() a 1D array, it remains a 1D array.
1
2
3
4
5
6
7
8
9
10
11
e = np.array([1,2,3], dtype=np.float64)
f = e.transpose()
g = np.copy(e.transpose()) # deep copy is possible via np.copy
prt(e, fmt="%0.1f", delimiter=",")
print(e.shape)
print()
prt(f, fmt="%0.1f", delimiter=",")
print(f.shape)
1
2
3
4
5
1.0, 2.0, 3.0
(3,)
1.0, 2.0, 3.0
(3,)
real / imag / conjugate
real,imagreturn the real and imaginary parts of each matrix entry respectively.imagtakes the imaginary part and returns it as a real matrix or vector.Both are shallow copies.
conjugateis a deep copy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
a = np.array([[1-2j, 3+1j, 1], [1+2j, 2-1j, 7]], dtype=np.complex128)
a_real = r.real
# prt(a, fmt="%0.1f", delimiter=",")
# print()
# prt(a_real, fmt="%0.1f", delimiter=",")
a_imag = r.imag # Returns imaginary part as real numbers
# prt(a, fmt="%0.1f", delimiter=",")
# print()
# prt(a_imag, fmt="%0.1f", delimiter=",")
a_conj = r.conjugate() # conjugate multiplies imaginary part by -1
# deep copy
# prt(a, fmt="%0.1f", delimiter=",")
# print()
# prt(a_conj, fmt="%0.1f", delimiter=","
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# a matrix
( 1.0-2.0j),( 3.0+1.0j),( 1.0+0.0j)
( 1.0+2.0j),( 2.0-1.0j),( 7.0+0.0j)
# real
1.0, 3.0, 1.0
1.0, 2.0, 7.0
# imag
-2.0, 1.0, 0.0
2.0,-1.0, 0.0
# conjugate
( 1.0+2.0j),( 3.0-1.0j),( 1.0+0.0j)
( 1.0-2.0j),( 2.0+1.0j),( 7.0+0.0j)
Multiplication
- Scalar Multiplication
- Order does not matter: result = r * A = A * r
1
2
3
4
5
6
7
8
A = np.array([[1,2,1],[2,1,3],[1,3,1]], dtype=np.float64)
scalar = 5.0
result = scalar * A
prt(A, fmt="%0.1f", delimiter=",")
print()
prt(result, fmt="%0.1f", delimiter=",")
1
2
3
4
5
6
7
1.0, 2.0, 1.0
2.0, 1.0, 3.0
1.0, 3.0, 1.0
5.0, 10.0, 5.0
10.0, 5.0, 15.0
5.0, 15.0, 5.0
- Matrix Multiplication
Order is very important. Same as mathematical matrix multiplication.
- Can use
@operator ornp.matmul()function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
A = np.array([[1,2,3], [3,2,1]], dtype=np.float64)
B = np.array([[2,1],[1,2],[-3, 3]], dtype=np.float64)
result = A @ B
result2 = np.matmul(A, B)
prt(A, fmt="%0.1f", delimiter=",")
print()
prt(B, fmt="%0.1f", delimiter=",")
print()
prt(result, fmt="%0.1f", delimiter=",")
print()
prt(result2, fmt="%0.1f", delimiter=",")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.0, 2.0, 3.0
3.0, 2.0, 1.0
2.0, 1.0
1.0, 2.0
-3.0, 3.0
# A @ B
-5.0, 14.0
5.0, 10.0
# np.matmul(A,B)
-5.0, 14.0
5.0, 10.0
- Matrix-Vector Multiplication
- Similarly, calculated using
result = A @ uorresult = np.matmul(A,u). - NOT dot product!
1
2
3
4
5
6
7
8
9
10
A = np.array([[1,2,1],[2,1,3],[1,3,1]], dtype=np.float64)
u = np.array([5,1,3], dtype=np.float64)
Au = np.matmul(A, u)
prt(A, fmt="%0.1f", delimiter=",")
print()
prt(u, fmt="%0.1f", delimiter=",")
print()
prt(Au, fmt="%0.1f", delimiter=",")
1
2
3
4
5
6
7
1.0, 2.0, 1.0
2.0, 1.0, 3.0
1.0, 3.0, 1.0
5.0, 1.0, 3.0
10.0, 20.0, 11.0
Inner Product : vdot
result = np.vdot(u, v)Here u, v are vectors.
- Inner Product is expressed as a linear combination:
real vector : $\quad \mathbf{u} \cdot \mathbf{v} = u_1v_1 + u_2v_2 + \dots + u_nv_n$
complex vector : $ \quad \mathbf{u} \cdot \mathbf{v} = u_1\bar{v}_1 + u_2\bar{v}_2 + \dots + u_n\bar{v}_n = \bar{u_1v_1} + \bar{u_2v_2} + \dots + \bar{u_nv_n}$
- Example..
1
2
3
4
5
6
7
8
9
10
u = np.array([1,1,1,1], dtype=np.float64)
v = np.array([-1,1,-1,1], dtype=np.float64)
vdot = np.vdot(u,v)
prt(u, fmt="%0.1f", delimiter=",")
print()
prt(v, fmt="%0.1f", delimiter=",")
print()
print(vdot)
1
2
3
4
5
1.0, 1.0, 1.0, 1.0
-1.0, 1.0,-1.0, 1.0
0.0
Norm
- The vector norm we learned in linear algebra is defined as \(\lVert \mathbf{x} Vert _2 = \left( \sum_{i=1}^n x_i^2 ight)^{1/2} \quad l_2 \mbox{-vector norm (Euclidean)}\), which is called the $l_2$ norm. There are many other vector norms.
vector norm
\[\lVert \mathbf{x} Vert _1 = \sum_{i=1}^n \lvert x_i vert \quad l_1 \mbox{-vector norm}\] \[\lVert \mathbf{x} Vert _2 = (\sum_{i=1}^n \lvert x_i^2 vert)^{1/2} \quad l_2 \mbox{-vector norm (Euclidean)}\] \[\lVert \mathbf{x} Vert _{\infty} = \max_{1 \le i \le n} \lvert x_i vert\]
- Similarly, matrix norms exist, like $l_1, l_2, l_{\infty}$ norms for vectors.
matrix norm
\[\lVert A Vert _1 = \max_{1 \le j \le n} \sum_{i=1}^m \lvert a_{ij} vert \quad l_1 \mbox{-matrix norm}\] \[\lVert A Vert _2 = \sigma _{\max} \le \left( \sum_{i=1}^m \sum_{j=1}^n \lvert a_{ij} vert ^2 ight)^{1/2} \quad l_2 \mbox{-matrix norm (spectral)}\] \[\lVert A Vert _{\infty} = \max_{1 \le i \le m} \sum_{j=1}^n \lvert a_{ij} vert \quad l_{\infty} \mbox{-matrix norm}\]
- To calculate using
linalg.normin numpy, you need the scipy library.
1
from scipy import linalg
- Example
1
2
3
4
5
6
7
w = np.array([1,1], dtype=np.float64)
norm = linalg.norm(w, 2)
prt(w, fmt="%0.1f", delimiter=",")
print()
print(norm)
1
2
3
1.0, 1.0
1.4142135623730951
- To find $l_{\infty}$ norm, pass
infas a parameter.
1
norm = linalg.norm(a, 2, inf)
slicing - Extracting part of a matrix or vector
It is a shallow copy.
Example
1
2
A = np.array([[1,2,3,4,5], [6,7,8,9,10], [11,12,13,14,15], [-1,-2,-3,-4,-5]])
sub_A = A[1:3, 1:4]
- row: 1~2, column: 1~3.
- i.e., up to index -1 of the number after
:. 1:3actually gets up to1:2.
1
2
3
4
5
sub_A = A[0:5, 2:6]
# Same as below
sub_A = A[ : , 2: ]
:means from start to end.2:means from 2 to the end.
- If slicing operator
:is not attached to row or column side, it returns a 1D array. - Can convert to 2D array via
np.reshape.
1
2
3
4
5
6
7
sub_A1 = A[1:2, 1:4] # Returns as 2D array
sub_A2 = A[1, 1:4] # Returns as 1D array
sub_A3 = A[1: , 1:2] # Returns as 2D array
sub_A4 = A[1:, 1] # Returns as 1D array
sub_A5 = np.reshape(sub_A3, (3,1)) # 1D array -> 2D array
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#sub_A1
7.0, 8.0, 9.0
(1, 3)
#sub_A2
7.0, 8.0, 9.0
(3,)
#sub_A3
7.0
12.0
-2.0
(3, 1)
#sub_A4
7.0, 12.0,-2.0
(3,)
#sub_A5
7.0
12.0
-2.0
(3, 1)
Example 1.
$A = \begin{bmatrix} 1 & 2 \ 3 & 4 \end{bmatrix} \quad \mathbf{x} = \begin{bmatrix} 5 \ 6 \end{bmatrix} $
Store $A, \mathbf{x}$ in variables as 2D and 1D arrays respectively. Given that 1D array cannot be transposed, calculate the quadratic form $\mathbf{x}^TA\mathbf{x}$.
1) Try converting to 2D array (np.reshape)
2) Try using np.vdot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
from print_lecture import print_custom as prt
from scipy import linalg
A = np.array([[1,2],[3,4]], dtype=np.float64)
x = np.array([5,6], dtype=np.float64)
# 1)
x_t = np.reshape(x, (2,1))
result1 = np.matmul(np.matmul(x_t.T, A), x)
print(result1)
# 2) np.vdot
result2 = np.vdot(x, A @ x)
print(result2)
1
2
3
4
5
6
7
# Result 1
319.0
# Result 2
319.0




