Оценка значений серии pandas с помощью логических выражений и if-операторов

У меня возникли проблемы с оценкой значений из словаря с использованием операторов 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

Заранее благодарим за помощь.

Ответ 1

То, что вы получаете, является объектом 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 столбца, чтобы ваше сравнение было более правильным.

Ответ 2

Серия A является подклассом NDFrame. Метод NDFrame.__bool__ всегда вызывает ValueError. Таким образом, попытка оценить серию в булевом контексте вызывает значение ValueError - даже если у Серии есть только одно значение.

Причина, по которой NDFrames не имеет логического значения (err, т.е. всегда повышает значение ValueError), объясняется тем, что существует более чем один возможный критерий, который можно было бы ожидать от NDFrame. Это может означать

  • каждый элемент в NDFrame имеет значение True, или (если это так, используйте .all())
  • любой элемент в NDFrame имеет значение True, или (если это так, используйте Series.any())
  • 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:

означает, что вы хотите, чтобы условие было истинным, когда одно значение в серии равно 1. Код

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

будет Истиной, если dtype Series является числовым, а значение в Серии любым числом, отличным от 0. Например, если взять 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 не выражает ваше намерение очень просто, так как вы знаете, что серия будет содержать только одно значение.

Ответ 3

Ваш вопрос не имеет ничего общего с словарями Python или родным Python. Он о pandas Series, а другие ответы дали вам правильный синтаксис:

Интерпретация ваших вопросов в более широком смысле, о том, как pandas Series был привязан к NumPy, и NumPy исторически до недавнего времени имел известную плохую поддержку для логические значения и операторы. pandas делает лучшую работу, которую он может с помощью NumPy. Иногда для ручного вызова numpy логических функций вместо написания кода с произвольными (Python) операторами является раздражающим и неуклюжим, а иногда и раздутым кодом pandas. Кроме того, вам часто приходится это делать для производительности (numpy лучше, чем thunking и из родного Python). Но это цена, которую мы платим.

Существует множество ограничений, причуд и gotchas (примеры ниже). Лучшим советом является недоверие к булевому как первоклассному гражданину в 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 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 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