数据科学 IPython 笔记本 9.8 比较,掩码和布尔逻辑

作者 : 开心源码 本文共6323个字,预计阅读时间需要16分钟 发布时间: 2022-05-12 共175人阅读

9.8 比较,掩码和布尔逻辑

本节是《Python 数据科学手册》(Python Data Science Handbook)的摘录。

译者:飞龙

协议:CC BY-NC-SA 4.0

本节详情如何使用布尔掩码,来检查和操作 NumPy 数组中的值。当你想要根据某些标准,提取,修改,计算或者以其余方式操纵数组中的值时,掩码会有所帮助:例如,你可能希望计算大于某个值的所有值,或者者可能删除高于某些阈值的所有异常值。

在 NumPy 中,布尔掩码通常是完成这些类型任务的最有效方法。

示例:统计雨天

想象一下,你有一系列数据表示某一城市一年中每天的降水量。例如,在这里我们将使用 Pandas 加载 2014 年西雅图市的每日降雨量统计数据(在第三章中有更详细的详情):

import numpy as npimport pandas as pd# 使用 pandas 将降雨量英寸提取为 NumPy 数组rainfall = pd.read_csv('data/Seattle2014.csv')['PRCP'].valuesinches = rainfall / 254  # 1/10mm -> 英寸inches.shape# (365,)

该数组包含 365 个值,提供了 2014 年 1 月 1 日至 12 月 31 日的每日降雨量,单位为英寸。

作为第一个简单的可视化,让我们看一下使用 Matplotlib 生成的雨天的直方图(我们将在第四章中更全面地探究这个工具):

%matplotlib inlineimport matplotlib.pyplot as pltimport seaborn; seaborn.set()  # 设置绘图风格plt.hist(inches, 40);

png

这个直方图让我们对数据的概略有了一个大概的理解:虽然它的声誉很高,但 2014 年西雅图的绝大多数日子的测得的降雨量几乎为零。但这并没有很好地传达我们希望看到的少量信息:例如,一年中有多少雨天?那些下雨天的平均降雨量是多少? 有多少天有超过半英寸的降雨?

挖掘数据

一种方法是手动答复这些问题:遍历数据,每当我们看到某个所需范围内的值时,递增计数器。因为本章探讨的起因,从编写代码的时间和计算结果的时间的角度来看,这种方法效率非常低。

我们在“NumPy 上的数组计算:通用函数”中看到,NumPy 的ufuncs可用于代替循环,对数组进行快速的逐元素算术运算;以同样的方式,我们可以使用其余ufunc对数组进行逐元素比较,而后我们可以操纵结果来答复我们的问题。

我们现在暂时搁置数据,并探讨 NumPy 中的少量常用工具,使用掩码快速答复这类的问题。

作为ufunc的比较运算

在“NumPy 上的数组计算:通用函数”中,我们详情了ufunc,专注于算术运算符。 我们看到,在数组上使用+-*/和其余,产生了逐元素操作。

NumPy 还将比较运算符,例如<(小于)和>(大于),实现为逐元素的ufunc。这些比较运算符的结果始终是布尔数据类型的数组。所有六种标准比较操作都可用:

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)

与算术运算符的情况一样,比较运算符在 NumPy 中实现为ufunc;例如,当你编写x <3时,NumPy 内部使用np.less(x, 3)

此处显示了比较运算符及其等价ufunc的摘要:

运算符等价 ufunc运算符等价 ufunc
==np.equal!=np.not_equal
<np.less<=np.less_equal
>np.greater>=np.greater_equal

就像算术ufunc的情况一样,这些适用于任何大小和形状的数组。这是一个二维的例子:

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)'''

在每种情况下,结果都是一个布尔数组,NumPy 提供了许多简单的模式来解决这些布尔结果。

使用布尔数组

给定一个布尔数组,你可以执行许多有用的操作。我们将使用x,我们之前创立的二维数组。

print(x)'''[[5 0 3 3] [7 9 3 5] [2 4 7 6]]'''

对元素计数

要计算布尔数组中True元素的数量,np.count_nonzero很有用:

# 多少个值小于 6np.count_nonzero(x < 6)# 8

我们看到有八个小于 6 的数组元素。获取此信息的另一种方法是使用np.sum;在这种情况下,False解释为0,而True解释为1

np.sum(x < 6)# 8

`sum()“的好处就是和其余NumPy聚合函数一样,这个求和也可以沿着行或者列来完成:

# 每一行有多少个值小于 6np.sum(x < 6, axis=1)# array([4, 2, 2])

这计算了矩阵每行中小于 6 的值的数量。

假如我们有兴趣快速检查,能否任何或者所有值都是真的,我们可以使用(你猜对了)np.any或者np.all

# 存在大于 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.allnp.any也可用于特定的轴。例如:

# 每一行的所有值都小于 4 吗?np.all(x < 8, axis=1)# array([ True, False,  True], dtype=bool)

这里第一行和第三行中的所有元素都小于 8,而第二行则不是这种情况。

最后,一个简单的警告:如“聚合:最小、最大和之间的任何东西”中所述,Python 内置了sum()any()all()函数。 它们的语法与 NumPy 版本不同,特别是在多维数组上使用时会失败或者产生意外结果。对于这些情况,请确保使用np.sum()np.any()np.all(()

布尔运算符

我们已经看到了我们如何计算,比方降雨量小于 4 英寸的所有日子,或者降雨量大于 2 英寸的所有日子。但是假如我们想理解降雨量小于 4 英寸且大于 1 英寸的所有日子呢?

这是通过 Python 的按位逻辑运算符,&|^~来实现的。与标准算术运算符一样,NumPy 将这些重载为ufunc,这些ufunc在(通常是布尔)数组上逐元素工作。例如,我们可以像这样处理这种复合问题:

np.sum((inches > 0.5) & (inches < 1))# 29

所以我们看到有 29 天的降雨量在 0.5 到 1.0 英寸之间。请注意,此处的括号很重要 – 因为运算符优先级规则,删除了括号,此表达式将按如下方式计算,这会导致错误:

inches > (0.5 & inches) < 1

使用A AND BNOT (NOT A OR NOT B)的等价性(假如你已经参与了逻辑入门课程,你可能还记得),我们可以用不同的方式计算相同的结果:

np.sum(~( (inches <= 0.5) | (inches >= 1) ))# 29

在数组上组合比较运算符和布尔运算符。可以实现广泛的高效逻辑运算。下表总结了按位布尔运算符及其等效的ufunc

运算符等价 ufunc运算符等价 ufunc
&np.bitwise_and<code>|</code>np.bitwise_or
^np.bitwise_xor~np.bitwise_not

使用这些工具,我们可以开始答复有关天气数据的问题。以下是将掩码聚合结合使用时,可以计算的少量结果示例:

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:       215Number days with rain:          150Days with more than 0.5 inches: 37Rainy days with < 0.2 inches  : 75'''

作为掩码的布尔数组

在上一节中,我们研究了直接在布尔数组上计算的聚合。更强大的模式是将布尔数组用作掩码,来选择数据本身的特定子集。回到之前的x数组,假设我们想要所有值小于 5 的数组:

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)# 为所有夏天构造掩码(6 月 21 日是第 172 天)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.194881889764Median precip on summer days in 2014 (inches):   0.0Maximum precip on summer days in 2014 (inches):  0.850393700787Median precip on non-summer rainy days (inches): 0.200787401575'''

通过组合布尔运算,掩码操作和聚合,我们可以非常快速地为我们的数据集答复这些问题。

注:使用关键字and/or与运算符&/|

一个常见的混淆点是,关键字andor,与运算符&|之间的区别。你什么时候使用其中一个?

区别在于:andor衡量整个对象的真实性或者错误性,而&|指的是每个对象中的位。当你使用andor时,它等同于要求 Python 将对象视为一个布尔实体。在 Python 中,所有非零整数都将计算为True。 从而:

bool(42), bool(0)# (True, False)bool(42 and 0)# Falsebool(42 or 0)# True

当你在整数上使用&|时,表达式操作元素的位,将“和”或者“或者”应用于构成数字的各个位:

bin(42)# '0b101010'bin(59)# '0b111011'bin(42 & 59)# '0b101010'bin(42 | 59)# '0b111011'

请注意,比较二进制表示的相应位来产生结果。

当你在 NumPy 中有一个布尔值数组时,它可以看做是一串位,其中1 = True0 = 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)

在这些数组上使用and或者or,将尝试求解整个数组对象的真实性或者错误性,这不是一个明确定义的值:

A or B'''---------------------------------------------------------------------------ValueError                                Traceback (most recent call last)<ipython-input-38-5d8e4f2e21c0> in <module>()----> 1 A or BValueError: 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()'''

所以记住这一点:andor对整个对象执行单个布尔求值,而&|对对象的内容(单个位或者字节)执行屡次布尔求值。对于布尔 NumPy 数组,后者几乎总是所需的操作。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 数据科学 IPython 笔记本 9.8 比较,掩码和布尔逻辑

发表回复