Python入门教程(六):Numpy计算之布尔运算
点击上方 蓝字 关注我们
什么是布尔运算?
布尔运算是一种关系运算,包括以下几类:
对于布尔类型boolean,永远只有true和false两个值。
比较运算符:>,>=,<,<=,==,!=
与运算 &&
或运算 ||
非运算 !
什么是布尔掩码?
布尔掩码是基于规则来抽取,修改,计数或者对一个数组中的值进行其他操作,例如,统计数组中有多少大值于某一个值给定的值,或者删除某些超出门限的异常值。
01
引言:统计下雨天示例
假设你有一系列某城市一年内日降水量的数据,这份数据包含了2014年1月1日到2014年12月31每天的降水量,单位英寸。
import numpy as np
import pandas as pd
# use pandas to extract rainfall inches as a NumPy array
rainfall = pd.read_csv('data/Seattle2014.csv')['PRCP'].values
inches = rainfall / 254.0 # 1/10mm -> inches
inches.shape
# (365,)
我们用matplotlib可视化,生成下雨天数量的直方图。
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn; seaborn.set() # set plot styles
plt.hist(inches, 40);
但是这个直方图表明2014年在西雅图大多数时间降水量为0,这样并不能很好地传达信息,例如我们并不知道这些下雨天的一个平均降水量,有多少天的降水量超过一英寸?
计算上述问题呢,我们可以使用通用的传统计算方式实现,即对所有数据循环,当碰到数据落在我们希望的区间时计数器加1。这种方法从计算结果的角度看,不仅浪费时间而且效率极低。
如果我们使用Numpy的通用函数可以用来替代循环,以实现快速的数组的逐元素比较,同样地,我们也可以用掩码来解决这些问题。
02
布尔运算与基础函数的比较
布尔运算是一种关系运算,包括以下几类:
对于布尔类型boolean,永远只有true和false两个值。
比较运算符:>,>=,<,<=,==,!=
与运算 &&
或运算 ||
非运算 !
这些运算的结果是一个布尔数据类型的数组,一共有一下操作
x = np.array([1, 2, 3, 4, 5])
x < 3 # 小于
# array([ True, True, False, False, False], dtype=bool)
x > 3 # 大于
# array([False, False, False, True, True], dtype=bool)
x <= 3 # 小于等于
# array([ True, True, True, False, False], dtype=bool)
x >= 3 # 大于等于
# array([False, False, True, True, True], dtype=bool)
x != 3 # 不等于
# array([ True, True, False, True, True], dtype=bool)
x == 3 # 等于
# array([False, False, True, False, False], dtype=bool)
同时,也可以用复合表达式对两个数组元素逐个比较。
(2 * x) == (x ** 2)
# array([False, True, False, False, False], dtype=bool)
如下表所示,我们对不同类型的布尔运算进行了总结。
同样的,和算术通用函数一样,这些比较运算函数也可以用于任意形状大小的数组。来看个二维数组的示例。
rng = np.random.RandomState(0)
x = rng.randint(10, size=(3, 4))
x
# array([[5, 0, 3, 3],
# [7, 9, 3, 5],
# [2, 4, 7, 6]])
x < 6
# array([[ True, True, True, True],
# [False, False, True, True],
# [ True, True, False, False]], dtype=bool)
03
布尔型数组的应用
给定一个布尔数组,我们可以实现很多操作,例如,计数,求和等等。
print(x)
# [[5 0 3 3]
# [7 9 3 5]
# [2 4 7 6]]
计数
统计布尔数组中True的记录个数,可以使用np.count_nonzero函数。
# 统计小于6的值
np.count_nonzero(x < 6)
# 8
另一种实现方式是用np.sum实现,这个例子中Flase会被判定为0,True会被判定为1.
np.sum(x < 6)
# 8
sum函数的好处是可以沿着行或者列进行操作。
# 每行中有多少个小于6的值
np.sum(x < 6, axis=1)
# array([4, 2, 2])
np.any()可以快速检查这些值是否为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()和np.any()也可以沿着特定的坐标轴进行计算。
# a是否是每行的所有值都小于8
np.all(x < 8, axis=1)
# array([ True, False, True], dtype=bool)
04
布尔运算符
布尔运算符包括了比较运算符:>,>=,<,<=,==,!=;与运算 &&;或运算 ||;非运算 !。同标准运算符一样,Numpy用通用函数重载了这些逻辑运算符,即可以实现数组的逐位运算。
例如,可以写如下的复合表达式:
np.sum((inches > 0.5) & (inches < 1))
# 29
我们可以看到降水量在0.5-1英寸间的天数是29天。这里的括号非常重要,因为有运算优先级,如果去掉括号,运行可能会发生错误。
利用 A AND B 和 NOT (NOT A OR NOT B) 的等价原理,可以以另外一种形式实现同样的结果。
np.sum(~( (inches <= 0.5) | (inches >= 1) ))
# 29
将比较运算符和布尔运算符合并起来用在数组上,可以实现更多有效的逻辑运算操作。
下表总结了逐位的布尔运算和其对应的通用函数。
利用这些工具,就可以回答那些天气数据的问题了。以下的数据是结合使用掩码和聚合实现的计算结果。
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.2 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.2 inches : 75
05
将布尔数组作为掩码
掩码就是数组的索引操作,为了将数组中的某些值选出来,可以进行简单的索引,即掩码操作。
布尔数组可以作为掩码,可以通过该掩码选择数据的子数组。
x
# array([[5, 0, 3, 3],
# [7, 9, 3, 5],
# [2, 4, 7, 6]])
首先,我们利用一个比较运算符得到一个布尔数组。
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)
# 创建一个包含整个夏季日期的掩码
days = np.arange(365)
summer = (days > 172) & (days < 262)
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
06
关键字和逻辑操作符的区别
初学者可能经常困惑于关键字and 和or,以及逻辑操作运算符&和|的区别,什么时候该选择哪一种?
它们的区别是:and和or判断整个对象是真是假,而&和|是指每个对象中的比特位。用and和or时,就相当于让Python将整个对象当作整个布尔尸体。在Python中所有非零的整数都会被当成True。
bool(42), bool(0)
# (True, False)
bool(42 and 0)
# False
bool(42 or 0)
# True
当你对整数使用&和|时,表达式操作的是元素的比特,将and和or应用于组成该数字的每个比特。
bin(42)
# '0b101010'
bin(59)
# '0b111011'
bin(42 & 59)
# '0b101010'
bin(42 | 59)
# '0b111011'
请注意,&和|运算时,对应的二进制的比特位进行比较以得出结果。
当你在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], dtype=bool)
而用or来计算两个数组时,Python会计算整个数组对象的真或假,这会导致程序出错。
A or B
# ---------------------------------------------------------------------------
# ValueError Traceback (most recent call last)
# <ipython-input-38-5d8e4f2e21c0> in <module>()
# ----> 1 A or B
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
同样,对于给定数组的进行逻辑运算时,我们也应该使用&或|,而不是or或and。
x = np.arange(10)
(x > 4) & (x < 8)
# array([False, False, False, False, False, True, True, True, False, False], dtype=bool)
如果试图对整个数组计算真或假时,程序同样也会给出ValueError的错误。
(x > 4) and (x < 8)
# ---------------------------------------------------------------------------
# ValueError Traceback (most recent call last)
# <ipython-input-40-3d24f1ffd63d> in <module>()
# ----> 1 (x > 4) and (x < 8)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
总结一下,and和or对整个对象执行单个布尔运算,而对&和|对一个对象的内容(单个比特或字节)执行多个布尔运算。对于Numpy数组,后者是比较常用的操作。
全部代码已上传,公众号后台回复【布尔】即可获得。
参考书籍:《python数据科学手册》
python入门系列文章持续更新中,欢迎加入数据人专属交流群
往期推荐
分享数据知识,成就数据理想
点个在看 你最好看