NumPy数组的计算:通用函数缓慢的循环通用函数介绍探索Numpy的通用函数高级通用函数的特性聚合:最小值、 最大值和其他值数组值求和最大值和最小值其他聚合函数
《Python数据科学手册》读书笔记
NumPy 数组的计算有时非常快, 有时也非常慢。使 NumPy 变快的关键是利用向量化操作, 通常在 NumPy 的通用函数(ufunc) 中实现。
python的缓慢通常出现在许多小操作与要不断重复的时候,比如对数组每个元素做循环
# 计算数组每个元素的倒数
import numpy as np
np.random.seed()
def compute_reciprocals(values):
output = np.empty(len(values))
for i in range(len(values)):
output[i] = 1.0 / values[i]
return output
values = np.random.randint(, , size=)
compute_reciprocals(values)
array([0.16666667, 1. , 0.25 , 0.25 , 0.125 ])
big_array = np.random.randint(, , size=)
# 循环一百万次所要的时间
%timeit compute_reciprocals(big_array)
5.73 s ± ms per loop (mean ± std. dev. of runs, loop each)
Numpy 为很多类型的操作提供了非常方便的,静态类型的可编译程序的接口,也被称作向量操作,是通过通用函数实现的,这样会取得更快的执行效率
# 输出结果相同
print(compute_reciprocals(values))
print(1.0 / values)
[0.16666667 1. 0.25 0.25 0.125 ]
[0.16666667 1. 0.25 0.25 0.125 ]
# 当数组很大时,通用函数的优势也就展现出来了
%timeit (1.0 / big_array)
12.1 ms ± 2.04 ms per loop (mean ± std. dev. of runs, loops each)
# 两个数组进行运算
np.arange() / np.arange(, )
array([0. , 0.5 , 0.66666667, 0.75 , 0.8 ])
# 多维数组进行运算
x = np.arange().reshape((, ))
** x
array([[ 1, 2, 4],
[ 8, 16, 32],
[ 64, 128, 256]], dtype=int32)
x = np.arange()
print("x =", x)
print("x + 5 =", x + )
print("x - 5 =", x - )
print("x * 2 =", x * )
print("x / 2 =", x / )
print("x // 2 =", x // ) # 地板除
print("-x = ", -x)
print("x ** 2 = ", x ** )
print("x % 2 = ", x % )
x = [0 1 2 3]
x + 5 = [5 6 7 8]
x - 5 = [-5 -4 -3 -2]
x * 2 = [0 2 4 6]
x / 2 = [0. 0.5 1. 1.5]
x // 2 = [0 0 1 1]
-x = [ 0 -1 -2 -3]
x ** 2 = [0 1 4 9]
x % 2 = [0 1 0 1]
Nmupy实现的算数运算符
Operator | Equivalent ufunc | Description |
---|---|---|
+ | np.add | Addition (e.g., 1 + 1 = 2) |
- | np.subtract | Subtraction (e.g., 3 - 2 = 1) |
- | np.negative | Unary negation (e.g., -2) |
* | np.multiply | Multiplication (e.g., 2 * 3 = 6) |
/ | np.divide | Division (e.g., 3 / 2 = 1.5) |
// | np.floor_divide | Floor division (e.g., 3 // 2 = 1) |
** | np.power | Exponentiation (e.g., 2 ** 3 = 8) |
% | np.mod | Modulus/remainder (e.g., 9 % 4 = 1 |
-(0.5*x + ) **
array([-1. , -2.25, -4. , -6.25])
np.add(x, )
array([, , , ])
x = np.array([-2, -1, , , ])
abs(x)
array([, , , , ])
np.absolute(x)#同np.abs
array([, , , , ])
x = np.array([ - 4j, - 3j, + 0j, + 1j])
np.abs(x)
array([5., 5., 2., 1.])
theta = np.linspace(, np.pi, )
print("theta = ", theta)
print("sin(theta) = ", np.sin(theta))
print("cos(theta) = ", np.cos(theta))
print("tan(theta) = ", np.tan(theta))
theta = [0. 1.57079633 3.14159265]
sin(theta) = [0.0000000e+00 1.0000000e+00 1.2246468e-16]
cos(theta) = [ 1.000000e+00 6.123234e-17 -1.000000e+00]
tan(theta) = [ 0.00000000e+00 1.63312394e+16 -1.22464680e-16]
x = [-1, , ]
print("x = ", x)
print("arcsin(x) = ", np.arcsin(x))
print("arccos(x) = ", np.arccos(x))
print("arctan(x) = ", np.arctan(x))
x = [-1, 0, 1]
arcsin(x) = [-1.57079633 0. 1.57079633]
arccos(x) = [3.14159265 1.57079633 0. ]
arctan(x) = [-0.78539816 0. 0.78539816]
x = [, , ]
print("x =", x)
print("e^x =", np.exp(x))
print("2^x =", np.exp2(x))
print("3^x =", np.power(, x))
x = [1, 2, 3]
e^x = [ 2.71828183 7.3890561 20.08553692]
^x = [2. 4. 8.]
^x = [ 3 9 27]
x = [, , , ]
print("x =", x)
print("ln(x) =", np.log(x))
print("log2(x) =", np.log2(x))
print("log10(x) =", np.log10(x))
x = [1, 2, 4, 10]
ln(x) = [0. 0.69314718 1.38629436 2.30258509]
log2(x) = [0. 1. 2. 3.32192809]
log10(x) = [0. 0.30103 0.60205999 1. ]
x = [, 0.001, 0.01, 0.1]
print("exp(x) - 1 =", np.expm1(x))
print("log(1 + x) =", np.log1p(x))
exp(x) - 1 = [0. 0.0010005 0.01005017 0.10517092]
log(1 + x) = [0. 0.0009995 0.00995033 0.09531018]
除了以上介绍到的, NumPy 还提供了很多通用函数, 包括双曲三角函数、 比特位运算、 比较运算符、 弧度转化为角度的运算、 取整 和求余运算, 等等。浏览 NumPy 的文档将会揭示很多有趣的功能。
from scipy import special
# Gamma函数和相关函数
x = [, , ]
print("gamma(x) =", special.gamma(x))
print("ln|gamma(x)| =", special.gammaln(x))
print("beta(x, 2) =", special.beta(x, ))
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.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]
在进行大量运算时, 有时候指定一个用于存放运算结果的数组是非常有用的。不同于创建临时数组, 你可以用这个特性将计算结果直接写入到你期望的存储位置。所有的通用函数都可以通过 out 参数来指定计算结果的存放位置:
x = np.arange()
y = np.empty()
np.multiply(x, , out=y)
print(y)
[ 0. 10. 20. 30. 40.]
y = np.zeros()
np.power(, x, out=y[::])
print(y)
[ 1. 0. 2. 0. 4. 0. 8. 0. 16. 0.]
如果这里写的是 y[::2] = 2 ** x, 那么结果将是创建一个临时数组, 该数组存放的是 2 ** x 的结果, 并且接下来会将这些值复制到 y 数组中。 对于上述例子中比较小的计算量来说, 这两种方式的差别并不大。但是对于较大的数组, 通过慎重使用 out 参数将能够有效节约内存。
二元通用函数有些非常有趣的聚合功能, 这些聚合可以直接在对象上计算。例如, 如果我们希望用一个特定的运算 reduce 一个数组, 那么可以用任何通用函数的 reduce 方法。一个 reduce 方法会对给定的元素和操作重复执行, 直至得到单个的结果。
x = np.arange(, )
# 返回数组中所有元素的和
np.add.reduce(x)
15
# 返回数组中所有元素的乘积
np.multiply.reduce(x)
120
# 存储每次计算的中间结果
np.add.accumulate(x)
array([ , , , , ], dtype=int32)
np.multiply.accumulate(x)
array([ , , , , ], dtype=int32)
请注意, 在一些特殊情况中, NumPy 提供了专用的函数(np.sum、 np.prod、 np.cumsum、 np.cumprod ) , 它们也可以实现以上 reduce 的功能。
最后, 任何通用函数都可以用 outer 方法获得两个不同输入数组所有元素对的函数运算结果。这意味着你可以用一行代码实现一个乘法表:
np.multiply.outer([, , ], [, , ])
array([[ 4, 5, 6],
[ 8, 10, 12],
[12, 15, 18]])
A = np.array([[, , ],
[, , ]])
B = np.array([[, , , ]])
C = np.multiply.outer(A, B)
C
array([[[[ 1, 2, 3, 4]],
[[ 2, 4, 6, 8]],
[[ 3, 6, 9, 12]]],
[[[ 4, 8, 12, 16]],
[[ 5, 10, 15, 20]],
[[ 6, 12, 18, 24]]]])
通用函数:更多的信息有关通用函数的更多信息(包括可用的通用函数的完整列表) 可以在 NumPy(http://www.numpy.org)和 SciPy(http://www.scipy.org) 文档的网站找到。
当你面对大量的数据时, 第一个步骤通常都是计算相关数据的概括统计值。最常用的概括统计值可能是均值和标准差, 这两个值能让你分别概括出数据集中的“经典”值, 但是其他一些形式的聚合也是非常有用的(如求和、 乘积、 中位数、 最小值和最大值、 分位数, 等等) 。
L = np.random.random()
sum(L)
46.18308216715081
# 结果是一样的
np.sum(L)
46.183082167150815
# NumPy的版本运行的更快些
big_array = np.random.rand()
%timeit sum(big_array)
%timeit np.sum(big_array)
ms ± 42.4 ms per loop (mean ± std. dev. of runs, loops each)
4.73 ms ± µs per loop (mean ± std. dev. of runs, loops each)
min(big_array), max(big_array)
(7.071203171893359e-07, 0.9999997207656334)
np.min(big_array), np.max(big_array)
(7.071203171893359e-07, 0.9999997207656334)
%timeit min(big_array)
%timeit np.min(big_array)
ms ± µs per loop (mean ± std. dev. of runs, loops each)
µs ± 6.87 µs per loop (mean ± std. dev. of runs, loops each)
对于 min、 max、 sum 和其他 NumPy 聚合, 一种更简洁的语法形式是数组对象直接调用这些方法:
print(big_array.min(), big_array.max(), big_array.sum())
7.071203171893359e-07 0.9999997207656334 500216.8034810001
一种常用的聚合操作是沿着一行或一列聚合。例如, 假设你有一些数据存储在二维数组中:
M = np.random.random((, ))
print(M)
[[0.79832448 0.44923861 0.95274259 0.03193135]
[0.18441813 0.71417358 0.76371195 0.11957117]
[0.37578601 0.11936151 0.37497044 0.22944653]]
默认情况下, 每一个 NumPy 聚合函数将会返回对整个数组的聚合结果:
M.sum()
5.1136763453287335
# 找到每一列的最小值
M.min(axis=)
array([0.18441813, 0.11936151, 0.37497044, 0.03193135])
# 找到每一行的最大值
M.max(axis=)
array([0.95274259, 0.76371195, 0.37578601])
Function Name | NaN-safe Version | Description |
---|---|---|
np.sum | np.nansum | Compute sum of elements |
np.prod | np.nanprod | Compute product of elements |
np.mean | np.nanmean | Compute mean of elements |
np.std | np.nanstd | Compute standard deviation |
np.var | np.nanvar | Compute variance |
np.min | np.nanmin | Find minimum value |
np.max | np.nanmax | Find maximum value |
np.argmin | np.nanargmin | Find index of minimum value |
np.argmax | np.nanargmax | Find index of maximum value |
np.median | np.nanmedian | Compute median of elements |
np.percentile | np.nanpercentile | Compute rank-based statistics of elements |
np.any | N/A | Evaluate whether any elements are true |
np.all | N/A | Evaluate whether all elements are true |