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().
Выводы
- Используйте
&
и|
вместоand
иor
для объединения сравнений в срезах данных pandas. - Избегайте использования цепочек сравнений в качестве одного из критериев разделения данных.
Спасибо за прочтение! Надеюсь, вам понравится использовать трюк Pandas в своей работе!
Пожалуйста, подпишитесь на мой Medium, если вы хотите читать больше историй от меня. И вы также можете присоединиться к членству в Medium по моей реферальной ссылке!