点击上方 蓝字 关注我们

‍‍‍‍‍什么是布尔运算?

布尔运算是一种关系运算,包括以下几类:

对于布尔类型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入门系列文章持续更新中,欢迎加入数据人专属交流群

往期推荐

Python入门教程(一):初识Numpy

Python入门教程(二):Numpy数组基础

SQL知识大全(六):SQL中的开窗函数

刷爆全网的动态条形图,原来5行Python代码就能实现!

 

分享数据知识,成就数据理想

点个在看 你最好看