Неоднозначное значение истинности Series. Используйте a.empty, a.bool(), a.item(), a.any() или a.all()
Я хочу отфильтровать свой DataFrame по условию с использованием оператора or
, чтобы оставить строки, значения определённого столбца которых находятся вне диапазона [-0.25, 0.25]
. Я попытался сделать это следующим образом:
df = df[(df['col'] < -0.25) or (df['col'] > 0.25)]
Однако я получаю следующую ошибку:
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Как правильно применить условие or
для фильтрации DataFrame в этом случае?
5 ответ(ов)
Несмотря на то, что в Pandas используются побитовые операторы &
и |
, необходимо оборачивать каждое условие в скобки ()
.
Пример корректного запроса:
data_query = data[(data['year'] >= 2005) & (data['year'] <= 2010)]
Попытка выполнить тот же запрос без скобок приведет к ошибке:
data_query = data[(data['year'] >= 2005 & data['year'] <= 2010)] # Это не сработает
Причина в том, что оператор &
имеет более высокий приоритет, чем оператор сравнения >=
, поэтому Python сначала пытается выполнить data['year'] >= 2005 & data['year']
, что приводит к некорректному результату. Всегда оборачивайте условия в скобки, чтобы избежать такого поведения.
Если у вас есть более одного значения в столбце DataFrame, вы можете использовать метод all()
для проверки, что все значения являются истинными:
df['col'].all()
Если у вас только одно значение, то для его получения следует использовать метод item()
, который вернет это единственное значение:
df['col'].item()
Таким образом, all()
подходит для проверки всех элементов в столбце, а item()
— для извлечения одного значения.
Вы столкнулись с ошибкой из-за неправильного использования оператора сравнения. В Python для проверки того, что переменная не равна определенному значению, лучше использовать оператор is not
вместо !=
.
Вот объяснение вашего кода:
Использование
if df != '':
будет работать, но вызывает некоторую путаницу, поскольку!=
— это оператор, который проверяет неравенство. Еслиdf
— это объект, а не строка, использование этого оператора может привести к неожиданным результатам.Запись
if df is not '':
является более предпочтительной в данном случае, посколькуis
проверяет на идентичность объектов, а не просто на равенство значений. Однако стоит помнить, что для сравнения строк использование!=
или==
предпочтительнее.
В общем, если вы хотите проверить, что строка df
не пуста, используйте:
if df != '':
pass
или, если вы хотите проверить пустой объект:
if bool(df): # или просто if df:
pass
Таким образом, оба применяемых метода будут более правильными в зависимости от контекста вашего кода.
Вам нужно использовать побитовые операторы |
вместо or
и &
вместо and
в библиотеке pandas. Нельзя просто использовать логические операторы из Python.
Для более сложной фильтрации лучше создать маску
и применить её к DataFrame.
Соберите все свои условия в маску и примените её. Например:
mask = (df["col1"] >= df["col2"]) & (df["col1"] <= df["col2"])
df_new = df[mask]
В данном вопросе рассматриваются три наиболее распространенных способа фильтрации данных в NumPy и их производительность.
Для начала код был протестирован на массиве NumPy, где используются разные методы фильтрации:
from timeit import repeat
setup = """
import numpy as np
import random
x = np.linspace(0, 100)
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
"""
stmts = 'x[(x > lb) * (x <= ub)]', 'x[(x > lb) & (x <= ub)]', 'x[np.logical_and(x > lb, x <= ub)]'
for _ in range(3):
for stmt in stmts:
t = min(repeat(stmt, setup, number=100_000))
print('%.4f' % t, stmt)
print()
Результаты выполнения показывают скорости для разных методов сравнения:
0.4808 x[(x > lb) * (x <= ub)]
0.4726 x[(x > lb) & (x <= ub)]
0.4904 x[np.logical_and(x > lb, x <= ub)]
0.4725 x[(x > lb) * (x <= ub)]
0.4806 x[(x > lb) & (x <= ub)]
0.5002 x[np.logical_and(x > lb, x <= ub)]
0.4781 x[(x > lb) * (x <= ub)]
0.4336 x[(x > lb) & (x <= ub)]
0.4974 x[np.logical_and(x > lb, x <= ub)]
Как видно, использование *
для булевой маски работает, но этот метод не поддерживается в pandas Series, что делает NumPy более быстрым в данном контексте. Тем не менее, NumPy быстрее pandas data frame (порядка 1000 раз).
Далее, код был протестирован на DataFrame от pandas:
from timeit import repeat
setup = """
import numpy as np
import random
import pandas as pd
x = pd.DataFrame(np.linspace(0, 100))
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
"""
stmts = 'x[(x > lb) & (x <= ub)]', 'x[np.logical_and(x > lb, x <= ub)]'
for _ in range(3):
for stmt in stmts:
t = min(repeat(stmt, setup, number=100))
print('%.4f' % t, stmt)
print()
Результаты тестов для pandas выглядят следующим образом:
0.1964 x[(x > lb) & (x <= ub)]
0.1992 x[np.logical_and(x > lb, x <= ub)]
0.2018 x[(x > lb) & (x <= ub)]
0.1838 x[np.logical_and(x > lb, x <= ub)]
0.1871 x[(x > lb) & (x <= ub)]
0.1883 x[np.logical_and(x > lb, x <= ub)]
Обратите внимание, что добавление x = x.to_numpy()
потребует примерно 20 мкс.
Для тех, кто предпочитает использовать %timeit
, был проведен следующий тест:
import numpy as np
import random
import pandas as pd
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
x = pd.DataFrame(np.linspace(0, 100))
def asterik(x):
x = x.to_numpy()
return x[(x > lb) * (x <= ub)]
def and_symbol(x):
x = x.to_numpy()
return x[(x > lb) & (x <= ub)]
def numpy_logical(x):
x = x.to_numpy()
return x[np.logical_and(x > lb, x <= ub)]
for i in range(3):
%timeit asterik(x)
%timeit and_symbol(x)
%timeit numpy_logical(x)
print('\n')
Результаты выглядят следующим образом:
23 µs ± 3.62 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
35.6 µs ± 9.53 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
31.3 µs ± 8.9 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
21.4 µs ± 3.35 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
21.9 µs ± 1.02 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
21.7 µs ± 500 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
25.1 µs ± 3.71 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
36.8 µs ± 18.3 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
28.2 µs ± 5.97 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Таким образом, использование &
в pandas является предпочтительным способом фильтрации, так как это обеспечивает корректную работу и поддержку во всех структурах данных, даже несмотря на то, что метод *
может показаться быстрее в случае с NumPy.
Как фильтровать DataFrame Pandas с помощью 'in' и 'not in', как в SQL
Переименование названий столбцов в Pandas
"Красивая печать всей Series / DataFrame в Pandas"
Преобразование списка словарей в DataFrame pandas
Объединение двух столбцов текста в DataFrame pandas