NumPy的统计函数

NumPy内部提供了很多用于统计的函数:

函数 说明
np.sum(a, axis=None) 根据给定轴axis计算数组a的相关元素之和, axis为整数或元组
np.mean(a, axis=None) 根据给定轴axis计算数组a的相关元素的期望
np.average(a, axis=None, weights=None) 根据给定轴axis计算数组a的相关元素的加权平均值
np.std(a, axis=None) 根据给定轴axis计算数组a的相关元素的标准差
np.var(a, axis=None) 根据给定轴axis计算数组a的相关元素的方差

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> import numpy as np
>>> a = np.arange(12).reshape(3, 4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> np.sum(a)
66
>>> np.sum(a, axis=0)
array([12, 15, 18, 21])
>>> np.sum(a, axis=1)
array([ 6, 22, 38])
>>> np.mean(a)
5.5
>>> np.mean(a, axis=0)
array([4., 5., 6., 7.])
>>> np.mean(a, axis=1)
array([1.5, 5.5, 9.5])
>>> np.average(a)
5.5
>>> np.average(a, weights=a)
7.666666666666667
>>> np.std(a)
3.452052529534663
>>> np.var(a)
11.916666666666666

从例子中我们可以看出, 当axis = 0 时, 函数是对x轴进行计算, 当axis = 1时, 函数是对y轴进行计算.
除此之外还有一些其他用于统计的函数:

函数 说明
a.max(axis=None) np.max(a, axis=None) a.min(axis=None) np.min(a, axis=None) 根据给定轴axis计算数组a中元素的最大/小值
a.argmax(axis=None) np.argmax(a, axis=None) a.argmin(axis=None) np.argmin(a, axis=None) 根据给定轴axis计算数组a中的最大/小值降一维后的下标
np.unravel_index(indices, shape) 根据shape, 找到indices的位置
np.ptp(a, axis=None) 根据给定轴axis计算数组a中元素的极值
np.median(a, axis=None) 根据给定轴axis计算数组中元素的中值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> b = np.arange(12).reshape(3, 4)
>>> b
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> b.max()
11
>>> np.max(b)
11
>>> b.max(axis=0)
array([ 8, 9, 10, 11])
>>> np.max(b, axis=0)
array([ 8, 9, 10, 11])
>>> np.argmax(b)
11
>>> np.unravel_index(np.argmax(b), b.shape)
(2, 3)
>>> np.ptp(b)
11
>>> np.median(b)
5.5

NumPy的梯度函数

梯度: 连续值之间的变化率, 即斜率.

函数 说明
np.gradient(a, axis=None) 计算数组f中元素的梯度, 当f为多维时, 返回每个维度梯度

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> import numpy as np
>>> a = np.arange(12).reshape(3, 4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> np.gradient(a)
[array([[4., 4., 4., 4.],
[4., 4., 4., 4.],
[4., 4., 4., 4.]]),
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])]

ndarry的复制、视图和深拷贝
简单赋值并不会创建数组对象或其数据的拷贝.

例如:

1
2
3
4
5
6
7
>>> a = np.arange(12)
>>> b = a # no new object is created
>>> b is a # a and b are two names for the same ndarray object
True
>>> b.shape = (3, 4) # changes the shape of a
>>> a.shape
(3, 4)

Python将可变对象作为引用传递, 所以函数调用不会复制.

1
2
3
4
5
6
7
>>> def f(x):
... print(id(x))
...
>>> id(a) # id is a unique identifier of an object
148293216
>>> f(a)
148293216

不同的数组对象可以共享相同的数据. view() 方法创建一个新的数组对象, 它可以查看相同的数据, 即视图(详见 官网文档 ).
例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> c = a.view()
>>> c is a
False
>>> c.base is a # c is a view of the data owned by a
True
>>> c.flags.owndata
False
>>>
>>> c.shape = (2, 6) # a's shape doesn't change
>>> a.shape
(3, 4)
>>> c[0,4] = 1234 # a's data changes
>>> a
array([[ 0, 1, 2, 3],
[1234, 5, 6, 7],
[ 8, 9, 10, 11]])

对数组切片返回一个视图:

1
2
3
4
5
6
>>> s = a[ : , 1:3]     # spaces added for clarity; could also be written "s = a[:,1:3]"
>>> s[:] = 10 # s[:] is a view of s. Note the difference between s=10 and s[:]=10
>>> a
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])

copy() 方法可以生成数组及其数据的完整拷贝, 即深拷贝(详见 官方文档 ).

1
2
3
4
5
6
7
8
9
10
>>> d = a.copy()                          # a new array object with new data is created
>>> d is a
False
>>> d.base is a # d doesn't share anything with a
False
>>> d[0,0] = 9999
>>> a
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])

广播(Broadcasting)规则

Broadcasting允许通用函数以有意义的方式处理具有不完全相同形状的输入(详见 官方文档).
NumPy操作通常是在逐个元素的基础上在数组对上完成的. 在最简单的情况下, 两个数组必须具有完全相同的形状, 如下例所示:

1
2
3
4
>>> a = np.array([1.0, 2.0, 3.0])
>>> b = np.array([2.0, 2.0, 2.0])
>>> a * b
array([ 2., 4., 6.])

当数组的形状满足一定的条件时, NumPy的broadcasting规则可以放宽这个限制. 最简单的broadcasting示例发生在一个操作包含数组和标量值的时候:

1
2
3
4
>>> a = np.array([1.0, 2.0, 3.0])
>>> b = 2.0
>>> a * b
array([ 2., 4., 6.])

结果等同于前面的例子, 其中b是一个数组. 在算术运算期间, 我们可以认为标量b被拉伸了, 形成与a相同形状的数组. b中的新元素是原始标量简单的拷贝. 拉伸这个比喻只是概念性的. NumPy足够聪明, 它使用原始的标量值而不会真正拷贝, 使broadcasting操作尽可能的内存和计算高效.

第二个例子中的代码比第一个例子中的代码更有效, 因为broadcasting在乘法期间移动较少的内存(b是标量而不是数组).

Broadcasting的一般规则

当在两个数组上操作时, NumPy在元素级别比较它们的形状. 它从尾随的维度开始, 并朝着前进的方向前进. 两个维度兼容, 当:

  1. 它们是相等的
  2. 其中之一是1

如果不满足这些条件, 则会抛出ValueError: frames are not aligned异常, 指示数组具有不兼容的形状. 结果数组的大小是沿着输入数组的每个维度的最大大小.

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
>>> import numpy as np
>>> x = np.arange(4)
>>> x
array([0, 1, 2, 3])
>>> xx = x.reshape(4, 1)
>>> xx
array([[0],
[1],
[2],
[3]])
>>> y = np.ones(5)
>>> y
array([1., 1., 1., 1., 1.])
>>> z = np.ones((3, 4))
>>> z
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
>>> x + y
ValueError: operands could not be broadcast together with shapes (4,) (5,)
>>> xx + y
array([[1., 1., 1., 1., 1.],
[2., 2., 2., 2., 2.],
[3., 3., 3., 3., 3.],
[4., 4., 4., 4., 4.]])
>>> x + z
array([[1., 2., 3., 4.],
[1., 2., 3., 4.],
[1., 2., 3., 4.]])

ndarry的花式索引

使用索引数组索引

1
2
3
4
5
6
7
8
9
>>> a = np.arange(12)**2                       # the first 12 square numbers
>>> i = np.array( [ 1,1,3,8,5 ] ) # an array of indices
>>> a[i] # the elements of a at the positions i
array([ 1, 1, 9, 64, 25])
>>>
>>> j = np.array( [ [ 3, 4], [ 9, 7 ] ] ) # a bidimensional array of indices
>>> a[j] # the same shape as j
array([[ 9, 16],
[81, 49]])

你还可以使用数组索引作为目标来赋值:

1
2
3
4
5
6
>>> a = np.arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> a[[1,3,4]] = 0
>>> a
array([0, 0, 2, 0, 0])

使用布尔值作为数组索引

当我们用(整数)索引数组索引数组时, 我们提供了要选择的索引列表. 使用布尔值作为索引时, 法是不同的; 我们明确地选择数组中的哪些元素我们想要的, 哪些不是.
我们可以想到的布尔索引最自然的方式是使用与原始数组具有相同形状的布尔数组:

1
2
3
4
5
6
7
8
>>> a = np.arange(12).reshape(3,4)
>>> b = a > 4
>>> b # b is a boolean with a's shape
array([[False, False, False, False],
[False, True, True, True],
[ True, True, True, True]])
>>> a[b] # 1d array with the selected elements
array([ 5, 6, 7, 8, 9, 10, 11])

此属性在赋值时非常有用:

1
2
3
4
5
>>> a[b] = 0                                   # All elements of 'a' higher than 4 become 0
>>> a
array([[0, 1, 2, 3],
[4, 0, 0, 0],
[0, 0, 0, 0]])

还可以按照另一数组的值分割当前数组:

1
2
3
4
5
6
7
8
>>> a = np.arange(5)
>>> b = np.array([0, 0, 1, 1, 2])
>>> a[b==0]
array([0, 1])
>>> a[b==1]
array([2, 3])
>>> a[b==2]
array([4])

ix_()函数索引

可以使用 ix_() 函数来组合不同的向量以获得每个n-uplet的结果(详见 官方文档 ). 例如, 如果要计算从向量a、b和c中的取得的所有三元组的所有a + b * c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
>>> a = np.array([2,3,4,5])
>>> b = np.array([8,5,4])
>>> c = np.array([5,4,6,8,3])
>>> ax,bx,cx = np.ix_(a,b,c)
>>> ax
array([[[2]],
[[3]],
[[4]],
[[5]]])
>>> bx
array([[[8],
[5],
[4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> result = ax+bx*cx
>>> result
array([[[42, 34, 50, 66, 26],
[27, 22, 32, 42, 17],
[22, 18, 26, 34, 14]],
[[43, 35, 51, 67, 27],
[28, 23, 33, 43, 18],
[23, 19, 27, 35, 15]],
[[44, 36, 52, 68, 28],
[29, 24, 34, 44, 19],
[24, 20, 28, 36, 16]],
[[45, 37, 53, 69, 29],
[30, 25, 35, 45, 20],
[25, 21, 29, 37, 17]]])
>>> result[3, 2, 4]
17
>>> a[3] + b[2] * c[4]
17