使用逻辑表达式和if语句评估pandas系列值

14

我在使用 if 语句时遇到了从字典中获取值的问题。

以下是一个字典示例,它是从数据框中导入的(如果有影响):

>>> pnl[company]
29:   Active Credit       Date   Debit Strike Type
0      1      0 2013-01-08  2.3265  21.15  Put
1      0      0 2012-11-26      40     80  Put
2      0      0 2012-11-26     400     80  Put

我尝试评估以下语句以确定 Active 的最终值:

if pnl[company].tail(1)['Active']==1:
    print 'yay'

然而,我遇到了以下错误信息:

Traceback (most recent call last):
  File "<pyshell#69>", line 1, in <module>
    if pnl[company].tail(1)['Active']==1:
  File "/usr/lib/python2.7/dist-packages/pandas/core/generic.py", line 676, in __nonzero__
    .format(self.__class__.__name__))
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

这让我感到惊讶,因为我可以使用上述命令显示我想要的值,而不需要 if 语句:

>>> pnl[company].tail(1)['Active']
30: 2    0
Name: Active, dtype: object

考虑到数值明显为零,索引为2,我进行了以下简要的健全性检查,并发现事情并不像我预期的那样发生:

>>> if pnl[company]['Active'][2]==0:
...     print 'woo-hoo'
... else:
...     print 'doh'


doh

我的问题是:

1)这里可能发生了什么?我怀疑我在某个基本层面上误解了字典。

2)我注意到,当我调用该字典的任何给定值时,左侧的数字会增加1。这代表什么?例如:

>>> pnl[company].tail(1)['Active']
31: 2    0
Name: Active, dtype: object
>>> pnl[company].tail(1)['Active']
32: 2    0
Name: Active, dtype: object
>>> pnl[company].tail(1)['Active']
33: 2    0
Name: Active, dtype: object
>>> pnl[company].tail(1)['Active']
34: 2    0
Name: Active, dtype: object

提前感谢任何帮助。


7
这不是关于字典的问题,而是关于 Pandas 的 Series 对象的问题。 - Daniel Roseman
1
这似乎是您正在使用的pandas库中特定的内容。看起来,pandas提供了一些类似于字典的对象,但在重要方面有所不同。明确一点,您在这里处理的不是通常的Python字典,而是使用pandas提供的具有类似字典语法的数据结构。 - Greg Hewgill
2
你产生的是一个序列而不是字典,因此它无法评估你的布尔查询。就像错误提示一样,你需要执行 pnl[company].tail(1)['Active'].any()==1,即使这仍然是单个值。 - EdChum
1
@neanderslob:从pandas库源文件中出现“Series的真值是模糊的”等错误信息。 - Greg Hewgill
@EdChum 嗯,也许是这样,不过没关系;似乎并没有造成任何影响。再次感谢您的帮助。 - neanderslob
显示剩余6条评论
3个回答

7
您得到的是一个Pandas Series对象,不能按照您尝试的方式进行评估,即使它只是一个需要更改的单个值,您也需要更改您的代码行为:
if pnl[company].tail(1)['Active'].any()==1:
  print 'yay'

关于你的第二个问题,请参考我的评论。

编辑

从评论和链接到你的输出的内容来看,调用any()修复了错误信息,但是你的数据实际上是字符串,因此比较仍然失败,你可以选择:

if pnl[company].tail(1)['Active'].any()=='1':
  print 'yay'

要进行字符串比较,或修复读取或生成的数据。

或者执行以下操作:

pnl['Company']['Active'] = pnl['Company']['Active'].astype(int)

为了使比较更准确,需要转换列的dtype


1
一如既往,感谢您的帮助!我尝试了解决方案,它确实消除了错误。但是,我仍然无法让它认可该值。无论该值为何,我都无法打印“yay”。在此处查看输出同样的情况也适用于我上面的“理智检查”。有任何想法为什么会发生这种情况吗? - neanderslob
1
这看起来像是一个字符串 if pnl[company].tail(1)['Active'].any()=='0',你想要它作为字符串还是整数/浮点数? - EdChum
从if语句输出中借鉴:“Yay”(...=='0'有效),实际上我更喜欢将其作为int处理,以回答您之前的评论。我猜这方面有什么技巧吗? - neanderslob
1
要么修复您最初读取数据的方式,要么执行此操作 pnl['Company']['Active'] = pnl['Company']['Active'].astype(int) - EdChum
我认为通常的方法应该是:if (pnl[company].tail(1)['Active']==1).any():。也许只有我这样,但我从不依赖于any返回除True或False之外的任何内容。 - unutbu
显示剩余5条评论

4
一系列是NDFrame的子类。 NDFrame的__bool__方法 总是引发ValueError。因此,即使Series只有一个值,在布尔上下文中尝试评估Series也会引发ValueError。
NDFrames没有布尔值(即总是引发ValueError)的原因是因为存在多个可能的标准可以合理地期望NDFrame为True。它可以表示:
1. NDFrame中的每个项都为True,或者(如果是这样,请使用.all()) 2. NDFrame中的任何项都为True,或者(如果是这样,请使用Series.any()) 3. NDFrame不为空(如果是这样,请使用.empty())
由于两者都是可能的,并且不同的用户具有不同的期望,开发人员拒绝猜测,而要求NDFrame的用户明确说明他们希望使用哪个标准。
错误消息列出了最有可能的选择:

使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()

由于在您的情况下,您知道该系列将仅包含一个值,因此您可以使用 item

if pnl[company].tail(1)['Active'].item() == 1:
    print 'yay'

关于您的第二个问题:左侧的数字似乎是由您的Python解释器(PyShell?)产生的行号 - 但这只是我的猜测。

警告:可能是由于编程错误,

if pnl[company].tail(1)['Active']==1:

意味着当Series中的单个值等于1时,您希望条件为True。 代码如下:

if pnl[company].tail(1)['Active'].any()==1:
    print 'yay'

如果Series的数据类型为数字且Series中的值为任何非零数字,则将为True。例如,如果我们将pnl [company] .tail(1)['Active'] 等同于
In [128]: s = pd.Series([2], index=[2])

那么

In [129]: s.any()
Out[129]: True

因此,

In [130]: s.any()==1
Out[130]: True

我认为s.item() == 1更加忠实地保留了您的意图:

In [132]: s.item()==1
Out[132]: False

(s == 1).any()也可以使用,但使用any不能很明显地表达您的意图,因为您知道Series只包含一个值。


0

你的问题与Python字典或原生Python没有任何关系。这是关于pandas Series的问题,其他答案已经给出了正确的语法:

从更广泛的意义上解释你的问题,它涉及到如何将pandas Series嵌入到NumPy中,而NumPy直到最近都对逻辑值和运算符支持不佳。pandas尽其所能利用NumPy提供的功能。有时需要手动调用numpy逻辑函数,而不是仅使用任意(Python)运算符编写代码,这很繁琐、笨拙,有时会使pandas代码膨胀。此外,为了性能,你经常需要这样做(numpy比从本地Python传输更好)。但这就是我们付出的代价。

存在许多限制、怪癖和陷阱(下面有例子)- 最好的建议是对pandas中的布尔类型持怀疑态度,因为numpy的限制:

.

import numpy as np
import pandas as pd
s = pd.Series([True, True, False, True, np.NaN])
s2  = pd.Series([True, True, False, True, np.NaN])
dir(s) # look at .all, .any, .bool, .eq, .equals, .invert, .isnull, .value_counts() ...

s.astype(bool) # WRONG: should use the member s.bool ; no parentheses, it's a member, not a function
# 0     True
# 1     True
# 2    False
# 3     True
# 4     True  # <--- should be NA!!
#dtype: bool

s.bool
# <bound method Series.bool of
# 0     True
# 1     True
# 2    False
# 3     True
# 4      NaN
# dtype: object>

# Limitation: value_counts() currently excludes NAs
s.value_counts()
# True     3
# False    1
# dtype: int64
help(s.value_counts) # "... Excludes NA values(!)"

# Equality comparison - vector - fails on NAs, again there's no NA-handling option):
s == s2 # or equivalently, s.eq(s2)
# 0     True
# 1     True
# 2     True
# 3     True
# 4    False  # BUG/LIMITATION: we should be able to choose NA==NA
# dtype: bool

# ...but the scalar equality comparison says they are equal!!
s.equals(s2)
# True

Pandas团队在修复/增强方面做得非常出色,因此请向他们报告任何修复/增强/注意事项/文档错误。此外,需要非常需要食谱和博客。 - smci
食谱已经存在一段时间了:http://pandas-docs.github.io/pandas-docs-travis/cookbook.html - Jeff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接