操作布尔数组
给定一个布尔数组,你可以实现很多有用的操作。首先打印出此前生成
的二维数组
x
:
print(x)
[[5 0 3 3]
[7 9 3 5]
[2 4 7 6]]
01.
统计记录的个数
如果需要统计布尔数组中
True
记录的个数,可以使用
np.count_nonzero
函数:
我们看到有
8
个数组记录是小于
6
的。另外一种实现方式是利用
np.sum
。在这个例子中,
False
会被解释成
0
,
True
会被解释成
1
:
np.sum(x < 6)
8
sum()
的好处是,和其他
NumPy
聚合函数一样,这个求和也可以
沿着行或列进行:
# 每行有多少值小于6?
np.sum(x < 6, axis=1)
array([4, 2, 2])
这是矩阵中每一行小于
6
的个数。如要快速检查任意或者所有这些值是否为
True
,可以用(你一定
猜到了)
# 有没有值大于8?
np.any(x > 8)
True
# 有没有值小于0?
np.any(x < 0)
False
# 是否所有值都小于10?
np.all(x < 10)
True
# 是否所有值都等于6?
np.all(x == 6)
False
np.all()
和
n p.any()
也可以用于沿着特定的坐标轴,例如:
# 是否每行的所有值都小于8?
np.all(x < 8, axis=1)
array([ True, False, True], dtype=bool)
这里第
1
行和第
3
行的所有元素都小于
8
,而第
2
行不是所有元素都小于
8
。最后需要提醒的是,
Python
有内置的
sum()
、
any()
和
all()
函数,这些函数在
NumPy
中有不同的语
法版本。如果在多维数组上混用这两个版本,会导致失败或产生不
可预知的错误结果。因此,确保在以上的示例中用的都是
np.sum()
、
np.any()
和
np.all()
函数。
02. 布尔运算符
这可以通过
Python
的逐位逻辑运算符(
bitwise logic operator
)
&
、
|
、
^
和
~
来实现。同标准的算术运算符一样,
NumPy
用通用函数重载了这些逻辑运算符,这样可以实现数组的
逐位运算(通常是布尔运算)。
例如,可以写如下的复合表达式:
np.sum((inches > 0.5) & (inches < 1))
29
可以看到,降水量在
0.5
英寸
~1
英寸间的天数是
29
天。
请注意,这些括号是非常重要的,因为有运算优先级规则。如果去
掉这些括号,该表达式会变成以下形式,这会导致运行错误:
利用
A AND B
和
NOT (A OR B)
的等价原理(你应该在基础逻辑
课程中学习过),可以用另外一种形式实现同样的结果:
将比较运算符和布尔运算符合并起来用在数组上,可以实现更多有
效的逻辑运算操作。以下表格总结了逐位的布尔运算符和其对应的通用函数。
利用这些工具,就可以回答那些关于天气数据的问题了。以下的示
例是结合使用掩码和聚合实现的结果计算:
print("Number days without rain: ", np.sum(inches == 0))
print("Number days with rain: ", np.sum(inches != 0))
print("Days with more than 0.5 inches:", np.sum(inches > 0.5))
print("Rainy days with < 0.1 inches :", np.sum((inches > 0) & (inches < 0.2)
Number days without rain: 215
Number days with rain: 150
Days with more than 0.5 inches: 37
Rainy days with < 0.1 inches : 75
2.6.4 将布尔数组作为掩码
在前面的小节中,我们看到了如何直接对布尔数组进行聚合计算。一种
更强大的模式是使用布尔数组作为掩码,通过该掩码选择数据的子数据
集。以前面小节用过的
x
数组为例,假设我们希望抽取出数组中所有小
于
5
的元素:
如前面介绍过的方法,利用比较运算符可以得到一个布尔数组:
x < 5
array([[False, True, True, True],
[False, False, True, False],
[ True, True, False, False]], dtype=bool)
现在为了将这些值从数组中选出,可以进行简单的索引,即掩码操作:
x[x < 5]
array([0, 3, 3, 3, 2, 4])
现在返回的是一个一维数组,它包含了所有满足条件的值。换句话说,
所有的这些值是掩码数组对应位置为
True
的值。现在,可以对这些值做任意操作,例如可以根据西雅图降水数据进行一
些相关统计:
# 为所有下雨天创建一个掩码
rainy = (inches > 0)
# 构建一个包含整个夏季日期的掩码(6月21日是第172天)
summer = (np.arange(365) - 172 < 90) & (np.arange(365) - 172 > 0)
print("Median precip on rainy days in 2014 (inches): ", np.median(inches[rainy]))
print("Median precip on summer days in 2014 (inches): ", np.median(inches[summer]))
print("Maximum precip on summer days in 2014 (inches): ", np.max(inches[summer]))
print("Median precip on non-summer rainy days (inches):", np.median(inches[rainy & ~summer]))
Median precip on rainy days in 2014 (inches): 0.194881889764
Median precip on summer days in 2014 (inches): 0.0
Maximum precip on summer days in 2014 (inches): 0.850393700787
Median precip on non-summer rainy days (inches): 0.200787401575
通过将布尔操作、掩码操作和聚合结合,可以快速回答对数据集提出的
这类问题。使用关键字
and
/
or
与使用逻辑操作运算符
&
/
|
人们经常困惑于关键字
and
和
or
,以及逻辑操作运算符
&
和
|
的
区别是什么,什么时候该选择哪一种?
它们的区别是:
and
和
or
判断整个对象是真或假,而
&
和
|
是指
每个对象中的比特位。当你使用
and
或
or
时,就等于让
Python
将这个对象当作整个布尔
实体。在
Python
中,所有非零的整数都会被当作是
True
:
当你对整数使用
&
和
|
时,表达式操作的是元素的比特,将
and
或
or
应用于组成该数字的每个比特:
请注意,
&
和
|
运算时,对应的二进制比特位进行比较以得到最终结果。
当你在
NumPy
中有一个布尔数组时,该数组可以被当作是由比特
字符组成的,其中
1 = True
、
0 = False
。这样的数组可以用上
面介绍的方式进行
&
和
|
的操作:
A = np.array([1, 0, 1, 0, 1, 0], dtype=bool)
B = np.array([1, 1, 1, 0, 1, 1], dtype=bool)
A | B
array([ True, True, True, False, True, True])
而用
or
来计算这两个数组时,
Python
会计算整个数组对象的真或
假,这会导致程序出错:
同样,对给定
数组进行逻辑运算时,你也应该使用
|
或
&
,而不是
or
或
and
:
如果试图计算整个数组的真或假,程序也同样会给出
ValueError
的错误:
因此可以记住:
and
和
or
对整个对象执行单个布尔运算,而
&
和
|
对一个对象的内容(单个比特或字节)执行多个布尔运算。对于
NumPy
布尔数组,后者是常用的操作。