数列的真值是不明确的。使用a.empty、a.bool()、a.item()、a.any()或a.all()

我想使用or条件过滤我的数据框,以保留具有超出[-0.25, 0.25]范围的特定列值的行。我尝试了:

df = df[(df['col'] < -0.25) or (df['col'] > 0.25)]

但我得到了错误:

数列的真值是不明确的。使用a.empty、a.bool()、a.item()、a.any()或a.all()

1878217 次浏览

orand python语句需要truth值。对于pandas,这些被认为是模棱两可的,因此您应该使用“按位”|(或)或&(和)操作:

df = df[(df['col'] < -0.25) | (df['col'] > 0.25)]

对于这些类型的数据结构,它们被重载以产生元素明智的orand


只是为了给这个说法增加一些解释:

当您想获取pandas.Seriesbool时,会引发异常:

>>> import pandas as pd
>>> x = pd.Series([1])
>>> bool(x)
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

你点击的是操作符隐含将操作数转换为bool的地方(你使用or,但它也发生在andifwhile):

>>> x or x
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> x and x
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> if x:
...     print('fun')
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> while x:
...     print('fun')
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

除了这4个语句之外,还有几个python函数隐藏了一些bool调用(如anyallfilter…),这些通常与pandas.Series没有问题,但为了完整性,我想提到这些。


在你的情况下,异常并没有真正的帮助,因为它没有提到正确的选择。对于andor,如果你想要进行元素比较,你可以使用:

  • numpy.logical_or

      >>> import numpy as np
    >>> np.logical_or(x, y)
    

    或者简单地|运算符:

      >>> x | y
    
  • numpy.logical_and

      >>> np.logical_and(x, y)
    

    或者简单地&运算符:

      >>> x & y
    

如果您使用的是运算符,那么请务必正确设置括号,因为运算符优先级

几个逻辑numpy函数,其中应该pandas.Series上工作。


如果您在执行ifwhile时遇到异常中提到的替代方案更适合。我将很快解释其中的每一个:

  • 如果您想检查您的系列是否为

      >>> x = pd.Series([])
    >>> x.empty
    True
    >>> x = pd.Series([1])
    >>> x.empty
    False
    

    如果没有明确的布尔值解释,Python通常会将容器的length(如listtuple,…)解释为真值。所以如果你想要类似python的检查,你可以执行:if x.sizeif not x.empty而不是if x

  • 如果您的Series包含一个而且只有一个布尔值:

      >>> x = pd.Series([100])
    >>> (x > 50).bool()
    True
    >>> (x < 50).bool()
    False
    
  • 如果您想检查系列的第一个也是唯一的项目(如.bool(),但即使不是布尔内容也可以工作):

      >>> x = pd.Series([100])
    >>> x.item()
    100
    
  • 如果要检查所有任何项是否为非零、非空或非False:

      >>> x = pd.Series([0, 1, 2])
    >>> x.all()   # because one element is zero
    False
    >>> x.any()   # because one (or more) elements are non-zero
    True
    

对于布尔逻辑,使用&|

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


>>> df
A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
2  0.950088 -0.151357 -0.103219
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.443863


>>> df.loc[(df.C > 0.25) | (df.C < -0.25)]
A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.443863

要查看发生了什么,您将为每个比较获得一列布尔值,例如。

df.C > 0.25
0     True
1    False
2    False
3     True
4     True
Name: C, dtype: bool

当您有多个条件时,您将返回多个列。这就是连接逻辑不明确的原因。使用andor将单独处理每列,因此您首先需要将该列减少为单个布尔值。例如,查看每个列中的任何值或所有值是否为True。

# Any value in either column is True?
(df.C > 0.25).any() or (df.C < -0.25).any()
True


# All values in either column is True?
(df.C > 0.25).all() or (df.C < -0.25).all()
False

实现同样的事情的一种复杂方法是将所有这些列压缩在一起,并执行适当的逻辑。

>>> df[[any([a, b]) for a, b in zip(df.C > 0.25, df.C < -0.25)]]
A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.443863

有关更多详细信息,请参阅文档中的布尔索引

或者,您可以使用运算符模块。更详细的信息在这里python文档

import operator
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,3), columns=list('ABC'))
df.loc[operator.or_(df.C > 0.25, df.C < -0.25)]


A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.4438

这个优秀的答案很好地解释了正在发生的事情并提供了一个解决方案。我想添加另一个可能适用于类似情况的解决方案:使用query方法:

df = df.query("(col > 0.25) or (col < -0.25)")

请参阅http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-query

(我目前正在使用的数据帧的一些测试表明,这种方法比在一系列布尔值上使用按位运算符慢一点:2 ms vs.870µs)

一个警告:至少有一种情况是,列名恰好是python表达式。我有名为WT_38hph_IP_2WT_38hph_input_2log2(WT_38hph_IP_2/WT_38hph_input_2)的列,并想执行以下查询:"(log2(WT_38hph_IP_2/WT_38hph_input_2) > 1) and (WT_38hph_IP_2 > 20)"

我获得了以下异常级联:

  • KeyError: 'log2'
  • UndefinedVariableError: name 'log2' is not defined
  • ValueError: "log2" is not a supported function

我猜发生这种情况是因为查询解析器试图从前两列中创建一些东西,而不是使用第三列的名称来识别表达式。

提出了一种可能的解决方法这里

熊猫使用按位&|,每个条件都应该包装在()

例如以下作品

data_query = data[(data['year'] >= 2005) & (data['year'] <= 2010)]

但是没有适当括号的相同查询不会

data_query = data[(data['year'] >= 2005 & data['year'] <= 2010)]

我遇到了同样的错误,并用pyspark数据框停顿了几天,我能够通过用0填充na值来成功解决它,因为我正在比较来自2个字段的整数值。

你需要在熊猫中使用按位运算符|而不是or&而不是and,你不能简单地使用python中的bool语句。

对于非常复杂的过滤,创建一个mask并在数据框上应用掩码。
将所有查询放入掩码中并应用它。
假设,

mask = (df["col1"]>=df["col2"]) & (stock["col1"]<=df["col2"])
df_new = df[mask]

一件小事,浪费了我的时间。

将条件(如果使用“=”,“; ! = ";进行比较)放在括号中,否则也会引发此异常。 这样就可以了

df[(some condition) conditional operator (some conditions)]

这不会

df[some condition conditional-operator some condition]

我将尝试给出三种最常见方式的基准(也在上面提到):

from timeit import repeat


setup = """
import numpy as np;
import random;
x = np.linspace(0,100);
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
"""
stmts = 'x[(x > lb) * (x <= ub)]', 'x[(x > lb) & (x <= ub)]', 'x[np.logical_and(x > lb, x <= ub)]'


for _ in range(3):
for stmt in stmts:
t = min(repeat(stmt, setup, number=100_000))
print('%.4f' % t, stmt)
print()

结果:

0.4808 x[(x > lb) * (x <= ub)]
0.4726 x[(x > lb) & (x <= ub)]
0.4904 x[np.logical_and(x > lb, x <= ub)]


0.4725 x[(x > lb) * (x <= ub)]
0.4806 x[(x > lb) & (x <= ub)]
0.5002 x[np.logical_and(x > lb, x <= ub)]


0.4781 x[(x > lb) * (x <= ub)]
0.4336 x[(x > lb) & (x <= ub)]
0.4974 x[np.logical_and(x > lb, x <= ub)]

但是,熊猫系列不支持*,NumPy Array比熊猫数据帧快(环绕速度慢1000倍,见数字):

from timeit import repeat


setup = """
import numpy as np;
import random;
import pandas as pd;
x = pd.DataFrame(np.linspace(0,100));
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
"""
stmts = 'x[(x > lb) & (x <= ub)]', 'x[np.logical_and(x > lb, x <= ub)]'


for _ in range(3):
for stmt in stmts:
t = min(repeat(stmt, setup, number=100))
print('%.4f' % t, stmt)
print()

结果:

0.1964 x[(x > lb) & (x <= ub)]
0.1992 x[np.logical_and(x > lb, x <= ub)]


0.2018 x[(x > lb) & (x <= ub)]
0.1838 x[np.logical_and(x > lb, x <= ub)]


0.1871 x[(x > lb) & (x <= ub)]
0.1883 x[np.logical_and(x > lb, x <= ub)]

注意:添加一行代码x = x.to_numpy()将需要大约20µs。

对于喜欢%timeit的人:

import numpy as np
import random
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
lb, ub
x = pd.DataFrame(np.linspace(0,100))


def asterik(x):
x = x.to_numpy()
return x[(x > lb) * (x <= ub)]


def and_symbol(x):
x = x.to_numpy()
return x[(x > lb) & (x <= ub)]


def numpy_logical(x):
x = x.to_numpy()
return x[np.logical_and(x > lb, x <= ub)]


for i in range(3):
%timeit asterik(x)
%timeit and_symbol(x)
%timeit numpy_logical(x)
print('\n')

结果:

23 µs ± 3.62 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
35.6 µs ± 9.53 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
31.3 µs ± 8.9 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)




21.4 µs ± 3.35 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
21.9 µs ± 1.02 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
21.7 µs ± 500 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)




25.1 µs ± 3.71 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
36.8 µs ± 18.3 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
28.2 µs ± 5.97 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

我在这个命令中遇到错误:

if df != '':
pass

但当我把它改成这样时,它起作用了:

if df is not '':
pass

如果你有一个以上的值:

df['col'].all()

如果只有一个值:

df['col'].item()

这是初学者在Pandas中创建多个条件时的一个常见问题。一般来说,有两种可能的条件导致此错误:

条件1:Python运算符优先级

有一段布尔索引|索引和选择数据-熊猫留档解释了这一点

另一个常见的操作是使用布尔向量来过滤数据。运算符是:|代表or&代表and~代表not。这些必须通过使用括号进行分组。

默认情况下,Python将评估一个表达式,例如df['A'] > 2 & df['B'] < 3作为df['A'] > (2 & df['B']) < 3,而所需的评估顺序是(df['A'] > 2) & (df['B'] < 3)

# Wrong
df['col'] < -0.25 | df['col'] > 0.25


# Right
(df['col'] < -0.25) | (df['col'] > 0.25)

有一些可能的方法可以去掉括号,我稍后会介绍这个。


条件2:操作符/语句不当

如前所述,您需要使用|表示or&表示and~表示not

# Wrong
(df['col'] < -0.25) or (df['col'] > 0.25)


# Right
(df['col'] < -0.25) | (df['col'] > 0.25)

另一种可能的情况是您在if语句中使用布尔系列。

# Wrong
if pd.Series([True, False]):
pass

很明显,Pythonif语句接受类似布尔的表达式而不是Pandas Series。您应该使用pandas.Series.any或错误消息中列出的方法将Series转换为您需要的值。

例如:

# Right
if df['col'].eq(0).all():
# If you want all column values equal to zero
print('do something')


# Right
if df['col'].eq(0).any():
# If you want at least one column value equal to zero
print('do something')

让我们来谈谈如何在第一种情况下避开括号。

  1. 使用Pandas数学函数

Pandas定义了很多数学函数,包括如下比较:

因此,您可以使用

df = df[(df['col'] < -0.25) | (df['col'] > 0.25)]


# is equal to


df = df[df['col'].lt(-0.25) | df['col'].gt(0.25)]
  1. 使用pandas.Series.between()

如果要在两个值之间选择行,可以使用pandas.Series.between

  • df['col].between(left, right)等于
    (left <= df['col']) & (df['col'] <= right)
  • df['col].between(left, right, inclusive='left)等于
    (left <= df['col']) & (df['col'] < right)
  • df['col].between(left, right, inclusive='right')等于
    (left < df['col']) & (df['col'] <= right)
  • df['col].between(left, right, inclusive='neither')等于
    (left < df['col']) & (df['col'] < right)
df = df[(df['col'] > -0.25) & (df['col'] < 0.25)]


# is equal to


df = df[df['col'].between(-0.25, 0.25, inclusive='neither')]
  1. 使用pandas.DataFrame.query()

之前引用的文档有一章query()方法很好地解释了这一点。

pandas.DataFrame.query()可以帮助您选择带有条件字符串的DataFrame。在查询字符串中,您可以使用按位运算符(&|)及其布尔表兄弟(andor)。此外,您可以省略括号,但出于可读性原因,我不建议这样做。

df = df[(df['col'] < -0.25) | (df['col'] > 0.25)]


# is equal to


df = df.query('col < -0.25 or col > 0.25')
  1. 使用pandas.DataFrame.eval()

pandas.DataFrame.eval()评估描述DataFrame列操作的字符串。因此,我们可以使用此方法来构建我们的多重条件。语法与pandas.DataFrame.query()相同。

df = df[(df['col'] < -0.25) | (df['col'] > 0.25)]


# is equal to


df = df[df.eval('col < -0.25 or col > 0.25')]

pandas.DataFrame.query()pandas.DataFrame.eval()可以做的事情比我在这里描述的更多,建议您阅读他们的留档并与他们一起玩。

我在Panda数据框架中工作时也遇到过同样的问题。

我用过:numpy.logical_and

在这里,我试图选择ID与41d7853匹配的行,degreee_type与Certification不匹配。

如下所示:

display(df_degrees.loc[np.logical_and(df_degrees['person_id'] == '41d7853' , df_degrees['degree_type'] !='Certification')])

如果我尝试编写如下代码:

display(df_degrees.loc[df_degrees['person_id'] == '41d7853' and df_degrees['degree_type'] !='Certification'])

我们将得到错误:

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

我使用了numpy.logical_and,它对我有用。