Python数据分析三大库一Numpy入门(2)-通用函数、数组运算处理

2、通用函数:快速的元素级数组函数

通用函数(即ufunc 是一种对ndarray中的数据执行元素级运算的函数。你可以将其看做简单函数(接受一个或多个标量值,并产生一个或多个标量值)的矢量化包装器。许多ufunc都是简单的元素级变体,如sqrt和exp(自然底数e的x次方):

In [137]: arr = np.arange(10)
    
In [138]: arr
Out[138]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    
In [139]: np.sqrt(arr)
Out[139]:
array([ 0. , 1. , 1.4142, 1.7321, 2. , 2.2361,2.4495,2.6458, 2.8284, 3. ])

In [140]: np.exp(arr)
Out[140]:
array([ 1. , 2.7183, 7.3891, 20.0855, 54.5982,148.4132, 403.4288, 1096.6332, 2980.958 , 8103.0839])

这些都是一元(unary)ufunc。另外一些(如add或maximum)接受2个数组(因此也叫二元(binary)ufunc),并返回一个结果数组:

In [141]: x = np.random.randn(8)
In [142]: y = np.random.randn(8)
    
In [143]: x
Out[143]:
array([-0.0119, 1.0048, 1.3272, -0.9193, -1.5491,0.0222, 0.7584,-0.6605])

In [144]: y
Out[144]:
array([ 0.8626, -0.01 , 0.05 , 0.6702, 0.853 ,-0.9559, -0.0235,-2.3042])

In [145]: np.maximum(x, y)
Out[145]:
array([ 0.8626, 1.0048, 1.3272, 0.6702, 0.853 ,0.0222, 0.7584,-0.6605])

这里,numpy.maximum计算了x和y中元素级别最大的元素。

虽然并不常见,但有些ufunc的确可以返回多个数组。

modf就是一个例子,它是Python内置函数divmod的矢量化版本,它会返回浮点数数组的小数和整数部分

In [146]: arr = np.random.randn(7) * 5
    
In [147]: arr
Out[147]: array([-3.2623, -6.0915, -6.663 , 5.3731,3.6182, 3.45 , 5.0077])
    
In [148]: remainder, whole_part = np.modf(arr)
    
In [149]: remainder
Out[149]: array([-0.2623, -0.0915, -0.663 , 0.3731,0.6182, 0.45 , 0.0077])
    
In [150]: whole_part
Out[150]: array([-3., -6., -6., 5., 3., 3., 5.])

Ufuncs(通用函数)可以接受一个out可选参数,这样就能在数组原地进行操作:

In [151]: arr
Out[151]: array([-3.2623, -6.0915, -6.663 , 5.3731,3.6182, 3.45 , 5.0077])
    
In [152]: np.sqrt(arr)
Out[152]: array([ nan, nan, nan, 2.318 , 1.9022,1.8574, 2.2378])
    
In [153]: np.sqrt(arr, arr)
Out[153]: array([ nan, nan, nan, 2.318 , 1.9022,1.8574, 2.2378])
    
In [154]: arr
Out[154]: array([ nan, nan, nan, 2.318 , 1.9022, 1.8574, 2.2378])

下标分别列出了一些一元和二元ufunc。

在这里插入图片描述
在这里插入图片描述

3、利用数组进行数据处理

3.1 两组值矢量化运算

NumPy数组使你可以将许多种数据处理任务表述为简洁的数组表达式(否则需要编写循环)。用数组表达式代替循环的做法,通常被称为矢量化。一般来说,矢量化数组运算要比等价的纯Python方式快上一两个数量级(甚至更多),尤其是各种数值计算。
作为简单的例子,假设我们想要在一组值(网格型)上计算函数 sqrt(x^2
+ y^2 ) 。np.meshgrid函数接受两个一维数组,并产生两个二维矩阵(对应于两个数组中所有的(x,y)对):

In [155]: points = np.arange(-5, 5, 0.01) # 1000 equally spaced points
    
In [156]: xs, ys = np.meshgrid(points, points)
    
In [157]: ys
Out[157]:
array([[-5. , -5. , -5. , ..., -5. , -5. , -5. ],
	   [-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
   	   [-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
                           ...,
   	   [ 4.97, 4.97, 4.97, ..., 4.97, 4.97, 4.97],
       [ 4.98, 4.98, 4.98, ..., 4.98, 4.98, 4.98],
       [ 4.99, 4.99, 4.99, ..., 4.99, 4.99, 4.99]])

现在,对该函数的求值运算就好办了,把这两个数组当做两个浮点数那样编写表达式即可:

In [158]: z = np.sqrt(xs ** 2 + ys ** 2)
    
In [159]: z
Out[159]:
array([[ 7.0711, 7.064 , 7.0569, ..., 7.0499, 7.0569, 7.064 ],
	   [ 7.064 , 7.0569, 7.0499, ..., 7.0428, 7.0499, 7.0569],
	   [ 7.0569, 7.0499, 7.0428, ..., 7.0357, 7.0428, 7.0499],
	   ...,
	   [ 7.0499, 7.0428, 7.0357, ..., 7.0286, 7.0357, 7.0428],
	   [ 7.0569, 7.0499, 7.0428, ..., 7.0357, 7.0428, 7.0499],
       [ 7.064 , 7.0569, 7.0499, ..., 7.0428, 7.0499, 7.0569]])
3.2 将条件逻辑表述为数组运算

numpy.where函数是三元表达式x if condition else y的矢量化版本。假设我们有一个布尔数组和两个值数组:

In [165]: xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
    
In [166]: yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
    
In [167]: cond = np.array([True, False, True, True, False])

假设我们想要根据cond中的值选取xarr和yarr的值:当cond中的值为True时,选取xarr的值,否则从yarr中选取。列表推导式的写法应该如下所示:

In [168]: result = [(x if c else y)
			: for x, y, c in zip(xarr, yarr, cond)]

In [169]: result
Out[169]: [1.1000000000000001, 2.2000000000000002,1.3, 1.3999999999999999, 2.5]

这有几个问题。

第一,它对大数组的处理速度不是很快(因为所有工作都是由纯Python完成的)。

第二,无法用于多维数组。若使用np.where,则可以将该功能写得非常简洁:

In [170]: result = np.where(cond, xarr, yarr)
    
In [171]: result
Out[171]: array([ 1.1, 2.2, 1.3, 1.4, 2.5])

np.where的第二个和第三个参数不必是数组,它们都可以是标量值。在数据分析工作中,where通常用于根据另一个数组而产生一个新的数组。假设有一个由随机数据组成的矩阵,你希望将所有正值替换为2,将所有负值替换为-2。若利用np.where,则会非常简单:

In [172]: arr = np.random.randn(4, 4)
    
In [173]: arr
Out[173]:
array([[-0.5031, -0.6223, -0.9212, -0.7262],
	   [ 0.2229, 0.0513, -1.1577, 0.8167],
	   [ 0.4336, 1.0107, 1.8249, -0.9975],
	   [ 0.8506, -0.1316, 0.9124, 0.1882]])

In [174]: arr > 0
Out[174]:
array([[False, False, False, False],
	   [ True, True, False, True],
	   [ True, True, True, False],
	   [ True, False, True, True]], dtype=bool)

In [175]: np.where(arr > 0, 2, -2)
Out[175]:
array([[-2, -2, -2, -2],
	   [ 2, 2, -2, 2],
	   [ 2, 2, 2, -2],
	   [ 2, -2, 2, 2]])

使用np.where,可以将标量和数组结合起来。例如,我可用常数2替换arr中所有正的值:

In [176]: np.where(arr > 0, 2, arr) # set only positive values to 2
Out[176]:
array([[-0.5031, -0.6223, -0.9212, -0.7262],
	   [ 2. , 2. , -1.1577, 2. ],
	   [ 2. , 2. , 2. , -0.9975],
	   [ 2. , -0.1316, 2. , 2. ]])

传递给where的数组大小可以不相等,甚至可以是标量值。

3.3 数学和统计方法

可以通过数组上的一组数学函数对整个数组或某个轴向的数据进行统计计算。sum、mean以及标准差std等聚合计算(aggregation,通常叫做约简(reduction))既可以当做数组的实例方法调用,也可以当做顶级NumPy函数使用。
这里,我生成了一些正态分布随机数据,然后做了聚类统计:

In [177]: arr = np.random.randn(5, 4)
    
In [178]: arr
Out[178]:
array([[ 2.1695, -0.1149, 2.0037, 0.0296],
       [ 0.7953, 0.1181, -0.7485, 0.585 ],
	   [ 0.1527, -1.5657, -0.5625, -0.0327],
	   [-0.929 , -0.4826, -0.0363, 1.0954],
	   [ 0.9809, -0.5895, 1.5817, -0.5287]])

In [179]: arr.mean()
Out[179]: 0.19607051119998253
    
In [180]: np.mean(arr)
Out[180]: 0.19607051119998253
    
In [181]: arr.sum()
Out[181]: 3.9214102239996507

mean和sum这类的函数可以接受一个axis选项参数,用于计算该轴向上的统计值,最终结果是一个少一维的数组:

In [182]: arr.mean(axis=1)
Out[182]: array([ 1.022 , 0.1875, -0.502 , -0.0881,0.3611])
    
In [183]: arr.sum(axis=0)
Out[183]: array([ 3.1693, -2.6345, 2.2381, 1.1486])

这里,arr.mean(1)是“计算的平均值”,arr.sum(0)是“计算每的和”。
其他如cumsum和cumprod之类的方法则不聚合,而是产生一个由中间结果组成的数组:

In [184]: arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])
    
In [185]: arr.cumsum()  #累加
Out[185]: array([ 0, 1, 3, 6, 10, 15, 21, 28])

在多维数组中,累加函数(如cumsum)返回的是同样大小的数组,但是会根据每个低维的切片沿着标记轴计算部分聚类:

In [186]: arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
    
In [187]: arr
Out[187]:
array([[0, 1, 2],
	   [3, 4, 5],
	   [6, 7, 8]])

In [188]: arr.cumsum(axis=0)
Out[188]:
array([[ 0, 1, 2],
	   [ 3, 5, 7],
	   [ 9, 12, 15]])

In [189]: arr.cumprod(axis=1)  #按行累乘
Out[189]:
array([[ 0, 0, 0],
	   [ 3, 12, 60],
	   [ 6, 42, 336]])

表4-5列出了全部的基本数组统计方法。后续章节中有很多例子都会用到这些方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mnKFFu7R-1648117294839)(C:\Users\LENOVO\AppData\Roaming\Typora\typora-user-images\image-20220324165037394.png)]

3.4 用于布尔型数组的方法

在上面这些方法中,布尔值会被强制转换为1(True)和0(False)。因此,sum经常被用来对布尔型数组中的True值计数:

In [190]: arr = np.random.randn(100)
    
In [191]: (arr > 0).sum() # Number of positive values
Out[191]: 42

另外还有两个方法any和all,它们对布尔型数组非常有用。any用于测试数组中是否存在一个或多个True,而all则检查数组中所有值是否都是True:

In [192]: bools = np.array([False, False, True,False])
    
In [193]: bools.any()
Out[193]: True
    
In [194]: bools.all()
Out[194]: False

这两个方法也能用于非布尔型数组,所有非0元素将会被当做True。

3.5 排序

跟Python内置的列表类型一样,NumPy数组也可以通过sort方法就地排序:

In [195]: arr = np.random.randn(6)
    
In [196]: arr
Out[196]: array([ 0.6095, -0.4938, 1.24 , -0.1357,1.43 , -0.8469])
    
In [197]: arr.sort()
In [198]: arr
Out[198]: array([-0.8469, -0.4938, -0.1357,0.6095,1.24 , 1.43 ])

多维数组可以在任何一个轴向上进行排序,只需将轴编号传给sort即可:

In [199]: arr = np.random.randn(5, 3)
    
In [200]: arr
Out[200]:
array([[ 0.6033, 1.2636, -0.2555],
	   [-0.4457, 0.4684, -0.9616],
	   [-1.8245, 0.6254, 1.0229],
	   [ 1.1074, 0.0909, -0.3501],
	   [ 0.218 , -0.8948, -1.7415]])

In [201]: arr.sort(1)
    
In [202]: arr
Out[202]:
array([[-0.2555, 0.6033, 1.2636],
	   [-0.9616, -0.4457, 0.4684],
	   [-1.8245, 0.6254, 1.0229],
	   [-0.3501, 0.0909, 1.1074],
	   [-1.7415, -0.8948, 0.218 ]])

顶级方法np.sort返回的是数组的已排序副本,而就地排序则会修改数组本身。计算数组分位数最简单的办法是对其进行排序,然后选取特定位置的值:

In [203]: large_arr = np.random.randn(1000)
    
In [204]: large_arr.sort()
    
In [205]: large_arr[int(0.05 * len(large_arr))] # 5% quantile
Out[205]: -1.5311513550102103
3.6 唯一化以及其它的集合逻辑

NumPy提供了一些针对一维ndarray的基本集合运算。最常用的可能要数np.unique了,它用于找出数组中的唯一值并返回已排序的结果

In [206]: names = np.array(['Bob', 'Joe', 'Will','Bob', 'Will', 'Joe', 'Joe'])
    
In [207]: np.unique(names)
Out[207]:
array(['Bob', 'Joe', 'Will'],dtype='<U4')

In [208]: ints = np.array([3, 3, 3, 2, 2, 1, 1, 4,4])
    
In [209]: np.unique(ints)
Out[209]: array([1, 2, 3, 4])

拿跟np.unique等价的纯Python代码来对比一下:

In [210]: sorted(set(names))
Out[210]: ['Bob', 'Joe', 'Will']

另一个函数np.in1d用于测试一个数组中的值在另一个数组中的成员资格,返回一个布尔型数组:

In [211]: values = np.array([6, 0, 0, 3, 2, 5, 6])
    
In [212]: np.in1d(values, [2, 3, 6])
Out[212]: array([ True, False, False, True,True,False, True], dtype=bool)

NumPy中的集合函数请参见表4-6。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值