КПКейсПрактика
Все материалы
Продуктовая аналитикагайдстарт

Pandas с нуля: DataFrame, Series и чтение CSV

Первый практический урок по pandas: как читать CSV, понимать DataFrame и Series, фильтровать строки, выбирать столбцы и проверять пропуски.

КПКейсПрактика29 июля 2026 г.12 мин
Два аналитика изучают DataFrame и сортируют строки таблицы в pandas.

Pandas нужен не для того, чтобы заменить SQL любой ценой. Он удобен, когда нужно быстро взять файл или результат запроса, проверить структуру, почистить данные и собрать небольшой воспроизводимый анализ рядом с графиком. Начнём с главной модели: `DataFrame` — таблица, `Series` — один её столбец.

Коротко

Хороший первый сценарий в pandas выглядит так: прочитать данные, проверить форму и типы, выбрать нужные строки и столбцы, обработать пропуски, получить сводку и сохранить результат. Каждое действие лучше делать явно, чтобы через неделю можно было восстановить, откуда взялась цифра.

  • `DataFrame` хранит строки и столбцы, `Series` — одну именованную колонку.
  • `head`, `shape`, `info` и `describe` — обязательная проверка перед расчётами.
  • Фильтр должен отвечать на вопрос аналитика, а не просто оставлять “интересные” строки.
  • Пропуск, пустая строка и ноль — разные состояния.

DataFrame и Series на одной картинке

Возьмём маленькую таблицу заказов. В ней одна строка — один заказ, `user_id` — покупатель, `channel` — канал, `revenue` — сумма заказа. Когда пишем `orders["revenue"]`, получаем Series. Когда выбираем несколько колонок, остаёмся в DataFrame.

Базовые объекты pandas
ОбъектЧто хранитПример
DataFrameтаблицу со строками и колонкамиorders
Seriesодну колонку с индексомorders["revenue"]
Indexметки строкorders.index
dtypeтип данных колонкиorders["revenue"].dtype
pythonСоздать небольшой DataFrame
import pandas as pd

orders = pd.DataFrame({
    'order_id': [101, 102, 103, 104],
    'user_id': [1, 2, 1, 3],
    'channel': ['organic', 'paid', 'organic', 'email'],
    'revenue': [1200, 800, 450, 1500],
})

revenue = orders['revenue']       # Series
order_slice = orders[['channel', 'revenue']]  # DataFrame

Прочитать CSV и сразу проверить данные

`read_csv` загружает файл, но не сообщает, правильно ли ты понял его структуру. Поэтому чтение всегда заканчивай коротким профилем: количество строк и колонок, типы, пример строк, пропуски и диапазон дат. Это занимает минуту и ловит ошибки разделителя, кодировки и типов.

pythonЗагрузка и первичная проверка файла
orders = pd.read_csv(
    'data/orders.csv',
    parse_dates=['created_at'],
)

print(orders.shape)
print(orders.dtypes)
display(orders.head())
display(orders.isna().mean().sort_values(ascending=False).head())
Не доверяй расширению файла

CSV может быть разделён запятой, точкой с запятой или табуляцией. Если таблица выглядит как один широкий столбец, сначала проверь sep, encoding и кавычки, а не начинай чистить строки вручную.

Выбрать строки и колонки

Для понятного анализа разделяй выборку на два шага: какие строки относятся к вопросу и какие поля нужны для ответа. `loc` работает с условиями и именами колонок, `query` иногда делает длинный фильтр читабельнее. Не меняй исходный DataFrame без явного присваивания или `copy()` — так меньше неожиданных предупреждений.

pythonФильтр оплаченных заказов за период
paid_orders = orders.loc[
    (orders['status'] == 'paid')
    & (orders['created_at'] >= '2026-07-01')
    & (orders['created_at'] < '2026-08-01'),
    ['order_id', 'user_id', 'channel', 'revenue'],
].copy()

paid_orders = paid_orders.sort_values('revenue', ascending=False)
  • Для нескольких условий используй круглые скобки вокруг каждого сравнения.
  • В pandas нужен `&` для “и” и `|` для “или”, а не обычные `and` и `or`.
  • Правая граница периода `< next_start` не включает следующий месяц.

Пропуски: сначала смысл, потом fillna

Пропуск в `channel` может означать неизвестный источник, а пропуск в `discount` — отсутствие скидки. В первом случае нужна явная категория `unknown`, во втором ноль может быть корректен. Универсальная замена всех пропусков нулём делает таблицу аккуратной только на вид.

pythonЯвно обработать разные типы пропусков
orders['channel'] = orders['channel'].fillna('unknown')
orders['discount'] = orders['discount'].fillna(0)

missing_revenue = orders['revenue'].isna().sum()
if missing_revenue > 0:
    raise ValueError('Revenue is missing: check source data')

Мини-профиль перед первой метрикой

До группировки проверь, что дата действительно дата, сумма — число, а идентификатор заказа уникален там, где это ожидается. Если одна строка — позиция, нельзя считать количество строк количеством заказов. Python не знает смысл бизнес-полей без твоей проверки.

  • `orders.shape` — размер таблицы.
  • `orders.nunique()` — количество уникальных значений по колонкам.
  • `orders.duplicated("order_id").sum()` — контроль повторных заказов.
  • `orders["created_at"].min()` и `.max()` — фактическое окно дат.
  • `orders.describe(include="all")` — быстрый обзор распределений.
Продолжить чтение
Вся библиотека