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

Pandas нужен не для того, чтобы заменить SQL любой ценой. Он удобен, когда нужно быстро взять файл или результат запроса, проверить структуру, почистить данные и собрать небольшой воспроизводимый анализ рядом с графиком. Начнём с главной модели: `DataFrame` — таблица, `Series` — один её столбец.
Коротко
Хороший первый сценарий в pandas выглядит так: прочитать данные, проверить форму и типы, выбрать нужные строки и столбцы, обработать пропуски, получить сводку и сохранить результат. Каждое действие лучше делать явно, чтобы через неделю можно было восстановить, откуда взялась цифра.
- `DataFrame` хранит строки и столбцы, `Series` — одну именованную колонку.
- `head`, `shape`, `info` и `describe` — обязательная проверка перед расчётами.
- Фильтр должен отвечать на вопрос аналитика, а не просто оставлять “интересные” строки.
- Пропуск, пустая строка и ноль — разные состояния.
DataFrame и Series на одной картинке
Возьмём маленькую таблицу заказов. В ней одна строка — один заказ, `user_id` — покупатель, `channel` — канал, `revenue` — сумма заказа. Когда пишем `orders["revenue"]`, получаем Series. Когда выбираем несколько колонок, остаёмся в DataFrame.
| Объект | Что хранит | Пример |
|---|---|---|
| DataFrame | таблицу со строками и колонками | orders |
| Series | одну колонку с индексом | orders["revenue"] |
| Index | метки строк | orders.index |
| dtype | тип данных колонки | orders["revenue"].dtype |
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` загружает файл, но не сообщает, правильно ли ты понял его структуру. Поэтому чтение всегда заканчивай коротким профилем: количество строк и колонок, типы, пример строк, пропуски и диапазон дат. Это занимает минуту и ловит ошибки разделителя, кодировки и типов.
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()` — так меньше неожиданных предупреждений.
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`, во втором ноль может быть корректен. Универсальная замена всех пропусков нулём делает таблицу аккуратной только на вид.
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")` — быстрый обзор распределений.
Материалы по теме
Как тестировать аналитические расчёты на Python и pandas
Практический гайд по тестам для аналитика: проверить метрики на маленьком датасете, поймать регрессию и защитить расчёт от тихих изменений.
Читать материал
Как ускорить pandas: память, типы и обработка больших файлов
Что делать, если pandas медленно работает или не помещает файл в память: категории, downcast, chunksize, Parquet и контроль размера данных.
Читать материал
Pandas apply и векторизация: как писать быстрее и понятнее
Когда использовать apply, почему векторные операции быстрее и как переписать медленный построчный расчёт в pandas.
Читать материал