数组的计算:广播广播的介绍广播的规则广播的实际应用比较,掩码和布尔逻辑比较操作操作布尔数组将布尔数组作为掩码
《Python数据科学手册》读书笔记
另外一种向量化操作的方法是利用 NumPy 的广播功能。
对于同样大小的数组, 二进制操作是对相应元素逐个计算:
import numpy as np
a = np.array([, , ])
b = np.array([, , ])
a + b
array([, , ])
广播允许这些二进制操作可以用于不同大小的数组。例如, 可以简单地将一个标量(可以认为是一个零维的数组) 和一个数组相加:
a +
array([, , ])
我们可以认为这个操作是将数值 5 扩展或重复至数组 [5, 5, 5], 然后执行加法。NumPy 广播功能的好处是, 这种对值的重复实际上并没有发生, 但是这是一种很好用的理解广播的模型。
M = np.ones((, ))
M
array([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
M + a
array([[1., 2., 3.],
[1., 2., 3.],
[1., 2., 3.]])
这里这个一维数组就被扩展或者广播了。它沿着第二个维度扩展, 扩展到匹配 M 数组的形状。
以上的这些例子理解起来都相对容易, 更复杂的情况会涉及对两个数组的同时广播:
# 两个数组同时广播
a = np.arange()
b = np.arange()[:, np.newaxis]
print(a)
print(b)
[ ]
[[]
[]
[]]
a + b
array([[0, 1, 2],
[1, 2, 3],
[2, 3, 4]])
M = np.ones((, ))
a = np.arange()
根据规则1
根据规则2
M + a
array([[1., 2., 3.],
[1., 2., 3.]])
a = np.arange().reshape((, ))
b = np.arange()
根据规则1
根据规则2
a + b
array([[0, 1, 2],
[1, 2, 3],
[2, 3, 4]])
M = np.ones((, ))
a = np.arange()
根据规则1
根据规则2
根据规则3
最终形状不匹配,这两个数组不兼容
M + a
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-43-8cac1d547906> in <module>()
----> 1 M + a
ValueError: operands could not be broadcast together with shapes (,) (,)
# 如果希望实现右边补齐,可以通过变形数组来实现
a[:, np.newaxis].shape
(3, 1)
M + a[:, np.newaxis]
array([[1., 1.],
[2., 2.],
[3., 3.]])
另外也需要注意, 这里仅用到了 + 运算符, 而这些广播规则对于任意二进制通用函数都是适用的。例如这里的 logaddexp(a, b) 函 数, 比起简单的方法, 该函数计算 log(exp(a) + exp(b)) 更准确:
np.logaddexp(M, a[:, np.newaxis])
array([[1.31326169, 1.31326169],
[1.69314718, 1.69314718],
[2.31326169, 2.31326169]])
X = np.random.random((, ))
# 计算每一列的平均值
Xmean = X.mean()
Xmean
array([0.47874092, 0.54918989, 0.48897961])
# 实现归一化
X_centered = X - Xmean
# 检验每一列的均值是否为0
X_centered.mean()
array([ 1.66533454e-17, -8.88178420e-17, 0.00000000e+00])
# x and y have 50 steps from 0 to 5
x = np.linspace(, , )
y = np.linspace(, , )[:, np.newaxis]
z = np.sin(x) ** + np.cos( + y * x) * np.cos(x)
%matplotlib inline
import matplotlib.pyplot as plt
plt.imshow(z, origin='lower', extent=[, , , ],
cmap='viridis')
plt.colorbar();
x = np.array([, , , , ])
x <
array([ True, True, False, False, False])
x >
array([False, False, False, True, True])
x <=
array([ True, True, True, False, False])
x >=
array([False, False, True, True, True])
x !=
array([ True, True, False, True, True])
x ==
array([False, False, True, False, False])
# 利用复合表达式对两个数组进行逐元素比较
( * x) == (x ** )
array([False, True, False, False, False])
运算符 | 对应通用函数 |
---|---|
== | np.equal |
< | np.less |
> | np.greater |
!= | np.not_equal |
<= | np.less_equal |
>= | np.greater_equal |
# 比较运算通用函数可以用于任何形状大小的数组
rng = np.random.RandomState()# 确保每次产生的随机数组是一样的
x = rng.randint(, size=(, ))
x
array([[5, 0, 3, 3],
[7, 9, 3, 5],
[2, 4, 7, 6]])
x <
array([[ True, True, True, True],
[False, False, True, True],
[ True, True, False, False]])
这样每次计算的结果都是布尔数组了。NumPy 提供了一些简明的模式来操作这些布尔结果。
给定一个布尔数组, 你可以实现很多有用的操作。首先打印出此前生成的二维数组 x:
print(x)
[[ ]
[ ]
[ ]]
# 有多少个值小于6
np.count_nonzero(x < )
8
# 也可使用sum来进行计数,这个例子中F被解释成0,T被解释成1
np.sum(x < )
8
# 每行有多少个值小于6
np.sum(x < , axis=)
array([, , ])
# 有没有值大于8
np.any(x > )
True
# 有没有值小于0
np.any(x < )
False
# 是否所有值小于10
np.all(x < )
True
# 是否所有值等于6
np.all(x == )
False
# 是否每行的所有值都小于8
np.all(x < , axis=)
array([ True, False, True])
运算符 | 对应通用函数 |
---|---|
& | np.bitwise_and |
np.bitwise_or | |
~ | np.bitwise_not |
一种更强大的模式是使用布尔数组作为掩码, 通过该掩码选择数据的子数据集。
x
array([[5, 0, 3, 3],
[7, 9, 3, 5],
[2, 4, 7, 6]])
x <
array([[False, True, True, True],
[False, False, True, False],
[ True, True, False, False]])
现在为了将这些值从数组中选出, 可以进行简单的索引, 即掩码操作:
# 将小于5的值从数组中筛选出来
x[x < ]
array([, , , , , ])
and和or对整个对象执行单个布尔运算,而&和|对一个对象的内容执行多个布尔运算,对于Numpy布尔数组,后者是最常用的操作