TLDR; Логічні оператори в Pandas є &
, |
і ~
, і круглі дужки (...)
є важливими!
Python це and
, or
і not
логічні оператори призначені для роботи з скалярів. Тож Pandas довелося зробити один кращий і перемогти бітові оператори, щоб досягти векторизованої (елементарно) версії цієї функціональності.
Отже, наступне в python ( exp1
і exp2
це вирази, які оцінюють до булевого результату) ...
exp1 and exp2 # Logical AND
exp1 or exp2 # Logical OR
not exp1 # Logical NOT
... перекладе на ...
exp1 & exp2 # Element-wise logical AND
exp1 | exp2 # Element-wise logical OR
~exp1 # Element-wise logical NOT
для панд.
Якщо в процесі виконання логічної операції ви отримуєте a ValueError
, тоді вам потрібно використовувати круглі дужки для групування:
(exp1) op (exp2)
Наприклад,
(df['col1'] == x) & (df['col2'] == y)
І так далі.
Булева індексація : загальною операцією є обчислення булевих масок через логічні умови фільтрації даних. Pandas пропонує три оператори:&
для логічного AND,|
для логічного АБО та~
для логічного NOT.
Розглянемо наступне налаштування:
np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (5, 3)), columns=list('ABC'))
df
A B C
0 5 0 3
1 3 7 9
2 3 5 2
3 4 7 6
4 8 8 1
Логічний І
Для df
вище, скажімо , ви хочете , щоб повернути всі рядки , де А <5 і B> 5. Це робиться шляхом обчислення маски для кожного стану окремо, і Андінг їх.
Перевантажений &
оператор бітового
режиму Перш ніж продовжувати, будь ласка, зверніть увагу на цей конкретний уривок документів, які містять стан
Ще одна поширена операція - використання булевих векторів для фільтрації даних. Оператори: |
для or
, &
для and
і ~
для not
. Вони повинні бути згруповані за допомогою круглих дужок , оскільки за замовчуванням Python оцінить такий вираз, як, df.A > 2 & df.B < 3
в df.A > (2 &
df.B) < 3
той час, як бажаний порядок оцінки (df.A > 2) & (df.B <
3)
.
Отже, маючи це на увазі, елемент розумний логічний AND може бути реалізований за допомогою розрядного оператора &
:
df['A'] < 5
0 False
1 True
2 True
3 True
4 False
Name: A, dtype: bool
df['B'] > 5
0 False
1 True
2 False
3 True
4 True
Name: B, dtype: bool
(df['A'] < 5) & (df['B'] > 5)
0 False
1 True
2 False
3 True
4 False
dtype: bool
А наступний етап фільтрації простий,
df[(df['A'] < 5) & (df['B'] > 5)]
A B C
1 3 7 9
3 4 7 6
Дужки використовуються для заміщення порядку пріоритетності за замовчуванням побітових операторів, які мають більший пріоритет над умовними операторами <
та >
. Дивіться розділ « Операторська пріоритетність» в документах python.
Якщо ви не використовуєте дужки, вираз оцінюється неправильно. Наприклад, якщо ви випадково спробуєте щось подібне
df['A'] < 5 & df['B'] > 5
Розбирається як
df['A'] < (5 & df['B']) > 5
Що стає,
df['A'] < something_you_dont_want > 5
Що стає (див. Документи пітона на порівнянні ланцюжкових операторів ),
(df['A'] < something_you_dont_want) and (something_you_dont_want > 5)
Що стає,
# Both operands are Series...
something_else_you_dont_want1 and something_else_you_dont_want2
Який кидає
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Отже, не робіть цієї помилки! 1
Уникнення групування дужок
Виправлення насправді досить просте. Більшість операторів мають відповідний зв'язаний метод для DataFrames. Якщо окремі маски створені за допомогою функцій замість умовних операторів, вам більше не потрібно буде групуватися за паренами, щоб вказати порядок оцінки:
df['A'].lt(5)
0 True
1 True
2 True
3 True
4 False
Name: A, dtype: bool
df['B'].gt(5)
0 False
1 True
2 False
3 True
4 True
Name: B, dtype: bool
df['A'].lt(5) & df['B'].gt(5)
0 False
1 True
2 False
3 True
4 False
dtype: bool
Дивіться розділ про гнучкі порівняння. . Підводячи підсумок, ми маємо
╒════╤════════════╤════════════╕
│ │ Operator │ Function │
╞════╪════════════╪════════════╡
│ 0 │ > │ gt │
├────┼────────────┼────────────┤
│ 1 │ >= │ ge │
├────┼────────────┼────────────┤
│ 2 │ < │ lt │
├────┼────────────┼────────────┤
│ 3 │ <= │ le │
├────┼────────────┼────────────┤
│ 4 │ == │ eq │
├────┼────────────┼────────────┤
│ 5 │ != │ ne │
╘════╧════════════╧════════════╛
Ще одним варіантом уникнення дужок є використання DataFrame.query
(або eval
):
df.query('A < 5 and B > 5')
A B C
1 3 7 9
3 4 7 6
Я широко документував query
і eval
в оцінці динамічної виразності в пандах, використовуючи pd.eval () .
operator.and_
Дозволяє виконувати цю операцію функціонально. Внутрішні дзвінки, Series.__and__
що відповідає побітовому оператору.
import operator
operator.and_(df['A'] < 5, df['B'] > 5)
# Same as,
# (df['A'] < 5).__and__(df['B'] > 5)
0 False
1 True
2 False
3 True
4 False
dtype: bool
df[operator.and_(df['A'] < 5, df['B'] > 5)]
A B C
1 3 7 9
3 4 7 6
Зазвичай вам це не потрібно, але це корисно знати.
Узагальнення: np.logical_and
(та logical_and.reduce
)
Використовується ще одна альтернатива np.logical_and
, яка також не потребує групування дужок:
np.logical_and(df['A'] < 5, df['B'] > 5)
0 False
1 True
2 False
3 True
4 False
Name: A, dtype: bool
df[np.logical_and(df['A'] < 5, df['B'] > 5)]
A B C
1 3 7 9
3 4 7 6
np.logical_and
є ufunc (Універсальні функції) , і більшість функціонерів мають reduce
метод. Це означає, що простіше узагальнити, logical_and
якщо у вас є кілька масок до AND. Наприклад, до масок AND m1
і m2
та m3
з &
, вам доведеться робити
m1 & m2 & m3
Однак простіший варіант
np.logical_and.reduce([m1, m2, m3])
Це потужно, тому що дозволяє будувати поверх цього більш складну логіку (наприклад, динамічно генеруючи маски в розумінні списку та додаючи їх усі):
import operator
cols = ['A', 'B']
ops = [np.less, np.greater]
values = [5, 5]
m = np.logical_and.reduce([op(df[c], v) for op, c, v in zip(ops, cols, values)])
m
# array([False, True, False, True, False])
df[m]
A B C
1 3 7 9
3 4 7 6
1 - Я знаю, що я намагаюся в цьому питанні, але будь ласка, будьте зі мною. Це є дуже , дуже помилкою загальних для починаючої, і має бути пояснено дуже ретельно.
Логічний АБО
Для df
вищесказаного скажіть, що ви хочете повернути всі рядки, де A == 3 або B == 7.
Перевантажений побітом |
df['A'] == 3
0 False
1 True
2 True
3 False
4 False
Name: A, dtype: bool
df['B'] == 7
0 False
1 True
2 False
3 True
4 False
Name: B, dtype: bool
(df['A'] == 3) | (df['B'] == 7)
0 False
1 True
2 True
3 True
4 False
dtype: bool
df[(df['A'] == 3) | (df['B'] == 7)]
A B C
1 3 7 9
2 3 5 2
3 4 7 6
Якщо ви ще цього не зробили, будь ласка, також прочитайте розділ " Логічні І" вище, тут застосовуються всі застереження.
Крім того, цю операцію можна вказати за допомогою
df[df['A'].eq(3) | df['B'].eq(7)]
A B C
1 3 7 9
2 3 5 2
3 4 7 6
operator.or_
Дзвінки Series.__or__
під капотом.
operator.or_(df['A'] == 3, df['B'] == 7)
# Same as,
# (df['A'] == 3).__or__(df['B'] == 7)
0 False
1 True
2 True
3 True
4 False
dtype: bool
df[operator.or_(df['A'] == 3, df['B'] == 7)]
A B C
1 3 7 9
2 3 5 2
3 4 7 6
np.logical_or
Для двох умов використовуйте logical_or
:
np.logical_or(df['A'] == 3, df['B'] == 7)
0 False
1 True
2 True
3 True
4 False
Name: A, dtype: bool
df[np.logical_or(df['A'] == 3, df['B'] == 7)]
A B C
1 3 7 9
2 3 5 2
3 4 7 6
Для декількох масок використовуйте logical_or.reduce
:
np.logical_or.reduce([df['A'] == 3, df['B'] == 7])
# array([False, True, True, True, False])
df[np.logical_or.reduce([df['A'] == 3, df['B'] == 7])]
A B C
1 3 7 9
2 3 5 2
3 4 7 6
Логічний НЕ
Дано маску, наприклад
mask = pd.Series([True, True, False])
Якщо вам потрібно інвертувати кожне булеве значення (щоб вийшов кінцевий результат [False, False, True]
), то ви можете скористатися будь-яким із наведених нижче методів.
Побітові ~
~mask
0 False
1 False
2 True
dtype: bool
Знову ж таки, вирази потрібно думати в дужках.
~(df['A'] == 3)
0 True
1 False
2 False
3 True
4 True
Name: A, dtype: bool
Це внутрішньо закликає
mask.__invert__()
0 False
1 False
2 True
dtype: bool
Але не використовуйте його безпосередньо.
operator.inv
Внутрішні дзвінки __invert__
на Серію.
operator.inv(mask)
0 False
1 False
2 True
dtype: bool
np.logical_not
Це нудний варіант.
np.logical_not(mask)
0 False
1 False
2 True
dtype: bool
Зауважте, np.logical_and
може бути замінено на np.bitwise_and
, logical_or
з bitwise_or
і logical_not
на invert
.