3-МИНУТНЫЕ ПАНДЫ

Нарезка кадра данных Pandas с использованием «&» и «|» Вместо «и» и «или»

когда вы видите ValueError: истинное значение серии неоднозначно. Используйте a.empty, a.bool(), a.item(), a.any() или a.all()

Фильтрация/нарезка данных — это повседневная задача, если вы работаете с данными.

Общая идея нарезки данных заключается в выборе строк, значения столбцов которых соответствуют определенным критериям. Например, выберите строки, значение второго столбца которых меньше 3, строки, значение третьего столбца которых находится в предварительно определенном списке, строки, значение пятого столбца которых начинается с ABC и т. д. (см. этот пост для как делать нарезку в деталях).

Если вы делаете нарезку данных в библиотеке Python pandasи комбинируете несколько критериев по andили orоператорам, вы наверняка сталкивались с этим,

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

Вот пример. Давайте создадим фрейм данных в pandas,

import pandas as pd
import numpy as np

data = {'name': ['John', 'Emily', 'David', 'Samantha', 'Michael', 'Amy', 'Mark', 'Jessica', 'Lucas', 'Maria'],
        'age': [25, 32, 19, 41, 28, 36, 24, 27, 30, 39],
        'city': ['New York', 'Paris', 'London', 'Tokyo', 'Los Angeles', 'Sydney', 'Toronto', 'Paris', 'Berlin', 'Rio de Janeiro'],
        'is_student': [True, False, True, False, False, True, True, False, False, True],
        'gpa': [3.7, 4.0, 3.2, 3.9, 3.5, 3.8, 3.1, 3.6, 3.3, 3.9],
        'income': [45000, 72000, 28000, 98000, 55000, 82000, 32000, 68000, 42000, 60000]}

# create the pandas DataFrame
df = pd.DataFrame(data)

Фрейм данных выглядит так,

Затем с помощью «и» мы попытались выбрать людей, которым не больше 25 лет, но которые зарабатывают не менее 50 000 в год.

df[(df['age']<=30) and (df['income']>=50000)]

Вот что я получил,

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/var/folders/13/qmh5_52s3m72pbywgbf08_ch0000gn/T/ipykernel_50721/396533384.py in <module>
----> 1 df[(df['age']<=30) and (df['income']>=50000)]

~/opt/anaconda3/lib/python3.9/site-packages/pandas/core/generic.py in __nonzero__(self)
   1525     @final
   1526     def __nonzero__(self):
-> 1527         raise ValueError(
   1528             f"The truth value of a {type(self).__name__} is ambiguous. "
   1529             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."

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

Я использовал and, чтобы объединить два критерия вместе, чтобы получить строки, соответствующие обоим критериям, из фрейма данных.

Я получаю эту «ошибку значения», потому что неоднозначно рассматривать объект серии pandas как логический объект, даже если эта серия pandas состоит из логических значений.

В частности, в выражении `(df['age']‹=30) и (df['income']›=50000)` Python пытался преобразовать логическую серию pandas `df['age']‹=30` и `df['income']›=50000` сначала в одно логическое значение.

Это неоднозначно, потому что Python не знает, должен ли он присвоить его True только в том случае, если все значения равны True или если какое-либо из значений равно True . Таким образом, это привело к ошибке значения.

Решить такую ​​проблему очень просто, и ключ в том, чтобы избежать преобразования серии pandas в одно логическое значение.

Избегайте преобразования pandas.Series() в bool

В приведенном выше примере нам действительно нужно поэлементно and между двумя сериями панд. Вот так.

Самое простое решение — изменить and на &.

df[(df['age']<=30) & (df['income']>=50000)]

который дает,

То же самое для операции or.

Например, мы хотим отобрать лиц, которые еще являются студентами или моложе 25 лет.

Использование or приведет к той же ошибке значения, что и использование and ,

df[(df['age']<=25) or df['is_student']]
df[(df['age']<=25) or df['is_student']]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/var/folders/13/qmh5_52s3m72pbywgbf08_ch0000gn/T/ipykernel_50721/1315814170.py in <module>
----> 1 df[(df['age']<=25) or df['is_student']]

~/opt/anaconda3/lib/python3.9/site-packages/pandas/core/generic.py in __nonzero__(self)
   1525     @final
   1526     def __nonzero__(self):
-> 1527         raise ValueError(
   1528             f"The truth value of a {type(self).__name__} is ambiguous. "
   1529             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."

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

Вместо этого мы должны сделать это,

df[(df['age']<=25) | df['is_student']]

который дает,

Конечно, мы также можем использовать функции NumPy для выполнения той же операции, потому что серия pandas построена на массивах NumPy.

df[np.logical_and(df['age']<=30,df['income']>=50000)]

что дает нам тот же результат, что и использование & ,

И аналогично для операции or,

df[np.logical_or(df['age']<=25, df['is_student'])]

который дает,

Чтобы коды выглядели аккуратно, я рекомендую использовать & и | для нарезки данных.

Избегайте цепных сравнений

Иногда вы можете запутаться, когда используете & и |, но все равно получаете те же ошибки.

Высока вероятность, что вы сделали цепные сравнения.

Например, используя те же данные выше, мы хотим получить людей в возрасте от 20 до 25 лет и со средним баллом не менее 3,0.

df[(20 < df['age'] < 25) &
  (df['gpa'] >= 3)]

который дает,

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/var/folders/13/qmh5_52s3m72pbywgbf08_ch0000gn/T/ipykernel_50721/1837177779.py in <module>
----> 1 df[(20 < df['age'] < 25) &
      2   (df['gpa'] >= 3)]

~/opt/anaconda3/lib/python3.9/site-packages/pandas/core/generic.py in __nonzero__(self)
   1525     @final
   1526     def __nonzero__(self):
-> 1527         raise ValueError(
   1528             f"The truth value of a {type(self).__name__} is ambiguous. "
   1529             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."

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

Не путайте эту ошибку с теми, что мы видим выше. Несмотря на то, что сообщения об ошибках те же, причина больше не в операции &. Речь идет о связанных сравнениях в 20<df['age']<25 .

К сожалению, при нарезке данных pandas не разрешено выполнять цепные сравнения.

Поэтому связанные сравнения должны быть разделены на два автономных сравнения.

df[(20 < df['age']) &
   (df['age']< 25) &
  (df['gpa'] >= 3)]

Теперь мы получаем правильный результат,

И не забудьте использовать круглые скобки для каждого критерия, который вы хотите использовать & или | для объединения, иначе операторы & и | получат более высокий приоритет для выполнения, чем сравнения.

Например,

df[20 < df['age'] &
   df['age']< 25 &
  df['gpa'] >= 3]

выдаст такое же сообщение об ошибке.

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/var/folders/13/qmh5_52s3m72pbywgbf08_ch0000gn/T/ipykernel_50721/2058952526.py in <module>
----> 1 df[20 < df['age'] &
      2    df['age']< 25 &
      3   df['gpa'] >= 3]

~/opt/anaconda3/lib/python3.9/site-packages/pandas/core/generic.py in __nonzero__(self)
   1525     @final
   1526     def __nonzero__(self):
-> 1527         raise ValueError(
   1528             f"The truth value of a {type(self).__name__} is ambiguous. "
   1529             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."

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

Выводы

  1. Используйте & и | вместо and и or для объединения сравнений в срезах данных pandas.
  2. Избегайте использования цепочек сравнений в качестве одного из критериев разделения данных.

Спасибо за прочтение! Надеюсь, вам понравится использовать трюк Pandas в своей работе!

Пожалуйста, подпишитесь на мой Medium, если вы хотите читать больше историй от меня. И вы также можете присоединиться к членству в Medium по моей реферальной ссылке!