Оценка значений серии 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

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


person neanderslob    schedule 04.05.2014    source источник
comment
Это вопрос не о словарях, а об объектах Pandas Series.   -  person Daniel Roseman    schedule 05.05.2014
comment
Похоже, это связано с используемой вами библиотекой pandas. Похоже, что pandas предоставляет объекты, которые действуют как словари, но имеют важные отличия. Чтобы было ясно, здесь вы имеете дело не с обычными словарями Python, а используете структуру данных, предоставляемую пандами, которая имеет синтаксис, подобный словарю.   -  person Greg Hewgill    schedule 05.05.2014
comment
Вы даете серию, а не словарь, поэтому он не может оценить ваш логический запрос, например, ошибка предполагает, что вам нужно сделать pnl[company].tail(1)['Active'].any()==1, хотя это все еще одно значение   -  person EdChum    schedule 05.05.2014
comment
Что касается вашего второго вопроса, вы путаете порядковый выходной номер? поэтому, если вы просто неоднократно печатали yay или print (yay) (для python 3), число все еще увеличивается   -  person EdChum    schedule 05.05.2014
comment
@GregHewgill спасибо за понимание. Просто чтобы я знал, что является ключевым признаком того, что это на самом деле не словарь?   -  person neanderslob    schedule 05.05.2014
comment
@neanderslob: Тот факт, что вы получаете ошибки, такие как The truth value of a Series is ambiguous, из исходных файлов библиотеки pandas.   -  person Greg Hewgill    schedule 05.05.2014
comment
Вы можете просто сделать print(type(pnl[company].tail(1)['Active']))   -  person EdChum    schedule 05.05.2014
comment
(Помечено и переименовано, чтобы добавить 'логическое выражение серии pandas' и удалить 'словарь')   -  person smci    schedule 05.05.2014
comment
@EdChum Что касается вашего вопроса о возможности порядкового номера вывода. Мне было интересно то же самое, но когда я просто делаю print yay, он не увеличивается. Вот результат, если вам интересно.   -  person neanderslob    schedule 05.05.2014
comment
Понятия не имею, может быть, вариант печати панд, с которым я никогда не сталкивался   -  person EdChum    schedule 05.05.2014
comment
@EdChum Ха, может быть и так, хотя; вроде ничего не болит. Спасибо еще раз за помощь.   -  person neanderslob    schedule 05.05.2014


Ответы (3)


То, что вы получаете, является объектом серии Pandas, и его нельзя оценить так, как вы пытаетесь, даже если это всего лишь одно значение, на которое вам нужно изменить свою строку:

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

person EdChum    schedule 04.05.2014
comment
Как всегда, спасибо за вашу помощь! Я попробовал решение, и оно действительно избавляет от ошибки. Тем не менее, я все еще не могу заставить его признать значение. Независимо от значения, я не могу заставить его напечатать «yay». Вывод см. здесь Та же ситуация применима к моей проверке работоспособности в вопросе выше. Любая идея, почему это происходит? - person neanderslob; 05.05.2014
comment
Похоже, что строка if pnl[company].tail(1)['Active'].any()=='0' работает? вы хотите это как строку или как int/float? - person EdChum; 05.05.2014
comment
@EdChum: .any() вернет True или False. Я не уверен, что сравнение возвращаемого значения any с 1 - это то, что действительно имеет в виду ОП... - person unutbu; 05.05.2014
comment
@unutbu Я не замечаю, что когда я вызываю df.tail(1)['col'].any(), он возвращает значение, сравнение дает True или False - person EdChum; 05.05.2014
comment
Чтобы позаимствовать вывод оператора if: Ура (...=='0' работало), я бы предпочел работать с ним как с целым числом, чтобы ответить на ваш предыдущий комментарий. Я предполагаю, что есть трюк для этого? - person neanderslob; 05.05.2014
comment
Либо исправьте, однако вы читаете данные в первую очередь, либо сделайте это pnl['Company']['Active'] = pnl['Company']['Active'].astype(int) - person EdChum; 05.05.2014
comment
как шарм, высоко ценится - person neanderslob; 05.05.2014
comment
@EdChum: Да, ты прав. При определенных условиях .any() может возвращать что-то отличное от True или False. - person unutbu; 05.05.2014
comment
@unutbu Я не видел ситуации, в которой я бы не ожидал, что это так, плюс, если бы это не произошло, сообщение об ошибке не было бы очень полезным, поскольку оно предлагает это как способ избежать двусмысленного вызова. - person EdChum; 05.05.2014
comment
Я думаю, что обычным способом было бы: if (pnl[company].tail(1)['Active']==1).any():. Может быть, это только я, но я никогда не полагаюсь на то, что any возвращает что-либо, кроме True или False. - person unutbu; 05.05.2014

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

Причина, по которой NDFrame не имеет логического значения (ошибка, то есть всегда вызывает 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:

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

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

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

person unutbu    schedule 04.05.2014

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

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

Есть много ограничений, причуд и ошибок (примеры ниже) - лучший совет - не доверять логическому значению как гражданину первого класса в пандах из-за ограничений 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
person smci    schedule 04.05.2014
comment
Команда pandas отлично работает над исправлениями/улучшениями, поэтому сообщайте о любых исправлениях/улучшениях/gotchas/docbugs к ним. Кроме того, поваренная книга и блоги очень нужны. - person smci; 05.05.2014
comment
поваренная книга существует уже давно: pandas-docs.github.io/pandas -docs-travis/cookbook.html - person Jeff; 05.05.2014