Неоднозначное значение истинности 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
Как изменить порядок столбцов в DataFrame?
Преобразование списка словарей в DataFrame pandas
Установить значение для конкретной ячейки в DataFrame pandas с использованием индекса
Выбор строки из pandas Series/DataFrame по целочисленному индексу