Pandas 中布尔索引的逻辑运算符

2024-11-26 08:36:00
admin
原创
156
摘要:问题描述:我正在使用 Pandas 中的布尔索引。问题是为什么会有这样的声明:a[(a['some_column']==some_number) & (a['some_other_column']==some_other_number)] 工作正常,而a[(a['some_column']==some...

问题描述:

我正在使用 Pandas 中的布尔索引。

问题是为什么会有这样的声明:

a[(a['some_column']==some_number) & (a['some_other_column']==some_other_number)]

工作正常,而

a[(a['some_column']==some_number) and (a['some_other_column']==some_other_number)]

因错误退出?

例子:

a = pd.DataFrame({'x':[1,1],'y':[10,20]})

In: a[(a['x']==1)&(a['y']==10)]
Out:    x   y
     0  1  10

In: a[(a['x']==1) and (a['y']==10)]
Out: ValueError: The truth value of an array with more than one element is ambiguous.     Use a.any() or a.all()

解决方案 1:

当你说

(a['x']==1) and (a['y']==10)

您隐式要求 Python 将 转换(a['x']==1)(a['y']==10)布尔值。

NumPy 数组(长度大于 1)和 Pandas 对象(例如 Series)没有布尔值——换句话说,它们会引发

ValueError:数组的真值不明确。请使用 a.empty、a.any() 或 a.all()。

当用作布尔值时。这是因为不清楚它何时应该是 True 或 False。一些用户可能会假设如果它们的长度不为零,它们就是 True ,就像 Python 列表一样。其他人可能希望只有当它的所有元素都为 True 时它才为 True 。其他人可能希望如果它的任何元素为 True,它就是 True 。

由于存在太多相互冲突的期望,NumPy 和 Pandas 的设计者拒绝猜测,而是引发 ValueError。

相反,您必须通过调用empty()all()any()方法明确地表明您想要哪种行为。

但是,在这种情况下,您似乎不需要布尔求值,而是需要逐元素逻辑与。这就是&二元运算符执行的操作:

(a['x']==1) & (a['y']==10)

返回一个布尔数组。


顺便说一句,正如alexpmil 指出的,括号是强制性的,因为它的运算符优先级比&更高。==

如果没有括号,

a['x']==1 & a['y']==10

将被评估为

a['x'] == (1 & a['y']) == 10

这又相当于链式比较

(a['x'] == (1 & a['y'])) and ((1 & a['y']) == 10)

这是形式为 的表达式Series and Series。使用and和两个系列将再次触发ValueError与上述相同的操作。这就是为什么括号是必需的。

解决方案 2:

TLDR: Pandas 中的逻辑运算符是&|~,并且括号(...)很重要!

Python 的andornot逻辑运算符旨在与标量配合使用。因此,Pandas 必须做得更好,并重写按位运算符以实现此功能的矢量化(逐元素)版本。

因此在 Python 中以下内容(其中exp1exp2是计算布尔结果的表达式)...

exp1 and exp2              # Logical AND
exp1 or exp2               # Logical OR
not exp1                   # Logical NOT

...将翻译为...

exp1 & exp2                # Element-wise logical AND
exp1 | exp2                # Element-wise logical OR
~exp1                      # Element-wise logical NOT

熊猫

如果在执行逻辑运算的过程中得到了ValueError,那么就需要使用括号进行分组:

(exp1) op (exp2)

例如,

(df['col1'] == x) & (df['col2'] == y) 

等等。


布尔索引:一种常见的操作是通过逻辑条件计算布尔掩码来过滤数据。Pandas 提供了三种运算符:&逻辑与、|逻辑或和~逻辑非。

考虑以下设置:

np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (5, 3)), columns=list('ABC'))
df

   A  B  C
0  5  0  3
1  3  7  9
2  3  5  2
3  4  7  6
4  8  8  1

逻辑与

对于df上述情况,假设您想要返回 A < 5 且 B > 5 的所有行。这是通过分别计算每个条件的掩码并对其进行 AND 运算来实现的。

重载&位运算符

在继续之前,请注意文档中的这一特定摘录,其中指出

另一个常见操作是使用布尔向量来过滤数据。运算符包括:|for or&forand~for not这些必须使用括号分组df.A > 2 & df.B < 3,因为默认情况下 Python 将评估诸如之类的表达式df.A > (2 & df.B) < 3,而所需的评估顺序是(df.A > 2) & (df.B < 3)

因此,考虑到这一点,可以使用按位运算符实现元素逻辑与&

df['A'] < 5

0    False
1     True
2     True
3     True
4    False
Name: A, dtype: bool

df['B'] > 5

0    False
1     True
2    False
3     True
4     True
Name: B, dtype: bool
(df['A'] < 5) & (df['B'] > 5)

0    False
1     True
2    False
3     True
4    False
dtype: bool

随后的过滤步骤很简单,

df[(df['A'] < 5) & (df['B'] > 5)]

   A  B  C
1  3  7  9
3  4  7  6

括号用于覆盖默认运算符优先级,其中按位运算符比比较运算符(<>)具有更高的优先级。

如果不使用括号,表达式的求值将不正确。例如,如果你不小心尝试了类似这样的操作

df['A'] < 5 & df['B'] > 5

它被解析为

df['A'] < (5 & df['B']) > 5

也就是说,

df['A'] < something_you_dont_want > 5

如下所示(参见 Python 文档中关于链式运算符的比较),

(df['A'] < something_you_dont_want) and (something_you_dont_want > 5)

也就是说,

# Both operands are Series...
something_else_you_dont_want1 and something_else_you_dont_want2

抛出

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

所以,不要犯这个错误!(我知道我在反复强调这一点,但请忍耐一下。这是一个非常非常常见初学者错误,必须非常彻底地解释。)

避免使用括号分组

修复其实很简单。大多数运算符都有相应的 DataFrames 绑定方法。如果使用函数而不是条件运算符构建单个掩码,则不再需要按括号分组来指定评估顺序:

df['A'].lt(5)
 
0     True
1     True
2     True
3     True
4    False
Name: A, dtype: bool

df['B'].gt(5)

0    False
1     True
2    False
3     True
4     True
Name: B, dtype: bool
df['A'].lt(5) & df['B'].gt(5)

0    False
1     True
2    False
3     True
4    False
dtype: bool

请参阅“灵活比较”部分。总而言之,我们有

╒════╤════════════╤════════════╕
│    │ Operator   │ Function   │
╞════╪════════════╪════════════╡
│  0 │ >          │ gt         │
├────┼────────────┼────────────┤
│  1 │ >=         │ ge         │
├────┼────────────┼────────────┤
│  2 │ <          │ lt         │
├────┼────────────┼────────────┤
│  3 │ <=         │ le         │
├────┼────────────┼────────────┤
│  4 │ ==         │ eq         │
├────┼────────────┼────────────┤
│  5 │ !=         │ ne         │
╘════╧════════════╧════════════╛

避免使用括号的另一种选择是使用DataFrame.query(或eval):

df.query('A < 5 and B > 5')

   A  B  C
1  3  7  9
3  4  7  6

我已经广泛地记录queryeval在Pandas 中根据公式动态评估表达式。

operator.and_

允许您以功能性方式执行此操作。内部调用Series.__and__与位运算符相对应的函数。

import operator 

operator.and_(df['A'] < 5, df['B'] > 5)
# Same as,
# (df['A'] < 5).__and__(df['B'] > 5) 

0    False
1     True
2    False
3     True
4    False
dtype: bool

df[operator.and_(df['A'] < 5, df['B'] > 5)]

   A  B  C
1  3  7  9
3  4  7  6

通常你不需要这个,但是了解它很有用。

概括:(np.logical_andlogical_and.reduce

另一种选择是使用 np.logical_and,它也不需要括号分组:

np.logical_and(df['A'] < 5, df['B'] > 5)

0    False
1     True
2    False
3     True
4    False
Name: A, dtype: bool

df[np.logical_and(df['A'] < 5, df['B'] > 5)]

   A  B  C
1  3  7  9
3  4  7  6

np.logical_and是一个ufunc(通用函数),大多数 ufunc 都有一个reduce方法。这意味着如果您有多个掩码要进行 AND,则更容易进行泛化logical_and。例如,要将掩码m1m2m3与进行 AND &,您必须执行

m1 & m2 & m3

然而,一个更简单的选择是

np.logical_and.reduce([m1, m2, m3])

它非常强大,因为它可以让你在此基础上构建更复杂的逻辑(例如,在列表推导中动态生成掩码并将它们全部添加):

import operator

cols = ['A', 'B']
ops = [np.less, np.greater]
values = [5, 5]

m = np.logical_and.reduce([op(df[c], v) for op, c, v in zip(ops, cols, values)])
m 
# array([False,  True, False,  True, False])

df[m]
   A  B  C
1  3  7  9
3  4  7  6

逻辑或

对于df上述情况,假设您想要返回 A == 3 或 B == 7 的所有行。

按位重载|

df['A'] == 3

0    False
1     True
2     True
3    False
4    False
Name: A, dtype: bool

df['B'] == 7
 
0    False
1     True
2    False
3     True
4    False
Name: B, dtype: bool
(df['A'] == 3) | (df['B'] == 7)

0    False
1     True
2     True
3     True
4    False
dtype: bool

df[(df['A'] == 3) | (df['B'] == 7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

如果您还没有,请阅读上面有关逻辑与的部分,所有警告都适用于此处。

或者,可以使用以下方式指定此操作

df[df['A'].eq(3) | df['B'].eq(7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

operator.or_

Series.__or__引擎盖下的呼叫。

operator.or_(df['A'] == 3, df['B'] == 7)
# Same as,
# (df['A'] == 3).__or__(df['B'] == 7)

0    False
1     True
2     True
3     True
4    False
dtype: bool

df[operator.or_(df['A'] == 3, df['B'] == 7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

np.logical_or

对于两个条件,使用logical_or

np.logical_or(df['A'] == 3, df['B'] == 7)

0    False
1     True
2     True
3     True
4    False
Name: A, dtype: bool

df[np.logical_or(df['A'] == 3, df['B'] == 7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

对于多个掩码,使用logical_or.reduce

np.logical_or.reduce([df['A'] == 3, df['B'] == 7])
# array([False,  True,  True,  True, False])

df[np.logical_or.reduce([df['A'] == 3, df['B'] == 7])]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

逻辑非

给定一个掩码,例如

mask = pd.Series([True, True, False])

如果您需要反转每个布尔值(以便最终结果为[False, False, True]),那么您可以使用以下任何一种方法。

按位~

~mask

0    False
1    False
2     True
dtype: bool

再次强调,表达式需要用括号括起来。

~(df['A'] == 3)

0     True
1    False
2    False
3     True
4     True
Name: A, dtype: bool

这在内部调用mask.__invert__(),但不要直接使用它。

operator.inv

内部调用__invert__该系列。

operator.inv(mask)

0    False
1    False
2     True
dtype: bool

np.logical_not

这是 numpy 变体。

np.logical_not(mask)

0    False
1    False
2     True
dtype: bool

注意,np.logical_and可以替代np.bitwise_andlogical_orbitwise_or, 和logical_not替代invert

解决方案 3:

Pandas 中布尔索引的逻辑运算符

重要的是要意识到您不能在或s 上使用任何 Python逻辑运算符andornot)(同样,您不能在具有多个元素的 s 上使用它们)。您不能使用这些的原因是因为它们隐式调用它们的操作数,这会引发异常,因为这些数据结构决定数组的布尔值是不明确的:pandas.Series`pandas.DataFramenumpy.arraybool`

>>> import numpy as np
>>> import pandas as pd
>>> arr = np.array([1,2,3])
>>> s = pd.Series([1,2,3])
>>> df = pd.DataFrame([1,2,3])
>>> bool(arr)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> bool(s)
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> bool(df)
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

我在回答“系列的真值不明确。请使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()”问答时更广泛地介绍了这一点。

NumPy 的逻辑函数

但是,NumPy提供了与这些运算符等效的元素操作,作为可以在numpy.arraypandas.Seriespandas.DataFrame或任何其他(符合)numpy.array子类上使用的函数:

  • andnp.logical_and

  • ornp.logical_or

  • notnp.logical_not

  • numpy.logical_xorPython 中没有对应的函数,但它是一个逻辑上的“排他或”运算

因此,本质上应该使用(假设df1df2是 Pandas DataFrames):

np.logical_and(df1, df2)
np.logical_or(df1, df2)
np.logical_not(df1)
np.logical_xor(df1, df2)

布尔值的按位函数和按位运算符

但是如果你有布尔 NumPy 数组、Pandas Series 或 Pandas DataFrames,你也可以使用元素位函数(对于布尔值,它们 - 或者至少应该是 - 与逻辑函数没有区别):

  • 按位与:np.bitwise_and&运算符

  • 按位或:np.bitwise_or|运算符

  • 按位非:(np.invert或别名np.bitwise_not)或~运算符

  • 按位异或:np.bitwise_xor^运算符

通常使用运算符。但是,当与比较运算符结合使用时,必须记住将比较括在括号中,因为按位运算符的优先级高于比较运算符:

(df1 < 10) | (df2 > 10)  # instead of the wrong df1 < 10 | df2 > 10

这可能会令人恼火,因为 Python 逻辑运算符的优先级低于比较运算符,所以您通常写a < 10 and b > 10(其中ab例如是简单的整数)而不需要括号。

逻辑运算和按位运算之间的差异(非布尔值)

需要强调的是,位运算和逻辑运算仅对布尔 NumPy 数组(以及布尔 Series 和 DataFrames)等效。如果这些数组不包含布尔值,则运算将产生不同的结果。我将使用 NumPy 数组提供示例,但对于 pandas 数据结构,结果将类似:

>>> import numpy as np
>>> a1 = np.array([0, 0, 1, 1])
>>> a2 = np.array([0, 1, 0, 1])

>>> np.logical_and(a1, a2)
array([False, False, False,  True])
>>> np.bitwise_and(a1, a2)
array([0, 0, 0, 1], dtype=int32)

并且由于 NumPy(以及类似的 Pandas)对布尔(布尔或“掩码”索引数组)和整数(索引数组)索引执行的操作不同,因此索引的结果也会不同:

>>> a3 = np.array([1, 2, 3, 4])

>>> a3[np.logical_and(a1, a2)]
array([4])
>>> a3[np.bitwise_and(a1, a2)]
array([1, 1, 1, 2])

摘要表

Logical operator | NumPy logical function | NumPy bitwise function | Bitwise operator
-------------------------------------------------------------------------------------
       and       |  np.logical_and        | np.bitwise_and         |        &
-------------------------------------------------------------------------------------
       or        |  np.logical_or         | np.bitwise_or          |        |
-------------------------------------------------------------------------------------
                 |  np.logical_xor        | np.bitwise_xor         |        ^
-------------------------------------------------------------------------------------
       not       |  np.logical_not        | np.invert              |        ~

逻辑运算符不适用于 NumPy 数组、Pandas Series 和 Pandas DataFrames。其他运算符适用于这些数据结构(和普通 Python 对象)并按元素工作。但是,在普通 Python 上进行按位反转时要小心,bool因为 bool 在此上下文中将被解释为整数(例如~Falsereturns-1~Truereturns -2)。

解决方案 4:

请注意,您还可以使用以下*方法and

   In [12]: np.all([a > 20, a < 40], axis=0)
   Out[12]:
   array([[False,  True, False, False,  True],
          [False, False, False, False, False],
          [ True,  True, False, False, False],
          [False,  True, False, False, False],
          [False,  True, False, False, False]])

   In [13]: (a > 20) * (a < 40)
   Out[13]:
   array([[False,  True, False, False,  True],
          [False, False, False, False, False],
          [ True,  True, False, False, False],
          [False,  True, False, False, False],
          [False,  True, False, False, False]])

我并不是说这比使用np.all或更好|。但它确实有效。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用