大数据,机器学习,人工智能机器学习与数据挖掘机器学习与计算机视觉

机器学习系列(四)——Numpy中的矩阵运算与索引

2019-06-07  本文已影响0人  Ice_spring

效率对比

给定向量a=[0,1,2,3,...,9],让其中每一个元素乘2得到[0,2,4,6,...,18]。

n=100000
L=[i for i in range(L)]
2*L
out:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

可以发现python原生list不支持这种整体操作。当然可以像下面这样来达成目的:

A=[]
for e in L:
    A.append(2*e)
A
out:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

但是,这样做效率其实是很低的,低于生成表达式效率

#时间效率测试
%%time
A=[]
for e in L:
    A.append(2*e)
out:CPU times: user 167 ms, sys: 8 ms, total: 175 ms
    Wall time: 174 ms

%%time
A=[2*e for e in L]
out:CPU times: user 66.3 ms, sys: 20.2 ms, total: 86.5 ms
    Wall time: 85.3 ms

如果使用numpy.array方式,效率将更强:

%%time
A=np.array(2*e for e in L)
out:CPU times: user 4.28 ms, sys: 7.84 ms, total: 12.1 ms
    Wall time: 12 ms

可以看到速度大大提高。而且numpy支持直接的整体操作速度又要更快:

L=np.array([1,2,3,4])
2*L
out:array([2, 4, 6, 8])

numpy将数组看成向量或矩阵,而且加入了非常好的优化,运行速度非常非常快,和python原生list不在一个数量级。

Universasl Function

x=np.arange(6).reshape((2,3))
x
array([[0, 1, 2],
       [3, 4, 5]])
××××××××××××××××××××××××××××××××××××
x*2#运算是对其中每个元素的操作
array([[ 0,  2,  4],
       [ 6,  8, 10]])
x+1#每个元素加1
array([[1, 2, 3],
       [4, 5, 6]])
x/2#每个元素除以2
array([[0. , 0.5, 1. ],
       [1.5, 2. , 2.5]])
x//2#除以2取整
array([[0, 0, 1],
       [1, 2, 2]])
x**2#二次方
array([[ 0,  1,  4],
       [ 9, 16, 25]])
x%2#每个元素取余
array([[0, 1, 0],
       [1, 0, 1]])
1/x#取倒数
np.cos(x)
np.arctan(x)
np.exp(x)
np.power(3,x)#等价于3**x
np.log(x+0.001)#默认以e为底,log2,log10

以上是对一个矩阵的操作,numpy还支持矩阵与矩阵之间的运算:

A=np.arange(4).reshape(2,2)
B=np.full((2,2),3)
A*B#相乘是对应元素相乘,类似有相加是对应元素相加
out:array([[0, 3],
       [6, 9]])

如果要进行矩阵乘法,用dot方法:

A.dot(B)
out:array([[ 3,  3],
       [15, 15]])
v.dot(A)
array([4, 7])

在向量与矩阵做乘法时,系统会自动判断向量处理为列向量还是行向量,A.dot(v)一样能运行。
A的转置:A.T
A的逆矩阵:np.linalg.inv(A)
非方阵可以求广义逆矩阵:np.linalg.pinv(B)

矩阵与向量运算

向量与矩阵相加默认是向量与矩阵每一行相加,相乘是与每行做乘法。

A
out:array([[0, 1],
       [2, 3]])
v=np.array([1,2])
v+A
out:array([[1, 3],
       [3, 5]])
v*A
out:array([[0, 2],
       [2, 6]])

上述方式往往不太直观,可以用vstack方法对v处理后再相加,效果一样:

v1=np.vstack([v]*A.shape[0])
out:array([[1, 2],
       [1, 2]])
v1+A
out:array([[1, 3],
       [3, 5]])

对于堆叠,有一个tile方法可以更方便进行,如对v在行上堆叠两次,列上堆叠一次:

np.tile(v,(2,1))
out:array([[1, 2],
       [1, 2]])

聚合运算

聚合运算是一组值处理为一个值的过程,如求和。

L=np.random.random(10)
sum(L)#python原生方法
out:5.379448046210108
np.sum(L)#numpy方法
out:5.379448046210108

两种求和差别在效率,np.sum效率远远超高sum。可用%time检测。
相应有:

np.max(L)#这种方式更全,建议使用
np.min(L)
#也可以这样调用
L.max()
L.min()
L.sum()

如果是二维矩阵,返回的仍然是一个值,如果要求每行的和或者每行的最大值,则需要指定axis:

A=np.arange(9).reshape(3,3)
out:array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
np.sum(A)
out:36
np.sum(A,axis=0)#axis=0,按行运算即沿着行进行运算,求得的是列和
out:array([ 9, 12, 15])
np.sum(A,axis=1)
out:array([ 3, 12, 21])#axis=1,按列运算即沿着列进行运算,求得的是对应的行和

下面这些聚合运算同样可以指定axis
返回矩阵中所有元素乘积:np.prod(A)
返回均值:np.mean(A)
返回中位数:np.median(A)
返回方差:np.var(A)
返回标准差:np.std(A)
返回百分位分位点:np.percentile(L,q=50)50%分位数

for p in [25,50,75]:
    print(np.percentile(A,q=p))
25%分位点:2.0
50%:4.0
75%:6.0

索引——arg运算

我们可以轻易求得向量中的最小值,但有时我们希望得到这个最小值的索引。

x=np.array([2,1,7,4,6,5,9,12,3])
np.max(x)
out:12
np.argmax(x)
out:7

排序中使用索引

x=np.arange(16)
np.random.shuffle(x)#对x乱序排列,会改变x本身
x
out:array([10, 13,  0, 14, 12, 15,  8,  9,  1,  7,  2,  5, 11,  6,  4,  3])
np.sort(x)#不改变x本身
out:array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])

如果希望直接改变x本身,则x.sort()即可。
如果对二维矩阵进行排序,默认是对每一行排序,默认axis为1

m=np.random.randint(10,size=(4,4))
m
out:array([[2, 2, 1, 2],
       [7, 6, 0, 5],
       [5, 3, 9, 0],
       [3, 7, 1, 0]])
np.sort(m)
out:array([[1, 2, 2, 2],
       [0, 5, 6, 7],
       [0, 3, 5, 9],
       [0, 1, 3, 7]])
np.sort(m,axis=0)
out:array([[2, 2, 0, 0],
       [3, 3, 1, 0],
       [5, 6, 1, 2],
       [7, 7, 9, 5]])

接着讨论对于向量x,排序后返回排序索引:

np.argsort(x)
out:array([ 2,  8, 10, 15, 14, 11, 13,  9,  6,  7,  0, 12,  4,  1,  3,  5])

·快速排序中有个partition,小于标定点的放左边,大于的放右边,也有np.argpartition方法求索引,只是用的比较少。

np.partition(x,9)
out:array([ 7,  3,  0,  4,  6,  5,  8,  2,  1,  9, 13, 15, 11, 12, 14, 10])

Fancy Indexing

指定索引下标后开始索引

x=np.arange(16)
out:array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])
[x[3],x[5],x[8]]
out:[3, 5, 8]
ind=[3,5,8]
x[ind]
out:array([3, 5, 8])

如果索引指定为二维,则会返回按索引排列的二维矩阵:

ind=np.array([[0,2],[1,3]])
x[ind]
out:array([[0, 2],
       [1, 3]])

同样在二维矩阵中也可以有一些神奇的索引:

m=x.reshape(4,-1)#不改变原数组
m
out:array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

现在对一些数据感兴趣,但是他们没有规律:

row=np.array([0,1,2])#(0,1)(1,2)(2,3)
col=np.array([1,2,3])
m[row,col]
out:array([ 1,  6, 11])
m[0,col]#第0行的列
out:array([1, 2, 3])

col=[True,False,True,True]#对0,2,3行感兴趣
m[1:3,col]#对中间两列的col感兴趣
out:array([[ 4,  6,  7],
       [ 8, 10, 11]])

numpy.array比较得bool值

x
out:array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])
x<3
out:array([ True,  True,  True, False, False, False, False, False, False,
       False, False, False, False, False, False, False])
x==3
out:array([False, False, False,  True, False, False, False, False, 
        False,False, False, False, False, False, False, False])
x!=3
out:array([ True,  True,  True, False,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True])

可以结合加减乘除获得更复杂表达式,也可以在二维矩阵使用:

2*x==24-4*x
out:array([False, False, False, False,  True, False, False, False,
        False,False, False, False, False, False, False, False])

假如x是年龄样本,求问有多少个年龄小于3的孩子:

np.sum(x<=3)#这种方式将true当作1,false当作0
out:4

数非零的元素个数:np.count_nonzero(x<=3)将true当作1,false当作0
查看是否有为0的元素:np.any(x==0)
查看是否有小于0的元素:np.any(x<0)
相应有:np.all(x>=0)必须全部>=0才会返回True
所有以上方法对二维矩阵也适用。

np.sum(m%2==0) #求m中偶数的个数,默认全局,可以指定axis求每行或每列
out:8
np.sum(m%2==0,axis=0)#按行,即看每列有多少偶数
out:array([4, 0, 4, 0])
np.all(m>0,axis=1)
out:array([False,  True,  True,  True])#第0行不满足每个元素都大于0

对以上比较也可以进行组合,如求x中>3,<10的个数:

np.sum((x>3)&(x<10))#这里必须使用位运算符
out:6

求年龄为偶数或者大于10的个数

np.sum((x%2==0)|(x>10))
out:11

求不等于0的

np.sum(~(x==0))#等价于np.sum(x!=0)
out:15

下面看索引的用法,返回x中小于5的数组成的子数组:

x[x<5]
out:array([0, 1, 2, 3, 4])

返回年龄是偶数的:

x[x%2==0]
out:array([ 0,  2,  4,  6,  8, 10, 12, 14])

返回矩阵m中最后一个元素能被3整除的行:

m[m[:,3]%3==0,:]
out:array([[ 0,  1,  2,  3],
       [12, 13, 14, 15]])

这些方法在实际处理数据时有非常大的用处,比如boston房价数剧集,取出室内面积大于50的行,去除距离中心小于100的行等。
另外有一个pandas库可对数据进行预处理。以后有机会会更新在笔者简书中。

上一篇下一篇

猜你喜欢

热点阅读