文章目录
- Defining lists and numpy arrays
- Algebraic operators on NumPy arrays vs. Python lists
- Matrix or Array of Arrays
- Scaling and translating matrices
- Transpose a matrix
- Get the norm of a nparray or matrix
- The dot product between arrays: All the flavors
- Sums by rows or columns
- Get the mean by rows or columns
- Center the columns of a matrix
理论课: C1W3.Vector Space Models
Numpy 是 Python 中最常用的数组操作库之一。它使我们只需几行代码就能对大型多维数组进行操作,不用再为矩阵操作而编写嵌套循环!
导入 numpy 库,并为其指定别名 np。这个是约定俗成的命名。
import numpy as np # The swiss knife of the data scientist.
Defining lists and numpy arrays
list 是 Python 的内置数据结构,可以包含不同类型的元素,如整数、浮点数、字符串等,甚至可以包含其他列表。
array 通常指的是 array 模块中的数组,它只能包含相同类型的元素,主要用于数值计算。
alist = [1, 2, 3, 4, 5] # Define a python list. It looks like an np array
narray = np.array([1, 2, 3, 4]) # Define a numpy array
print(alist)
print(narray)
print(type(alist))
print(type(narray))
结果:
[1, 2, 3, 4, 5]
[1 2 3 4]
<class ‘list’>
<class ‘numpy.ndarray’>
Algebraic operators on NumPy arrays vs. Python lists
初学者常见的错误之一就是混淆 NumPy 数组和 Python 列表的线性操作。例如:NumPy 数组中的 "+"操作符执行的是元素加法,而 Python 列表中的相同操作则是列表连接。
print(narray + narray)
print(alist + alist)
结果:
[2 4 6 8]
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
这里注意替换两种数据结构设计的用途。
对应乘法运算符也有不同效果,对于矩阵相当于向量的放大,而list则相当于拼接操作:
print(narray * 3)
print(alist * 3)
结果:
[ 3 6 9 12]
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
Matrix or Array of Arrays
在线性代数中,矩阵是由 n 行和 m 列组成的结构。这意味着每一行必须有相同数量的列。在 NumPy 中,我们有两种创建矩阵的方法:
- 使用
np.array
(推荐)创建数组数组。 - 使用
np.matrix
创建矩阵(仍然可用,但可能很快就会移除)。
NumPy 数组或列表可用于初始化矩阵,但生成的矩阵将仅由 NumPy 数组组成。
下面看三种初始化矩阵的方式:
npmatrix1 = np.array([narray, narray, narray]) # Matrix initialized with NumPy arrays
npmatrix2 = np.array([alist, alist, alist]) # Matrix initialized with lists
npmatrix3 = np.array([narray, [1, 1, 1, 1], narray]) # Matrix initialized with both types
print(npmatrix1)
print(npmatrix2)
print(npmatrix3)
[[1 2 3 4]
[1 2 3 4]
[1 2 3 4]]
[[1 2 3 4 5]
[1 2 3 4 5]
[1 2 3 4 5]]
[[1 2 3 4]
[1 1 1 1]
[1 2 3 4]]
Scaling and translating matrices
建立正确 NumPy 数组和矩阵后,再看看在 Python 中使用常规代数运算符(如 + 和 -)对它们进行操作有多简单。这些操作可以在数组与数组之间或数组与标量之间进行。
okmatrix = np.array([[1, 2], [3, 4]]) # Define a 2x2 matrix
print(okmatrix) # Print okmatrix
# Scale by 2 and translate 1 unit the matrix
result = okmatrix * 2 + 1 # For each element in the matrix, multiply by 2 and add 1
print(result)
结果:
[[1 2]
[3 4]]
[[3 5]
[7 9]]
# Add two compatible matrices
result1 = okmatrix + okmatrix
print(result1)
# Subtract two compatible matrices. This is called the difference vector
result2 = okmatrix - okmatrix
print(result2)
结果:
[[2 4]
[6 8]]
[[0 0]
[0 0]]
注意,星号代表的是元素相乘。请勿将其与点乘混淆。
result = okmatrix * okmatrix # Multiply each element by itself
print(result)
结果:
[[ 1 4]
[ 9 16]]
Transpose a matrix
在线性代数中,矩阵的转置是一种将矩阵在其对角线上翻转的运算,转置运算将矩阵的行和列索引互换,产生另一个矩阵。如果原始矩阵的维数是 n 乘 m,那么转置后的矩阵将是 m 乘 n。
通常用上标T 表示 NumPy 矩阵的转置操作。
matrix3x2 = np.array([[1, 2], [3, 4], [5, 6]]) # Define a 3x2 matrix
print('Original matrix 3 x 2')
print(matrix3x2)
print('Transposed matrix 2 x 3')
print(matrix3x2.T)
结果:
Original matrix 3 x 2
[[1 2]
[3 4]
[5 6]]
Transposed matrix 2 x 3
[[1 3 5]
[2 4 6]]
注意,转置不会影响一维数组。
nparray = np.array([1, 2, 3, 4]) # Define an array
print('Original array')
print(nparray)
print('Transposed array')
print(nparray.T)
结果:
Original array
[1 2 3 4]
Transposed array
[1 2 3 4]
上面定义的是一个一维数组,它的形状是 (4,),即有4个元素的数组。当你使用 .T 属性进行转置时,由于一维数组没有行和列的概念,转置操作实际上不会改变数组的形状,只是返回了数组本身。
nparray = np.array([[1, 2, 3, 4]]) # Define a 1 x 4 matrix. Note the 2 level of square brackets
print('Original array')
print(nparray)
print('Transposed array')
print(nparray.T)
结果:
Original array
[[1 2 3 4]]
Transposed array
[[1]
[2]
[3]
[4]]
上面定义的是一个二维数组,形状是 (1, 4),即有1行4列的矩阵。使用 .T 属性进行转置后,矩阵的形状变为 (4, 1),即4行1列的矩阵。
Get the norm of a nparray or matrix
在线性代数中,n 维向量
a
⃗
\vec a
a 的范数定义如下:
n
o
r
m
(
a
⃗
)
=
∣
∣
a
⃗
∣
∣
=
∑
i
=
1
n
a
i
2
norm(\vec a) = ||\vec a|| = \sqrt {\sum_{i=1}^{n} a_i ^ 2}
norm(a)=∣∣a∣∣=i=1∑nai2
计算向量甚至矩阵的范数是处理数据时的一种通用操作。Numpy 的linalg子包中有一组线性代数函数,其中包括norm函数。如果没有指定范数的类型,np.linalg.norm 计算的是 L2 范数,也就是欧几里得范数,它是数组元素平方和的平方根。
nparray1 = np.array([1, 2, 3, 4]) # Define an array
norm1 = np.linalg.norm(nparray1)
nparray2 = np.array([[1, 2], [3, 4]]) # Define a 2 x 2 matrix. Note the 2 level of square brackets
norm2 = np.linalg.norm(nparray2)
print(norm1)
print(norm2)
结果:
5.477225575051661
5.477225575051661
对于一维数组 nparray1 来说,np.linalg.norm(nparray1) 计算的是这个一维数组的 L2 范数。由于 nparray1 是一个一维数组,其 L2 范数实际上就是数组元素平方和的平方根。
对于二维数组 nparray2 来说,np.linalg.norm(nparray2) 默认计算的是矩阵的 L2 范数,这实际上是矩阵的最大奇异值。然而,如果二维数组是方阵,并且我们想要计算所有元素平方和的平方根,我们可以使用 axis 参数来指定沿着哪个轴计算范数。例如,np.linalg.norm(nparray2, axis=1) 会沿着每一列计算范数,而 np.linalg.norm(nparray2, axis=0) 会沿着每一行计算范数。
nparray2 = np.array([[1, 1], [2, 2], [3, 3]]) # Define a 3 x 2 matrix.
normByCols = np.linalg.norm(nparray2, axis=0) # Get the norm for each column. Returns 2 elements
normByRows = np.linalg.norm(nparray2, axis=1) # get the norm for each row. Returns 3 elements
print(normByCols)
print(normByRows)
结果:
[3.74165739 3.74165739]
[1.41421356 2.82842712 4.24264069]
The dot product between arrays: All the flavors
大小相同的两个向量
a
⃗
\vec a
a 和
b
⃗
\vec b
b 之间的点积、标量积或内积定义如下:
a
⃗
⋅
b
⃗
=
∑
i
=
1
n
a
i
b
i
\vec a \cdot \vec b = \sum_{i=1}^{n} a_i b_i
a⋅b=i=1∑naibi
点积吃两个向量,返回一个数字。
nparray1 = np.array([0, 1, 2, 3]) # Define an array
nparray2 = np.array([4, 5, 6, 7]) # Define an array
flavor1 = np.dot(nparray1, nparray2) # Recommended way
print(flavor1)
flavor2 = np.sum(nparray1 * nparray2) # Ok way
print(flavor2)
flavor3 = nparray1 @ nparray2 # Geeks way
print(flavor3)
# As you never should do: # Noobs way
flavor4 = 0
for a, b in zip(nparray1, nparray2):
flavor4 += a * b
print(flavor4)
结果:
38
38
38
38
这里强烈推荐第一种方法,因为np.dot可以吃矩阵也可以吃列表:
norm1 = np.dot(np.array([1, 2]), np.array([3, 4])) # Dot product on nparrays
norm2 = np.dot([1, 2], [3, 4]) # Dot product on python lists
print(norm1, '=', norm2 )
由范数定义可知,其是向量本身的点积结果再开放,因此可以写成:
n
o
r
m
(
a
⃗
)
=
∣
∣
a
⃗
∣
∣
=
∑
i
=
1
n
a
i
2
=
a
⋅
a
norm(\vec a) = ||\vec a|| = \sqrt {\sum_{i=1}^{n} a_i ^ 2} = \sqrt {a \cdot a}
norm(a)=∣∣a∣∣=i=1∑nai2=a⋅a
Sums by rows or columns
对矩阵进行的另一种常规操作是按行或列求和。
正如我们对函数 norm 所做的一样,axis 参数控制着操作的形式:
axis=0 表示每列元素相加。
axis=1 表示将每一行的元素相加。
nparray2 = np.array([[1, -1], [2, -2], [3, -3]]) # Define a 3 x 2 matrix.
sumByCols = np.sum(nparray2, axis=0) # Get the sum for each column. Returns 2 elements
sumByRows = np.sum(nparray2, axis=1) # get the sum for each row. Returns 3 elements
print('Sum by columns: ')
print(sumByCols)
print('Sum by rows:')
print(sumByRows)
结果:
Sum by columns:
[ 6 -6]
Sum by rows:
[0 0 0]
Get the mean by rows or columns
与求和一样,我们可以使用axis参数按行或列求取mean。均值是各元素之和除以向量长度的结果
m
e
a
n
(
a
⃗
)
=
∑
i
=
1
n
a
i
n
mean(\vec a) = \frac {{\sum_{i=1}^{n} a_i }}{n}
mean(a)=n∑i=1nai
nparray2 = np.array([[1, -1], [2, -2], [3, -3]]) # Define a 3 x 2 matrix. Chosen to be a matrix with 0 mean
mean = np.mean(nparray2) # Get the mean for the whole matrix
meanByCols = np.mean(nparray2, axis=0) # Get the mean for each column. Returns 2 elements
meanByRows = np.mean(nparray2, axis=1) # get the mean for each row. Returns 3 elements
print('Matrix mean: ')
print(mean)
print('Mean by columns: ')
print(meanByCols)
print('Mean by rows:')
print(meanByRows)
结果:
Matrix mean:
0.0
Mean by columns:
[ 2. -2.]
Mean by rows:
[0. 0. 0.]
Center the columns of a matrix
去中心化是矩阵另一个重要的预处理步骤。矩阵居中意味着去掉列内每个元素的列均值。去中心化后矩阵的列均值始终为 0。
nparray2 = np.array([[1, 1], [2, 2], [3, 3]]) # Define a 3 x 2 matrix.
nparrayCentered = nparray2 - np.mean(nparray2, axis=0) # Remove the mean for each column
print('Original matrix')
print(nparray2)
print('Centered by columns matrix')
print(nparrayCentered)
print('New mean by column')
print(nparrayCentered.mean(axis=0))
结果:
Original matrix
[[1 1]
[2 2]
[3 3]]
Centered by columns matrix
[[-1. -1.]
[ 0. 0.]
[ 1. 1.]]
New mean by column
[0. 0.]
注意:去中心化不适用于行,可考虑对矩阵进行转置,按列去中心化,然后将结果转置回去。例子:
nparray2 = np.array([[1, 3], [2, 4], [3, 5]]) # Define a 3 x 2 matrix.
nparrayCentered = nparray2.T - np.mean(nparray2, axis=1) # Remove the mean for each row
nparrayCentered = nparrayCentered.T # Transpose back the result
print('Original matrix')
print(nparray2)
print('Centered by rows matrix')
print(nparrayCentered)
print('New mean by rows')
print(nparrayCentered.mean(axis=1))
结果:
Original matrix
[[1 3]
[2 4]
[3 5]]
Centered by rows matrix
[[-1. 1.]
[-1. 1.]
[-1. 1.]]
New mean by rows
[0. 0. 0.]
可以使用静态或者动态方法,当然更推荐前者:
nparray2 = np.array([[1, 3], [2, 4], [3, 5]]) # Define a 3 x 2 matrix.
mean1 = np.mean(nparray2) # Static way.mean1
mean2 = nparray2.mean() # Dinamic way.mean2
print(mean1, ' == ', mean2)
结果:
3.0 == 3.0
在上面代码中,mean1 和 mean2 都是用来计算 nparray2 矩阵的平均值,但是它们使用的是 NumPy 数组的两种不同方法:
mean1 = np.mean(nparray2) 使用的是 NumPy 库中的 mean 函数。这是一种静态方法,意味着它是直接调用 NumPy 模块提供的一个函数。在这个函数调用中,没有指定 axis 参数,所以它计算的是整个数组的全局均值,即所有元素的总和除以元素的总数。
mean2 = nparray2.mean() 使用的是 NumPy 数组对象的 mean 方法。这是一种动态方法,意味着它是直接在数组对象上调用的一个方法。同样,由于没有指定 axis 参数,它也会计算整个数组的全局均值。