专用的通用函数
除了以上介绍到的,
NumPy
还提供了很多通用函数,包括双曲三
角函数、比特位运算、比较运算符、弧度转化为角度的运算、取整
和求余运算,等等。浏览
NumPy
的文档将会揭示很多有趣的功
能。还有一个更加专用,也更加晦涩的通用函数优异来源是子模块
scipy.special
。如果你希望对你的数据进行一些更晦涩的数学计
算,
scipy.special
可能包含了你需要的计算函数。这些函数能
列一个长长的列表,下面的代码片段展示了一些可能在统计学中用
到的函数:
from scipy import special
# Gamma函数(广义阶乘,generalized factorials)和相关函数
x = [1, 5, 10]
print("gamma(x) =", special.gamma(x))
print("ln|gamma(x)| =", special.gammaln(x))
print("beta(x, 2) =", special.beta(x, 2))
gamma(x) = [1.0000e+00 2.4000e+01 3.6288e+05]
ln|gamma(x)| = [ 0. 3.17805383 12.80182748]
beta(x, 2) = [0.5 0.03333333 0.00909091]
# 误差函数(高斯积分)
# 它的实现和它的逆实现
x = np.array([0, 0.3, 0.7, 1.0])
print("erf(x) =", special.erf(x))
print("erfc(x) =", special.erfc(x))
print("erfinv(x) =", special.erfinv(x))
erf(x) = [0. 0.32862676 0.67780119 0.84270079]
erfc(x) = [1. 0.67137324 0.32219881 0.15729921]
erfinv(x) = [0. 0.27246271 0.73286908 inf]
NumPy
和
scipy.special
中提供了大量的通用函数,这些包的文
档在网上就可以查到,搜索
“gamma function python”
即可。
高级的通用函数特性
很多
NumPy
用户在没有完全了解通用函数的特性时就开始使用它们,
这里将介绍一些通用函数的特殊性质。
指定输出
在进行大量运算时,有时候指定一个用于存放运算结果的数组是非
常有用的。不同于创建临时数组,你可以用这个特性将计算结果直
接写入到你期望的存储位置。所有的通用函数都可以通过
out
参数
来指定计算结果的存放位置:
x = np.arange(5)
y = np.empty(5)
np.multiply(x, 10, out=y)
print(y)
[ 0. 10. 20. 30. 40.]
这个特性也可以被用作数组视图,例如可以将计算结果写入指定数
组的每隔一个元素的位置:
y = np.zeros(10)
np.power(2, x, out=y[::2])
print(y)
[ 1. 0. 2. 0. 4. 0. 8. 0. 16. 0.]
如果这里写的是
y[::2] = 2 ** x
,那么结果将是创建一个临时
数组,该数组存放的是
2 ** x
的结果,并且接下来会将这些值复
制到
y
数组中。对于上述例子中比较小的计算量来说,这两种方式
的差别并不大。但是对于较大的数组,通过慎重使用
out
参数将能
够有效节约内存。
聚合
二元通用函数有些非常有趣的聚合功能,这些聚合可以直接在对象
上计算。例如,如果我们希望用一个特定的运算
reduce
一个数
组,那么可以用任何通用函数的
reduce
方法。一个
reduce
方法
会对给定的元素和操作重复执行,直至得到单个的结果。
例如,对
add
通用函数调用
reduce
方法会返回数组中所有元素的
和:
x = np.arange(1, 6)
np.add.reduce(x)
15
同样,对 multiply 通用函数调用 reduce 方法会返回数组中所有 元素的乘积:
np.multiply.reduce(x)
120
如果需要存储每次计算的中间结果,可以使用
accumulate
:
np.add.accumulate(x)
array([ 1, 3, 6, 10, 15])
np.multiply.accumulate(x)
array([ 1, 2, 6, 24, 120])
请注意,在一些特殊情况中,
NumPy
提供了专用的函数
(
np.sum
、
np.prod
、
np.cumsum
、
np.cumprod
),它们也可以
实现以上
reduce
的功能,
外积
最后,任何通用函数都可以用
outer
方法获得两个不同输入数组
所有元素对的函数运算结果。这意味着你可以用一行代码实现一个
乘法表:
x = np.arange(1, 6)
np.multiply.outer(x, x)
array([[ 1, 2, 3, 4, 5],
[ 2, 4, 6, 8, 10],
[ 3, 6, 9, 12, 15],
[ 4, 8, 12, 16, 20],
[ 5, 10, 15, 20, 25]])
介绍非常有用的
ufunc.at
和
ufunc.reduceat
方法。
通用函数另外一个非常有用的特性是它能操作不同大小和形状的数
组,一组这样的操作被称为广播(
broadcasting
)。
通用函数:更多的信息
有关通用函数的更多信息(包括可用的通用函数的完整列表)可以在
NumPy
(
http://www.numpy.org
)和
SciPy
(
http://www.scipy.org
)文档
的网站找到。
聚合:最小值、最大值和其他值
当你面对大量的数据时,第一个步骤通常都是计算相关数据的概括统计
值。最常用的概括统计值可能是均值和标准差,这两个值能让你分别概
括出数据集中的
“
经典
”
值,但是其他一些形式的聚合也是非常有用的
(如求和、乘积、中位数、最小值和最大值、分位数,等等)。
NumPy
有非常快速的内置聚合函数可用于数组,我们将介绍其中的一
些。
数组值求和
先来看一个小例子,设想计算一个数组中所有元素的和。
Python
本身可
用内置的
sum
函数来实现:
import numpy as np
L = np.random.random(100)
sum(L)
49.21042168543532
它的语法和
NumPy
的
sum
函数非常相似,并且在这个简单的例子中的
结果也是一样的:
np.sum(L)
49.210421685435314
但是,因为
NumPy
的
sum
函数在编译码中执行操作,所以
NumPy
的
操作计算得更快一些:
big_array = np.random.rand(1000000)
%timeit sum(big_array)
%timeit np.sum(big_array)
239 ms ± 39.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
1.37 ms ± 118 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
但是需要注意,
sum
函数和
np.sum
函数并不等同,这有时会导致混
淆。尤其是它们各自的可选参数都有不同的含义,
np.sum
函数是知道
数组的维度的,这一点将在接下来的部分讲解。
最小值和最大值
同样,
Python
也有内置的
min
函数和
max
函数,分别被用于获取给定
数组的最小值和最大值:
min(big_array), max(big_array)
(2.067514368597756e-07, 0.9999992772471815)
NumPy
对应的函数也有类似的语法,并且也执行得更快:
np.min(big_array), np.max(big_array)
(2.067514368597756e-07, 0.9999992772471815)
%timeit min(big_array)
%timeit np.min(big_array)
168 ms ± 20.4 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
819 µs ± 43.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
对于
min
、
max
、
sum
和其他
NumPy
聚合,一种更简洁的语法形式是数
组对象直接调用这些方法:
print(big_array.min(), big_array.max(), big_array.sum())
2.067514368597756e-07 0.9999992772471815 499859.51691425196
当你操作
NumPy
数组时,确保你执行的是
NumPy
版本的聚合。
多维度聚合
一种常用的聚合操作是沿着一行或一列聚合。例如,假设你有一些
数据存储在二维数组中:
M = np.random.random((3, 4))
print(M)
[[0.93152842 0.36925427 0.42378446 0.0046071 ]
[0.05448783 0.98656956 0.25546815 0.79733112]
[0.56661317 0.30162416 0.24306571 0.82712545]]
默认情况下,每一个
NumPy
聚合函数将会返回对整个数组的聚合
结果:
M.sum()
5.761459397412782
聚合函数还有一个参数,用于指定沿着哪个轴的方向进行聚合。例
如,可以通过指定
axis=0
找到每一列的最小值:
M.min(axis=0)
array([0.05448783, 0.30162416, 0.24306571, 0.0046071 ])
这个函数返回四个值,对应四列数字的计算值。同样,也可以找到
每一行的最大值:
M.max(axis=1)
array([0.93152842, 0.98656956, 0.82712545])
其他语言的用户会对轴的指定方式比较困惑。
axis
关键字指定的
是数组将会被折叠的维度,而不是将要返回的维度。因此指定
axis=0
意味着第一个轴将要被折叠
——
对于二维数组,这意味着
每一列的值都将被聚合。
其他聚合函数
NumPy
提供了很多其他聚合函数,但是这里不会详细地介绍它
们。另外,大多数的聚合都有对
NaN
值的安全处理策略(
NaN
-
safe
),即计算时忽略所有的缺失值,这些缺失值即特殊的
IEEE
浮点型
NaN
值(关于缺失值更全面的介绍请参见
3.5
节)。有些
NaN
-safe
的函数直到
NumPy 1.8
版本才加进去,所以更早版本的
NumPy
并不支持此功能。
表
2-3
提供了一个
NumPy
中可用的聚合函数的清单。
表
2-3
:
NumPy
中可用的聚合函数
本书的其余部分将展示这些聚合函数的使用方法。