Перейти к основному содержимому

Pandas — объединение таблиц, своды и временные ряды

Разработчику

См. также: Анализ данных — обзор NumPy и Pandas · 337 — NumPy (теория) · NumPy — массивы и матрицы (Lab) · типовые операции — примеры в Lab · файлы и CSV — stdlib в Lab · Даты и время · Классическое ML


Series и DataFrame

СтруктураРазмерностьАналогТипичное использование
Series1Dименованный столбецодна метрика, результат groupby, временной ряд
DataFrame2Dтаблица Excelнесколько колонок разных типов, EDA, merge

Series — одномерный индексированный массив: у каждого значения есть метка (индекс). Можно создать из списка, словаря или скаляра:


import pandas as pd

s = pd.Series([10, 20, 30], index=["a", "b", "c"])
s_from_dict = pd.Series({"Москва": 100, "Казань": 50})

DataFrame — набор Series с общим индексом строк; каждый столбец — отдельная Series. Одна колонка DataFrame — это Series:

df = pd.DataFrame({"city": ["Москва", "Казань"], "sales": [100, 50]})
col = df["sales"] # Series

Подробный обзор загрузки и очистки — 33 — Pandas и Lab 1113.


Для кого эта глава

В обзорной статье разобраны NumPy, базовый Pandas и визуализация. Базовые операции (фильтр, groupby, очистка, merge) с построчным разбором — в Pandas — типовые операции. Здесь — углублённые операции аналитика — точный выбор ячеек, соединение таблиц из разных источников, сводные отчёты и ряды по дате.

Эту статью удобно читать как "рабочий справочник" — открыл, взял шаблон, адаптировал под свои поля и запустил. Поэтому ниже не только синтаксис, но и пояснение "когда это реально нужно в проекте".


import pandas as pd

Разбор:

  • import pandas as pd подключает библиотеку Pandas.
  • Псевдоним pd делает дальнейшие вызовы короче и читаемее.
  • Такой импорт используют почти во всех проектах на Pandas.

loc и iloc

МетодИндексация
ilocПо позиции (0, 1, 2 …), как в NumPy
locПо метке индекса и имени столбца
df = pd.DataFrame(
{"city": ["Москва", "Казань", "Москва"], "sales": [100, 50, 120]},
index=["a", "b", "c"],
)

df.loc["a", "sales"] # 100 — метка строки и столбца
df.iloc[0, 1] # 100 — первая строка, второй столбец
df.loc[df["city"] == "Москва", ["sales", "city"]]
df.iloc[0:2, :] # первые две строки, все столбцы

Разбор:

  • pd.DataFrame(...) создаёт таблицу и индекс с метками a, b, c.
  • loc["a", "sales"] выбирает значение по именованной строке и колонке.
  • iloc[0, 1] выбирает ту же ячейку по позициям.
  • loc[df["city"] == "Москва", ...] фильтрует строки по условию.
  • iloc[0:2, :] берёт срез по позициям: первые две строки и все колонки.

Срезы в loc включают правую границу ("a":"b" берёт и b). В iloc правая граница исключается — как в Python.

Присваивание через loc изменяет исходный фрейм (осторожно с SettingWithCopyWarning — работайте с .copy() при цепочках фильтров).


merge и join — склейка таблиц

Аналог SQL JOIN: два DataFrame по ключевым столбцам.

orders = pd.DataFrame({
"order_id": [1, 2, 3],
"user_id": [10, 10, 20],
"amount": [500, 300, 150],
})
users = pd.DataFrame({
"user_id": [10, 20],
"name": ["Анна", "Борис"],
})

pd.merge(orders, users, on="user_id", how="inner")
howРезультат
innerТолько совпадающие ключи
leftВсе строки левой таблицы
rightВсе строки правой
outerОбъединение с NaN где нет пары

Разные имена ключей: left_on="uid", right_on="user_id". Несколько ключей — список в on.

pd.concat([df1, df2], axis=0)стопка по строкам (одинаковые столбцы). axis=1 — добавление столбцов бок о бок по совпадающему индексу.

Практический совет: перед merge проверьте уникальность ключей:

orders["order_id"].duplicated().any()
users["user_id"].duplicated().any()

Разбор:

  • duplicated() отмечает повторяющиеся значения в колонке ключа.
  • any() проверяет, есть ли хотя бы один дубликат.
  • Если получился True, перед merge нужно проверить кардинальность ключей.
  • Это предотвращает случайное размножение строк после соединения.

Если ключи неуникальны с двух сторон, вы получите "размножение" строк (many-to-many), что часто выглядит как ошибка в отчёте.


pivot_table и melt

Сводная таблица — агрегация по измерениям (как свод в Excel; формулы СУММЕСЛИ и сводные — Excel и Google Sheets — формулы, EDA — Разведочный анализ данных в Excel):

Код ITЗагрузка примера кода…

Разбор:

  • pivot_table(...) формирует сводный отчёт из исходных строк.
  • index="region" делает регионы строками результата.
  • columns="product" раскладывает продукты по колонкам.
  • aggfunc="sum" суммирует выручку по каждой комбинации.
  • fill_value=0 заменяет пустые ячейки нулями вместо NaN.

melt ("расплавление") переводит широкий формат в длинный — удобно перед визуализацией:

pd.melt(
wide_df,
id_vars=["id"],
value_vars=["jan", "feb"],
var_name="month",
value_name="amount",
)

Разбор:

  • melt(...) переводит широкий формат таблицы в длинный.
  • value_vars=["jan", "feb"] указывает колонки, которые нужно расплавить.
  • var_name="month" записывает имя исходной колонки в поле месяца.
  • value_name="amount" сохраняет значение в отдельной числовой колонке.
  • id_vars=["id"] сохраняет идентификатор строки для дальнейшего анализа.

Очистка — пропуски и дубликаты

df.dropna(subset=["email"]) # удалить строки без email
df.fillna({"age": df["age"].median()})
df.drop_duplicates(subset=["user_id"], keep="last")

Разбор:

  • dropna(subset=["email"]) удаляет только строки, где отсутствует email.
  • fillna({"age": ...}) заполняет пропуски в age медианой колонки.
  • drop_duplicates(subset=["user_id"], keep="last") оставляет по одному актуальному ряду на пользователя.

Интерполяция по времени:

ts = df.set_index("date")["value"]
ts.interpolate(method="time")

Разбор:

  • set_index("date") переводит дату в индекс, удобный для временных операций.
  • ["value"] выбирает целевую числовую серию для обработки.
  • interpolate(method="time") заполняет пропуски по временной шкале.
  • Метод учитывает расстояние между соседними временными точками.

Документируйте, почему выбран способ заполнения пропусков — это влияет на метрики и отчёты.

Частая безопасная практика: хранить отдельный флаг-колонку перед заполнением:

df["age_was_missing"] = df["age"].isna()
df["age"] = df["age"].fillna(df["age"].median())

Разбор:

  • isna() помечает строки, где в age был пропуск.
  • Новая колонка age_was_missing сохраняет этот признак для аудита.
  • fillna(...) заменяет пропущенные значения медианой.
  • Такой подход оставляет и чистые данные, и след об исходном качестве источника.

Так вы не теряете информацию о факте пропуска.


Временные ряды

Код ITЗагрузка примера кода…

Сводка resample, shift, date_range, asfreq и strftimeтиповые операции Pandas.

Разбор:

  • to_datetime(...) переводит строковые даты в тип datetime.
  • set_index("date").sort_index() подготавливает корректный временной индекс.
  • loc["2026-01":"2026-03"] делает календарный срез по периоду.
  • resample("W").sum() агрегирует значения по неделям.
  • rolling(7).mean() считает 7-дневное скользящее среднее для сглаживания шума.

Часовые пояса: после pd.to_datetimedf.index.tz_localize("UTC") или tz_convert — подробнее в главе про datetime.


Производительность и границы

  • Операции над всей колонкой быстрее, чем apply по строкам в Python-цикле.
  • Для десятков гигабайт — Dask, Polars, Spark (см. обзор анализа данных).
  • Перед merge проверяйте дубликаты ключей — иначе строки размножатся.

Типовой мини-пайплайн "CSV → отчёт"

Код ITЗагрузка примера кода…

Разбор:

  • read_csv(...) загружает исходные продажи в DataFrame.
  • to_datetime(..., errors="coerce") нормализует даты и переводит ошибочные значения в NaT.
  • dropna(...) удаляет строки без даты или суммы, которые ломают расчёты.
  • Выражение dt.to_period("M").astype(str) формирует месяц для группировки.
  • groupby(...).agg(...) считает суммарные продажи и число уникальных заказов.
  • sort_values(...) упорядочивает отчёт по месяцу и объёму продаж.
  • to_csv(..., index=False) экспортирует финальный файл без служебного индекса.

Этот шаблон покрывает типичный сценарий операционной аналитики — очистка, нормализация дат, агрегация, экспорт.


Связанные материалы