Pandas

Pandas — главная библиотека для работы с табличными данными: чтение CSV, очистка, фильтры, группировки и агрегирование в пару строк кода.

Уровень: начальный 20 разделов ⏱ 4–5 часов

1. Что такое Pandas

Pandas превращает Python в «электронную таблицу с кодом». Основные структуры:

На всех этапах работы с данными — от чтения до отправки в модель — Pandas незаменим.

2. Установка и импорт

pip install pandas
import pandas as pd
print(pd.__version__)

3. Создание DataFrame

import pandas as pd

df = pd.DataFrame({
    'name': ['Аня', 'Борис', 'Вера'],
    'age': [25, 31, 28],
    'city': ['Москва', 'Питер', 'Москва']
})
print(df)
print(df.shape)       # (3, 3)
print(df.columns)     # Index(['name','age','city'])
print(df.describe())  # сводка по числам

4. Чтение и запись данных

df = pd.read_csv('data.csv')       # CSV
df = pd.read_excel('file.xlsx')    # Excel
df = pd.read_json('data.json')     # JSON
df = pd.read_sql('SELECT * FROM t', conn)   # SQL

df.to_csv('out.csv', index=False)
df.to_excel('out.xlsx', index=False)
Все базы данных из курса SQL/MySQL подключаются к Pandas через read_sql по схеме DB-API.

5. Просмотр и описание

df.head(10)      # первые строки
df.tail(5)       # последние
df.info()        # типы и NULL
df.dtypes        # типы колонок
df['age'].value_counts()   # частоты

6. Выбор строк и столбцов

df['name']          # столбец (Series)
df[['name', 'age']] # несколько столбцов
df.loc[0]           # строка по индексу
df.iloc[1, 0]       # по позиции

df[df['age'] > 26]                 # фильтр
df[(df['age'] > 26) & (df['city'] == 'Москва')]
df.query('age > 26 and city == "Москва"')

7. Работа с пропусками

df.isna().sum()          # сколько NaN в каждой колонке
df.dropna()              # удалить строки с NaN
df.dropna(subset=['age'])# только по колонке
df.fillna(0)             # залить нулями
df['age'] = df['age'].fillna(df['age'].mean())  # средним
Не удаляй пропуски вслепую: сначала посмотри df.isna().sum(), сколько данных теряется — иногда выгоднее заполнить.

8. Новые колонки и функции

df['bmi'] = df['weight'] / (df['height'] ** 2)

def bucket(a):
    if a > 30:
        return 'high'
    return 'low'

df['level'] = df['bmi'].apply(bucket)

Векторные условия — np.where из NumPy:

import numpy as np
df['tag'] = np.where(df['age'] > 30, 'взрослый', 'молодой')

9. Группировка (groupby)

df.groupby('city')['age'].mean()       # средний возраст по городам
df.groupby('city').agg({'age': ['mean', 'median', 'max']})
df.groupby(['city', 'level']).size()   # частоты по двум полям

Аналог GROUP BY из SQL — только с результатом в DataFrame.

10. Сводные таблицы

pd.pivot_table(df,
    values='age',
    index='city',
    columns='level',
    aggfunc='mean',
    fill_value=0)

Как Excel-овская сводная: строки, столбцы, агрегат.

11. Объединение таблиц (Merge)

orders = pd.DataFrame({'user_id': [1, 2, 3], 'amount': [100, 250, 50]})
users = pd.DataFrame({'user_id': [1, 2, 3], 'name': ['A', 'B', 'C']})

merged = orders.merge(users, on='user_id', how='left')
print(merged)

how выбирает тип соединения: left/right/inner/outer — как JOIN в SQL. Для конкатенации строк — pd.concat.

12. Обработка дат и строк

df['date'] = pd.to_datetime(df['date'])
df['year'] = df['date'].dt.year
df = df.sort_values('date').set_index('date')
df.resample('M').sum()      # помесячная агрегация

df['name'] = df['name'].str.strip().str.title()
После разбора дат ставь дату в индекс — так включается мощный ресемплинг (по дням, месяцам, кварталам).

13. Дубликаты и выбросы

Грязные данные ломают агрегаты: дубликаты завышают суммы, выбросы искажают средние. Первый шаг EDA — поиск полных и частичных дубликатов.

import pandas as pd

df = pd.read_csv('sales.csv')

# Полные дубликаты
print(df.duplicated().sum())
df_clean = df.drop_duplicates()

# Дубликат по ключу (например email)
df = df.drop_duplicates(subset=['email'], keep='first')

# Выбросы по IQR
q1, q3 = df['amount'].quantile([0.25, 0.75])
iqr = q3 - q1
outliers = df[
  (df['amount'] < q1 - 1.5 * iqr) |
  (df['amount'] > q3 + 1.5 * iqr)
]
print(outliers)
Разделяйте «исправить» и «проверить»: дубликаты режутся автоматически, а выбросы анализируйте вручную — иногда это не ошибка, а пиковая продажа или крупный разовый заказ.
drop_duplicates не отличит случайный повтор от двух одинаковых покупок. Сначала добавьте колонку-счётчик или ключ заказа, только потом удаляйте.

14. Категориальные данные

Тип category экономит память и ускоряет группировки: значения хранятся как числа-коды с таблицей категорий, а операции groupby/value_counts работают в разы быстрее.

df['status'] = df['status'].astype('category')

print(df['status'].cat.categories)   # список категорий
print(df['status'].value_counts())   # быстрее, чем по строкам

# Порядок важен для сортировки
df['priority'] = pd.Categorical(
  df['priority'],
  categories=['low', 'medium', 'high'],
  ordered=True
)
МетодНазначение
cat.codesчисловые коды для ML
cat.rename_categoriesпереименование значений
cat.remove_unused_categoriesчистка пустых категорий
Категории с большим числом уникальных значений (ключи, телефоны) выгоды не дают — там оставайтесь на обычных object или string типах.

15. apply и vectorized-операции

Циклы по строкам DataFrame — самая медленная операция. Вместо них используйте векторные выражения, np.select и apply только там, где логику не выразить поколоночно.

import numpy as np

# Условия по всему столбцу
df['is_big'] = df['amount'] > 1000

# Много условий — numpy.select
cond = [df['amount'] > 5000, df['amount'] > 1000]
choice = ['VIP', 'обычный']
df['segment'] = np.select(cond, choice, default='мелкий')

# apply — только для сложной логики на скаляре
df['slug'] = df['name'].str.lower().str.replace(' ', '-')
Выражения по столбцам компилируются в быстрый код без Python-цикла. Если пишете df.iterrows() — почти наверняка найдётся векторная замена.
apply с функцией по строке копирует все колонки и работает в десятки раз медленнее векторных. Используйте df['col'].map(fn) вместо df.apply(..., axis=1) где возможно.

16. Работа с Excel, CSV и SQL

pandas читает и пишет Excel, CSV, JSON и базы данных. Для объёмных файлов указывайте нужные колонки и типы при чтении — это экономит память и парсинг.

df = pd.read_csv('sales.csv',
                 usecols=['date', 'amount'],
                 dtype={'amount': 'float32'})

df.to_csv('sales_clean.csv', index=False)

# Excel: один лист или весь файл
df = pd.read_excel('report.xlsx', sheet_name='Январь')

# SQL: чтение и запись
from sqlalchemy import create_engine
engine = create_engine('postgresql://user:pass@localhost/db')
df = pd.read_sql('SELECT * FROM sales', engine)
df.to_sql('sales_copy', engine, if_exists='replace')
Параметр index=False обязателен при записи, иначе в файл уйдёт ненужная колонка Unnamed: 0. При чтении CSV проверяйте разделитель и кодировку encoding='utf-8' — русские заголовки часто теряются из-за cp1251.
Для больших таблиц из SQL предпочтите чтение с пагинацией или продуманные WHERE: тянуть всю базу в память ради фильтра по одной колонке — напрасная трата.

17. Изменение формы: melt, stack и unstack

Данные приходят в разной форме: широкой (одна строка — один объект, много колонок) и длинной (одна строка — одно измерение). Melt переводит «широкую» таблицу в «длинную» для визуализаций и агрегаций: каждая пара колонок превращается в пары variable/value.

import pandas as pd

sales = pd.DataFrame({
  'month': ['Янв', 'Фев', 'Мар'],
  'north': [100, 120, 130],
  'south': [90, 85, 110]
})

long = sales.melt(
  id_vars='month',
  var_name='region',
  value_name='revenue'
)
print(long)

Обратная операция — pivot_table или pivot, а stack/unstack умеют перекладывать уровни индекс/колонки то вниз, то вверх. При pivot следите за возможными дублями ключей: pivot_table корректно агрегирует, а pivot бросит ошибку.

ФункцияДействие
meltширокую → длинную
pivot / pivot_tableдлинную → широкую
stackколонки → строки (индекс)
unstackуровень индекса → колонки
Перед melt оставляйте в id_vars только ключи-идентификаторы, иначе каждая «лишняя» колонка разлетится на отдельную строку.

18. Скользящие окна: rolling, expanding и shift

Скользящие агрегаты строят метрики по истории: среднее за 7 дней, максимум за месяц, сумма со скользящим окном. Метод rolling принимает размер окна, expanding считает по всем предыдущим значениям («кумулятивно»), а shift сдвигает ряд на N позиций — стандартная заготовка для расчёта приростов.

import pandas as pd

df = pd.DataFrame(
    {'days': pd.date_range('2026-01-01', periods=10, freq='D'),
     'price': [10, 12, 11, 14, 13, 15, 18, 16, 17, 20]})

df['ma3'] = df['price'].rolling(3).mean()
df['cumsum'] = df['price'].expanding().sum()
df['prev'] = df['price'].shift(1)
df['change'] = df['price'].pct_change()

print(df.head())

При работе с временными рядами сортируйте индекс по дате заранее: rolling не сортирует, а значит окно окажется «перемешанным», если данные не упорядочены. Для интервалов времени вместо фиксированного числа строк применяйте resample («за любой день месяца»).

Первые N-1 строк окна всегда пустые (или min_periods с записями): не забывайте про fillna в отчётах.

19. Оптимизация памяти и чтение больших файлов

Память — дефицит в аналитике: каждая колонка хранится целиком. Начните с типов данных: числовые колонки в float64 легко превращаются во float32, целые — в int32/int16, а строки — в категории, если значений мало.

import pandas as pd

df = pd.read_csv('big.csv', dtype={
    'id': 'int32',
    'qty': 'int16'
})

df['city'] = df['city'].astype('category')

# чтение по частям, если файл не влазит в память
reader = pd.read_csv('huge.csv', chunksize=100_000)
for chunk in reader:
    process(chunk)

# тонкий запрос вместо полной загрузки
df_small = pd.read_csv('huge.csv', usecols=['id', 'qty'])

Для таблиц из БД передавайте в SQL точный WHERE и только нужные колонки: не тяните всю базу ради пары фильтров. Параллельное чтение через engine='pyarrow' (dtype_backend) в новых pandas ускоряет большие файлы.

df = pd.read_csv('big.csv', engine='pyarrow',
                 dtype_backend='pyarrow')
Контролируйте занятость: df.memory_usage(deep=True) показывает, сколько памяти занимает каждая колонка, а типичная экономия от конвертации типов — 30–70%.
Категории хороши при малом числе уникальных значений: миллионы уникальных кодов в категорию не превращайте — выигрыша не будет, а сложность пробежит по всему коду.

20. Конвейеры данных и визуализация

Тяжёлая обработка в pandas не всегда «из коробки». Метод pipe позволяет строить читаемые конвейеры: каждая функция принимает и возвращает DataFrame, а цепочка вызывается по очереди. Плюс этого подхода — легко тестировать отдельные шаги.

import pandas as pd

def clean(df):
    return df.dropna().rename(columns=str.lower)

def add_rate(df):
    return df.assign(rate=df.revenue / df.days)

df = (pd.DataFrame({'Revenue': [100, 200, None],
                    'Days':    [2, 4, 5]})
        .pipe(clean)
        .pipe(add_rate))
print(df)

Особенно удобны конвейеры с кодом визуализации: на выходе каждой стадии — та же таблица, а графики строятся в отдельном шаге. Связка с Matplotlib и Seaborn выводит отчёт одним вызовом, а для интерактивности — Plotly или pandas-bokeh.

Серийные «код-спагетти» из строчек mutate — источник багов: прогоняйте один и тот же конвейер на тестовых данных с эталоном.

Что дальше

С таблицами разобрались — теперь графики Matplotlib и Seaborn. Для ML переход к Scikit-Learn. Всё удобно вести в Jupyter Notebook.

← Вернуться к курсам