之前一直有个疑惑,为什么要使用numpy?更本质的说,为什么要使用矩阵?矩阵是什么?意味着什么?关于关于矩阵的问题,这里推荐一个系列视频,讲的非常非常好。至于为什么用numpy,从程序的角度来讲,我认为最大的好处就是减少代码量以及提高效率(恩,写此文的时候觉得这是一句废话,但确实困扰了我一阵……)

比如,在学习KNN算法时需要计算欧式距离,公式如下:

$$d=\sqrt{ {({A_0}-{B_0})^2}+{({A_1}-{B_1})^2}+{({A_2}-{B_2})^2}+{({A_3}-{B_3})^2}+… }$$

这里用2维为例,公式变成:

$$d=\sqrt{ {({A_0}-{B_0})^2}+{({A_1}-{B_1})^2} }$$

这货貌似是已知直角三角形直角边求斜边长啊?

假设有2个已知点(0,0)和(2,2),求点(1,1)到这2个点的距离。

如果不使用矩阵,计算欧式距离的代码为:

1
2
3
4
5
6
7
8
9
10
a = [(0,0),(2,2)]
b = (1,1)

result = []

for o in a:
tmp = 0
for i in range(len(b)):
tmp += (b[i] - o[i]) ** 2
result.append(tmp ** 0.5)

而使用矩阵呢?代码则变成:

1
2
3
4
5
from numpy import *

a = array([[0,0],[2,2]])
b = [1,1]
result = (((tile(b,(a.shape[0],1)) - a) ** 2).sum(axis=1)) ** 0.5

使用矩阵1行代码解决了一个原来复杂度为 \(O(n^2)\) 的算法,这对大量数据时候是非常有用的。

这里,继续上一篇文章来看numpy的常用函数。

引入numpy包:

1
import numpy as np

生成随机数组

1
2
3
arr = np.random.rand(4,4)
print(arr)
print(type(arr))
1
2
3
4
5
[[ 0.2315333   0.84604056  0.64167686  0.44570765]
[ 0.36795508 0.60307855 0.79436951 0.563711 ]
[ 0.58187498 0.79001508 0.80846423 0.30307415]
[ 0.11054066 0.0864696 0.28637939 0.8327235 ]]
<class 'numpy.ndarray'>

将数组转换为矩阵

1
2
3
arr_mat = np.mat(arr)
print(arr_mat)
print(type(arr_mat))
1
2
3
4
5
[[ 0.2315333   0.84604056  0.64167686  0.44570765]
[ 0.36795508 0.60307855 0.79436951 0.563711 ]
[ 0.58187498 0.79001508 0.80846423 0.30307415]
[ 0.11054066 0.0864696 0.28637939 0.8327235 ]]
<class 'numpy.matrixlib.defmatrix.matrix'>

逆矩阵

1
2
arr_mat_i = arr_mat.I
print(arr_mat_i)
1
2
3
4
[[-1.5270842  -4.08677263  4.54386593  1.93012868]
[ 2.5195223 -3.03511915 0.84074721 0.4000739 ]
[-1.5393072 6.41241092 -2.98039392 -2.4322422 ]
[ 0.47046671 -1.34760492 0.33449566 1.73958357]]

矩阵乘法

1
arr_mat * arr_mat_i
1
2
3
4
5
6
7
8
matrix([[  1.00000000e+00,  -5.65692771e-17,   5.05172371e-17,
-1.18514985e-16],
[ -1.14978095e-16, 1.00000000e+00, 1.08559905e-17,
-1.74632936e-16],
[ 1.15042834e-16, 1.03538359e-16, 1.00000000e+00,
-2.49909228e-16],
[ 1.10902216e-16, -6.74225888e-17, 4.88343657e-17,
1.00000000e+00]])

矩阵和逆矩阵相乘结果应该为单位矩阵(对角线为1,其他为0),但这里产生了一些极小的误差。

eye函数

eye()函数可以用来生成单位矩阵

1
np.eye(4)
1
2
3
4
array([[ 1.,  0.,  0.,  0.],
[ 0., 1., 0., 0.],
[ 0., 0., 1., 0.],
[ 0., 0., 0., 1.]])

shape属性

返回矩阵的形状

1
arr.shape
1
(4, 4)

第一个是行,第二个是列

tile函数

将第一个参数重复第二个参数次,第二个参数可以为整形或者元祖。

1
2
range_arr = np.arange(0,40,10) # 可以参考range函数,从0开始,到40停止,步长10
print(range_arr)
1
[ 0 10 20 30]
1
np.tile(range_arr,3) # 将range_arr列重复3次,等同于np.tile(range_arr,(1,3))
1
array([ 0, 10, 20, 30,  0, 10, 20, 30,  0, 10, 20, 30])
1
np.tile(range_arr,(3,1)) # 将range_arr行重复3次,列1次
1
2
3
array([[ 0, 10, 20, 30],
[ 0, 10, 20, 30],
[ 0, 10, 20, 30]])
1
np.tile(range_arr,(0,3)) # 将range_arr压缩为0行12列,没什么意义
1
array([], shape=(0, 12), dtype=int64)
1
np.tile(range_arr,(3,0)) # 将range_arr压缩为3行0列,没什么意义
1
array([], shape=(3, 0), dtype=int64)
1
np.tile(range_arr,(2,3)) # 将range_arr行重复2次,列重复3次
1
2
array([[ 0, 10, 20, 30,  0, 10, 20, 30,  0, 10, 20, 30],
[ 0, 10, 20, 30, 0, 10, 20, 30, 0, 10, 20, 30]])

argsort函数

将数组从小到大排序,并返回排序后原来元素的索引

1
2
print(arr)
print(arr.argsort())
1
2
3
4
5
6
7
8
[[ 0.2315333   0.84604056  0.64167686  0.44570765]
[ 0.36795508 0.60307855 0.79436951 0.563711 ]
[ 0.58187498 0.79001508 0.80846423 0.30307415]
[ 0.11054066 0.0864696 0.28637939 0.8327235 ]]
[[0 3 2 1]
[0 3 1 2]
[3 0 1 2]
[1 0 2 3]]

以第一行为例,0.23最小,然后是0.44,0.64,0.84,所以结果是他们的索引[0,3,2,1]