Как фильтровать DataFrame в pandas

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Базовый синтаксис

df[df['column'] > 100]

df['column'] > 100 возвращает Series булевых значений. df[...] оставляет только строки, где True.

1. Простое условие

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David'],
    'age':  [30, 25, 35, 28],
    'city': ['Moscow', 'SPb', 'Moscow', 'Kazan']
})

# возраст больше 27
df[df['age'] > 27]

# город — Москва
df[df['city'] == 'Moscow']

# НЕ Москва
df[df['city'] != 'Moscow']

2. Несколько условий (AND, OR)

Важно: используйте &, |, ~ (битовые операторы), не and, or, not.

И скобки обязательны вокруг каждого условия:

# AND
df[(df['age'] > 25) & (df['city'] == 'Moscow')]

# OR
df[(df['age'] < 25) | (df['city'] == 'Moscow')]

# NOT
df[~(df['city'] == 'Moscow')]

Забыть скобки — частая ошибка:

# SyntaxError / неверный результат
df[df['age'] > 25 & df['city'] == 'Moscow']

# правильно
df[(df['age'] > 25) & (df['city'] == 'Moscow')]

3. isin — значение в списке

# город в списке
df[df['city'].isin(['Moscow', 'SPb'])]

# НЕ в списке
df[~df['city'].isin(['Moscow', 'SPb'])]

4. between — диапазон

df[df['age'].between(25, 30)]  # inclusive обе границы
df[df['age'].between(25, 30, inclusive='neither')]  # без границ
df[df['age'].between(25, 30, inclusive='left')]     # включая левую

5. Строковые методы (.str)

# имена, начинающиеся с A
df[df['name'].str.startswith('A')]

# имена, заканчивающиеся на e
df[df['name'].str.endswith('e')]

# содержит подстроку
df[df['name'].str.contains('li')]

# case-insensitive
df[df['name'].str.contains('ali', case=False)]

# по regex
df[df['name'].str.contains(r'^[AB]')]  # начинается с A или B

6. Null / NaN

# строки с NaN в age
df[df['age'].isna()]

# без NaN
df[df['age'].notna()]
# или
df[df['age'].notnull()]

# любая колонка содержит NaN
df[df.isna().any(axis=1)]

# все колонки без NaN
df[df.notna().all(axis=1)]

7. query — SQL-подобный синтаксис

# удобно для сложных условий
df.query('age > 25 and city == "Moscow"')

# с переменной
min_age = 25
df.query('age > @min_age')

# несколько условий
df.query('age > 25 and city in ["Moscow", "SPb"]')

Плюсы query: читаемее при длинных условиях.

8. По нескольким колонкам одновременно

# любая из колонок содержит слово
cols = ['name', 'city']
mask = df[cols].apply(lambda x: x.str.contains('Moscow')).any(axis=1)
df[mask]

9. Top N / Bottom N

# 5 самых старших
df.nlargest(5, 'age')

# 5 самых младших
df.nsmallest(5, 'age')

10. Через loc

Альтернативный синтаксис:

df.loc[df['age'] > 25]

# с одновременным выбором колонок
df.loc[df['age'] > 25, ['name', 'age']]

11. Удалить строки по условию

Удалить — это то же самое, что оставить НЕ подходящие:

# удалить строки с age < 25
df = df[df['age'] >= 25]

# или через drop (по индексам)
df = df.drop(df[df['age'] < 25].index)

12. Отфильтровать по индексу

# первые 10 строк (не по условию)
df.head(10)

# по списку индексов
df.loc[[0, 2, 4]]

# срез по индексу
df.loc[5:15]

13. Фильтр + новая колонка

# флаг по условию (не фильтр, а аугментация)
df['is_moscow'] = df['city'] == 'Moscow'

# numpy.where
import numpy as np
df['status'] = np.where(df['age'] >= 30, 'senior', 'junior')

14. Performance для больших DataFrame

# избегать .loc с boolean для очень больших данных
df[mask]              # быстрее
df.loc[mask]          # медленнее
df.query('cond')      # чуть медленнее boolean, но читаемее

Для огромных данных рассмотрите:

  • категориальные dtypes (меньше памяти)
  • numpy.where вместо apply
  • vectorized операции

Частые ошибки

Ошибка 1. Использовать and вместо &

# TypeError или неверный результат
df[df['a'] > 0 and df['b'] < 10]

# правильно
df[(df['a'] > 0) & (df['b'] < 10)]

Ошибка 2. Забыть скобки

# приоритет & выше, чем ==
df[df['age'] > 25 & df['city'] == 'Moscow']  # сломано

# правильно
df[(df['age'] > 25) & (df['city'] == 'Moscow')]

Ошибка 3. Сравнение с NaN

# всегда False
df[df['age'] == np.nan]

# правильно
df[df['age'].isna()]

Ошибка 4. SettingWithCopyWarning

filtered = df[df['age'] > 25]
filtered['new_col'] = 1  # warning!

# правильно
filtered = df[df['age'] > 25].copy()
filtered['new_col'] = 1

Ошибка 5. isin с одиночным значением

# технически работает, но странно
df[df['city'].isin(['Moscow'])]

# короче
df[df['city'] == 'Moscow']

Связанные темы

FAQ

df[...] или df.loc[...]?

Для простого boolean-фильтра — df[mask]. Для одновременного выбора строк и колонок — df.loc[mask, cols].

query или обычный синтаксис?

query читаемее при длинных условиях. Обычный быстрее и явнее. Вкусовщина.

Как отфильтровать по дате?

df[df['date'] > '2026-01-01']
df[pd.to_datetime(df['date']).dt.year == 2026]

SettingWithCopyWarning — что делать?

.copy() после фильтрации, если собираетесь изменять.


Тренируйте Python и pandas — откройте тренажёр с 1500+ вопросами для собесов.