本篇博客所有示例使用Jupyter NoteBook演示。
Python数据分析系列笔记基于:利用Python进行数据分析(第2版)
目录
1.相同大小数组间的运算
数组很重要,因为它使你不用编写循环即可对数据执行批量运算,我们把它称为矢量化。
大小相等的数组间的任何算术运算都会将运算应用到元素级:
import numpy as np
arr = np.array([[1,2,3],[4,5,6]])
print(arr)
#同等大小数组间的乘法 对应位置元素相乘 以下几种形式等价 numpy重载了python的运算符
print(arr*arr)
print(np.multiply(arr,arr))
print("----------------------")
#同等大小数组间的减法
print(arr-arr)
数组与标量的运算相当于数组中的每个元素都与该标量进行运算:
print(arr)
#对数组的每个元素取倒数
print(1/arr)
print(1./arr)
print("-----------------")
#对数组的每个元素取2次幂
print(arr**2)
大小相同的数组之间进行比较会产生布尔值数组:
arr1 = np.array([[1.2,2.4,5.],[2,4,1]])
print(arr)
print(arr1)
print(arr < arr1) #NumPy数组重载了Python的比较运算符 对应位置的元素进行比较
2.不同大小数组间的运算(广播)
广播指的是不同形状的数组之间的运算执行方式。它是一个非常强大的功能,但也容易令人误解。
- 简单的广播
数组和标量值进行运算时会发生最简单的广播:
arr = np.arange(5)
print(arr)
print(arr*4) #对数组的每个元素乘以4
对数组的每一列进行0均值化,即把每列的元素减去该列对应的均值:
arr = np.random.randn(4,3)
print(arr)
print(arr.mean(0)) #0代表对每列求均值
arr1 = arr - arr.mean(0)
print(arr1)
print(arr1.mean(0))
下图形象的展示了上述过程:
- 广播的规则
如果两个数组的后缘维度(从末尾开始算起的维度)的轴长度相同或者其中一方的长度为1,则他们是广播兼容的。广播会在缺失或长度为1的维度上进行。
如上述对4*3的2维数组的每一列元素减去该列对应的均值(长度为3的数组),(4,3)-(3,) 两个数组的后缘维度都是3,那么可以广播,在缺失的维度上进行。
对数组的每一行进行0均值化,即把每行元素减去该行对应的均值:
print(arr)
row_mean = arr.mean(1) #1代表对每行求均值
print(row_mean)
print(row_mean.shape) #row_mean是一个长度为3的一维数组
print(row_mean.reshape((4,1))) #把row_mean重新塑形为 4*1的二维数组
arr1 = arr - row_mean.reshape((4,1)) #对每一行的元素进行0均值化
print(arr1.mean(1))
下图说明了该运算的过程:
对每行的元素进行0均值化时,数组为(4,3),每行的均值是一个长度为4的一维数组(4,) 两个数组的后缘维度轴长度不相同且没有一个数组的后缘维度的轴长度为1,所以不能进行广播,此时需要把该一维数组重新塑形为(4,1)的二维数组,满足其中一个数组的后缘维度的轴长度为1,在轴长度为1的维度上进行广播。
对于高维数组也是一样的,下例是在一个三维数组上沿0轴加一个2维数组:
(3,4,2)和(4,2) 两个数组的后缘维度相同,可以进行广播,在缺失的维度上进行。
- 沿其他轴向广播
高维数组的广播似乎难以理解,而实际上他也遵守广播原则。
如果数组广播不兼容,就会报下面的错误,如对之前的2维数组的每行进行0均值化,如果不对一维数组进行塑形的话:
print(arr - arr.mean(1))
我们经常需要通过算术运算将较低维度/较小的数组在除0轴以外的其他轴上广播,此时根据广播的规则较小数组的"广播维"必须是1. 在0轴上进行广播的话,可以不用是1,因为在0轴广播一般满足两个数组的后缘维度轴长度相同这个条件。
如上述的这个对数组的每一行进行0均值化的例子,在1轴上进行广播,此时行平均值的形状是(4,1)而不是(4,)
print(arr - arr.mean(1).reshape((4,1)))
对于三维数组的情况,在三维数组的任何一维上广播其实也就是将数据重塑维兼容的形状而已。下图说明了在三维数组各个维度上广播的形状要求:
为广播添加一个长度为1的新轴:reshape是一个办法,但是插入新轴需要构造一个表示新形状的元组,比较麻烦;NumPy提供了一个通过索引机制插入新轴的语法,通过np.newaxis和全切片来插入新轴:
#使用np.newaxis 和全切片: 添加一个长度为1的新轴
arr = np.zeros((4,4))
arr_3d = arr[:,np.newaxis,:]
print(arr_3d.shape)
print("-------------------------")
arr_1d = np.random.normal(size=5)
print(arr_1d[:,np.newaxis])
print(arr_1d[np.newaxis,:])
如果有一个三维数组,希望对轴2进行0均值化,可以这样做:
arr = np.random.randn(3,4,5)
depth_mean = arr.mean(2)
print(depth_mean.shape)
demeaned = arr - depth_mean[:,:,np.newaxis]
print(demeaned.mean(2))
- 通过广播设置数组的值
算术运算所遵循的广播原则同样适用于通过索引机制设置数组值的操作:
arr = np.zeros((4,3))
arr[:] = 5 #把数组中所有值设置为5
print(arr)
通过一个一维数组来设置目标数组的各个列,保证形状兼容即可:
col = np.array([1.2,3.4,3.3,4.])
arr[:] = col[:,np.newaxis]
print(arr)
print("------------------------")
arr[:2] = [[100],[200]] #2*3 2*1
print(arr)