Pandas знают все, помнят синтаксис — не все. Типичный вечер дата-саентиста: 30% — думать, 70% — гуглить, как сделать «то же самое, но через groupby с несколькими агрегатами». Claude Code забирает второй пункт полностью.

Канал с гайдами и контентом по claude code, выкладываем новости (когда режут лимиты в 10 раз) и какие инструменты через claude реализуем для проектов, канал: https://t.me/claudedevolper

CLAUDE.md для анализа

## Стек
- Python 3.11+, Jupyter
- pandas 2.x, numpy
- matplotlib + seaborn, plotly для интерактива
- great_tables для красивых таблиц

## Правила
- Никаких chained indexing `df[a][b]` — только `.loc[]` / `.iloc[]`
- Копии явные: `df2 = df.copy()` когда мутируем
- SettingWithCopyWarning — не игнорировать
- Для больших данных — polars вместо pandas

Чистка данных

Типичный черновой датасет:

import pandas as pd

df = pd.read_csv("sales.csv")
df.info()
df.describe(include="all")
df.isna().sum().sort_values(ascending=False).head(20)

Затем в Claude: «почисти датасет: удали дубликаты, заполни NaN в income медианой, приведи signup_date к datetime, отфильтруй age в диапазон 18-100».

# Дубликаты по ключу
df = df.drop_duplicates(subset=["user_id", "order_id"], keep="last")

# Типы
df["signup_date"] = pd.to_datetime(df["signup_date"], errors="coerce")

# NaN — медианой по группе
df["income"] = df.groupby("country")["income"].transform(
    lambda s: s.fillna(s.median())
)

# Фильтр
df = df[df["age"].between(18, 100)]

# Проверка
assert df["signup_date"].isna().sum() == 0

Groupby с несколькими агрегатами

summary = (
    df.groupby(["country", "product_category"])
      .agg(
          orders=("order_id", "count"),
          revenue=("amount", "sum"),
          avg_ticket=("amount", "mean"),
          unique_users=("user_id", "nunique"),
      )
      .reset_index()
      .sort_values("revenue", ascending=False)
)

С выводом в формате аналитика:

summary["revenue_pct"] = (
    summary["revenue"] / summary["revenue"].sum() * 100
).round(2)

Time series

Типичная задача: «конверсия по дням + 7-дневное скользящее среднее».

daily = (
    df.assign(day=df["signup_date"].dt.date)
      .groupby("day")
      .agg(
          signups=("user_id", "count"),
          conversions=("converted", "sum"),
      )
      .reset_index()
)
daily["cr"] = daily["conversions"] / daily["signups"]
daily["cr_7d"] = daily["cr"].rolling(window=7, min_periods=3).mean()

График:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(12, 4))
ax.plot(daily["day"], daily["cr"], alpha=0.3, label="Daily")
ax.plot(daily["day"], daily["cr_7d"], linewidth=2, label="7-day MA")
ax.set_ylabel("Conversion rate")
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("conversion.png", dpi=120)

Merge без потерь

merged = users.merge(orders, on="user_id", how="left", indicator=True)

# Проверка, сколько осталось без заказа
print(merged["_merge"].value_counts())
# left_only     1204
# both         33921

# Проверка дубликатов после merge — если M:N случайно
assert not merged.duplicated(subset=["user_id", "order_id"]).any()

indicator=True даёт колонку _merge — видно, какие строки с какой стороны.

Pivot table на аналитику

pivot = df.pivot_table(
    index="country",
    columns="month",
    values="revenue",
    aggfunc="sum",
    fill_value=0,
    margins=True,
    margins_name="Total",
)

Polars когда pandas мелкий

Для таблиц > 10 ГБ pandas начинает тормозить. Polars делает то же быстрее и параллельно:

import polars as pl

df = pl.scan_csv("huge_dataset.csv")  # lazy
result = (
    df.filter(pl.col("amount") > 0)
      .group_by("country")
      .agg([
          pl.col("amount").sum().alias("revenue"),
          pl.col("user_id").n_unique().alias("users"),
      ])
      .sort("revenue", descending=True)
      .collect()   # выполнение только тут
)

Подводные камни

  • SettingWithCopyWarning. Обычно говорит «ты пишешь в view, а не в копию». Лечится .copy() или .loc[].
  • NaN в groupby. По умолчанию NaN-группы теряются. dropna=False если нужны.
  • Datetime без errors="coerce". Один мусорный формат роняет весь парсинг.
  • Float для денег. Копейки теряются. Decimal или int64 в копейках.

Как попробовать

1. Jupyter + CLAUDE.md 2. Загрузи свой CSV 3. Попроси: «почисти + посчитай CR по когортам» 4. Получи работающий ноутбук за 2-3 итерации

Канал с гайдами и контентом по claude code, выкладываем новости (когда режут лимиты в 10 раз) и какие инструменты через claude реализуем для проектов, канал: https://t.me/claudedevolper