Основы Pandas: анализ данных через реальные бизнес-кейсы

Курс проведёт вас от первых шагов работы с табличными данными до уверенного владения ключевыми инструментами библиотеки Pandas. Через реальные истории и практические кейсы вы освоите загрузку, фильтрацию, группировку, очистку и объединение данных.

1. История данных: загрузка и первичный осмотр

История данных: загрузка и первичный осмотр

Представьте: вы — аналитик в ритейл-компании, и утром вам приходит Excel-файл с продажами за квартал. 47 тысяч строк, 18 столбцов, и директор ждёт ответ на вопрос: «Почему в марте просели продажи в регионе Юг?» Прежде чем искать ответ, нужно понять, с чем вообще имеешь дело. Именно здесь начинается любая аналитическая работа — с загрузки и первичного осмотра данных.

Почему нельзя просто открыть файл и начать считать

В Excel вы привыкли видеть таблицу целиком. Но Pandas работает иначе: данные загружаются в специальную структуру — DataFrame, которая представляет собой двумерную таблицу с именованными столбцами и индексами строк. Это не просто «таблица в коде» — это объект с методами, которые позволяют мгновенно получать статистику, фильтровать, трансформировать данные без ручного перебора ячеек.

Загрузка данных — первый и критически важный шаг. Ошибки на этом этапе приводят к тому, что весь дальнейший анализ строится на неверных данных. Например, если при загрузке числовые столбцы распознаются как текст, все средние значения и суммы будут некорректными.

Загрузка данных: три основных источника

На практике данные приходят из разных источников, и Pandas умеет работать с каждым.

CSV-файлы — самый распространённый формат. Это простой текстовый файл, где значения разделены запятыми (или другими символами). Загрузка выполняется функцией pd.read_csv():

Но здесь кроется первая ловушка: кодировка. Если файл сохранён в Windows-кодировке cp1251, а вы загружаете его без указания кодировки, русские символы превратятся в «кракозябры». Решение — параметр encoding:

Excel-файлы загружаются через pd.read_excel(). Важный нюанс: в Excel-файле может быть несколько листов. Если не указать параметр sheet_name, Pandas загрузит первый лист:

Базы данных — третий источник. Через pd.read_sql() можно подключиться к PostgreSQL, MySQL, SQLite и другим СУБД, выполнив SQL-запрос и получив результат сразу в DataFrame:

Первичный осмотр: пять команд, которые расскажут всё

Допустим, файл загружен. Что делать дальше? Не открывать все 47 тысяч строк вручную — для этого существуют специальные методы.

head() и tail() — показывают первые или последние строки. По умолчанию — пять, но можно указать любое число:

Это как беглое знакомство с человеком: вы видите «лицо» данных — названия столбцов, формат значений, примеры записей.

shape — возвращает кортеж (количество строк, количество столбцов). Одной командой вы узнаёте масштаб задачи:

info() — самая информативная команда для первичного осмотра. Она показывает тип данных в каждом столбце, количество непустых значений и объём памяти:

Именно здесь вы обнаружите, что столбец revenue имеет тип object (текст) вместо float64 (число с плавающей точкой), а в столбце customer_id есть пропуски.

describe() — вычисляет описательную статистику для числовых столбцов: среднее, стандартное отклонение, минимум, максимум и квартили. Это быстрый способ увидеть аномалии:

Если в столбце quantity максимальное значение — 99 999 при среднем в 12 единиц, это повод проверить данные на выбросы.

dtypes — возвращает типы данных всех столбцов. Быстрый способ убедиться, что числа — это числа, даты — это даты, а категории — это категории:

Типы данных: почему это важно

Pandas автоматически определяет тип каждого столбца при загрузке, но делает это не всегда корректно. Вот основные типы, с которыми вы встретитесь:

| Тип данных | Обозначение | Пример | |---|---|---| | Целые числа | int64 | Количество товара | | Числа с плавающей точкой | float64 | Цена, выручка | | Текст | object | Название товара, город | | Дата и время | datetime64 | Дата заказа | | Категории | category | Статус заказа, регион |

Почему это критично? Потому что от типа зависит, какие операции доступны. С текстовым столбцом нельзя посчитать среднее. С датой нельзя работать как с текстом — не получится извлечь месяц или день недели. Если Pandas неправильно определил тип, вы можете исправить это вручную:

Индекс: невидимый каркас таблицы

Каждый DataFrame имеет индекс — это метки строк. По умолчанию Pandas создаёт числовой индекс от 0 до N-1. Но часто в данных есть столбец, который логичнее использовать как индекс — например, уникальный идентификатор заказа:

Индекс — это не просто нумерация. Он определяет, как Pandas обращается к строкам. Если вы позже будете объединять таблицы, правильный индекс сэкономит время и предотвратит ошибки.

Практический кейс: знакомство с датасетом продаж

Вернёмся к нашей задаче. После загрузки файла аналитик выполняет последовательность действий:

После этих пяти команд аналитик уже знает: в файле 47 231 запись, 18 столбцов, в трёх столбцах есть пропуски, столбец с датой загрузился как текст, а максимальная сумма заказа — 2,3 млн руб. при среднем в 4 700 руб. — явный выброс, который нужно исследовать.

> Загрузка и осмотр данных — это не формальность, а фундамент. Каждая минута, потраченная на понимание структуры данных в начале, экономит часы отладки в дальнейшем.

Теперь, когда мы знаем, как загружать данные и получать о них первое впечатление, пришло время научиться извлекать из них нужные фрагменты — фильтровать и отбирать именно те записи, которые отвечают на наш вопрос.

2. Искусство фильтрации и отбора данных

Искусство фильтрации и отбора данных

Вернёмся к нашему аналитику. Он загрузил 47 тысяч строк продаж и увидел, что в данных есть заказы из всех регионов России за три месяца. Но директор спросил про регион Юг и март. Как из 47 тысяч строк получить только те 800, которые нужны для ответа? Вот здесь начинается искусство фильтрации.

Два способа обращения к данным: loc и iloc

Прежде чем фильтровать, нужно понять, как Pandas обращается к ячейкам, строкам и столбцам. Здесь есть два фундаментальных инструмента.

iloc работает с позициями — числовыми индексами строк и столбцов. Это как сказать: «Дай мне 5-ю строку и 3-й столбец»:

loc работает с метками — реальными названиями столбцов и индексами строк. Это более читаемый и предпочтительный способ:

Важный нюанс: при использовании loc срез 0:5 включает обе границы — и 0, и 5. А в iloc срез работает как в обычном Python: 0:5 вернёт строки с 0 по 4.

Условная фильтрация: сердце анализа данных

Самая частая операция в аналитике — отобрать строки, удовлетворяющие условию. В Pandas это делается через 布尔ову маску — серию значений True/False, где True означает «строка подходит»:

За кулисами происходит следующее: выражение sales['region'] == 'Юг' создаёт серию из 47 тысяч булевых значений. Когда Pandas применяет эту серию к DataFrame, он оставляет только строки с True.

Условия можно комбинировать. Для этого используются операторы:

  • & — логическое И (оба условия должны выполняться)
  • | — логическое ИЛИ (хотя бы одно условие)
  • ~ — логическое НЕ (инверсия условия)
  • Каждое условие обязательно берётся в скобки — это требование Python из-за приоритета операторов:

    Метод query: фильтрация на человеческом языке

    Если комбинации скобок и амперсандов кажутся громоздкими, есть альтернатива — метод query(). Он принимает строку с условием, записанным почти на естественном языке:

    query() особенно удобен, когда условие сложное и содержит много столбцов. Но у него есть ограничение: он не работает с названиями столбцов, содержащими пробелы (например, order id). В таких случаях приходится возвращаться к классическому синтаксису с булевыми масками.

    Фильтрация по списку значений: метод isin

    Часто нужно отобрать строки, где значение столбца совпадает с одним из нескольких вариантов. Например, аналитику нужны данные по трём конкретным регионам. Вместо длинной цепочки |:

    Используйте isin() — он принимает список и возвращает True, если значение столбца входит в этот список:

    Обратная операция — отобрать всё, кроме перечисленных значений, — выполняется через ~:

    Фильтрация по диапазону: between и строки

    Для числовых диапазонов существует метод between(), который включает обе границы:

    Это эквивалент (sales['revenue'] >= 5000) & (sales['revenue'] <= 15000), но записывается компактнее.

    Для текстовых данных полезен метод str.contains(), который ищет подстроку. Например, найти все товары, содержащие слово «ноутбук»:

    Параметр case=False делает поиск нечувствительным к регистру, а na=False обрабатывает пропущенные значения, которые иначе вызвали бы ошибку.

    Выбор конкретных столбцов

    Помимо отбора строк, часто нужно выбрать только определённые столбцы. Двойные квадратные скобки возвращают DataFrame с указанными столбцами:

    Обратите внимание: одинарные скобки sales['city'] возвращают Series (одномерный массив), а двойные sales[['city']]DataFrame (таблицу с одним столбцом). Это различие важно, когда вы планируете дальнейшие операции с результатом.

    Практический кейс: расследование просадки продаж

    Вернёмся к задаче аналитика. Директор спросил: «Почему в марте просели продажи в регионе Юг?» Аналитик строит цепочку фильтрации:

    Результат показывает: 34% заказов в марте были отменены — в два раза выше обычного. Теперь аналитик знает направление для дальнейшего исследования.

    > Фильтрация — это не просто «взять нужные строки». Это способ задавать данные вопросы и получать на них точные ответы. Каждое условие — это гипотеза, которую вы проверяете.

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

    3. Группировка и агрегация: поиск инсайтов

    Группировка и агрегация: поиск инсайтов

    Аналитик обнаружил, что в марте в регионе Юг 34% заказов отменены. Но этого мало для отчёта директору. Нужно понять: отмены равномерно распределены по всем городам или сконцентрированы в одном? Какова средняя сумма отменённого заказа? Какой товар чаще всего возвращают? Ответы на эти вопросы даёт группировка и агрегация — инструмент, который превращает тысячи строк в осмысленные сводки.

    Группировка: разрезаем данные на логические части

    Метод groupby() — это, пожалуй, самый мощный инструмент Pandas. Он работает по принципу «разделяй-считай-объединяй»: разбивает данные на группы по значению столбца, применяет функцию к каждой группе и собирает результат обратно в таблицу.

    Представьте, что вы сортируете стопку чеков по кассирам. Каждый кассир — это группа. После сортировки вы можете посчитать общую сумму чеков для каждого кассира — это и есть агрегация.

    Здесь 'region' — столбец-ключ группировки, 'revenue' — столбец, к которому применяется функция, а .sum() — сама агрегирующая функция.

    Базовые агрегирующие функции

    Pandas предоставляет набор встроенных функций, каждая из которых отвечает на конкретный вопрос:

    | Функция | Что считает | Бизнес-вопрос | |---|---|---| | sum() | Сумма | Сколько всего заработали? | | mean() | Среднее | Каков средний чек? | | median() | Медиана | Какова «типичная» сумма заказа? | | count() | Количество непустых | Сколько заказов с указанным городом? | | nunique() | Количество уникальных | Сколько уникальных клиентов? | | min() / max() | Минимум / Максимум | Самый дешёвый и дорогой заказ? | | std() | Стандартное отклонение | Насколько разнятся суммы заказов? |

    Каждая из них применяется к сгруппированным данным:

    Метод agg: несколько агрегаций одновременно

    Часто нужно посчитать несколько показателей для одной группировки. Вместо того чтобы вызывать groupby три раза, используйте метод agg() — он принимает словарь, где ключи — названия столбцов, а значения — функции или их списки:

    Результат — таблица, где строки — это регионы, а столбцы — разные агрегации. Можно пойти дальше и задать разные функции для разных столбцов:

    Обратите внимание на именованный синтаксис: total_revenue=('revenue', 'sum') — это кортеж (столбец, функция), присвоенный имени нового столбца. Результат получается с понятными названиями колонок.

    Группировка по нескольким столбцам

    Иногда одной оси группировки недостаточно. Например, нужно посмотреть выручку в разрезе и региона, и месяца одновременно. Для этого передайте список столбцов в groupby:

    Результат — мультииндекс: строки имеют два уровня — сначала регион, затем месяц. Это мощная структура, но для дальнейшего анализа её часто удобно «развернуть» с помощью reset_index():

    Pivot table: группировка в табличном виде

    Если мультииндекс кажется неудобным, альтернатива — pivot_table(). Она создаёт привычную «сводную таблицу», как в Excel: строки — один признак, столбцы — другой, значения — агрегация:

    Результат — таблица, где по вертикали регионы, по горизонтали месяцы, а на пересечении — сумма выручки. Такую таблицу сразу можно вставить в презентацию.

    Практический кейс: углублённый анализ отмен

    Вернёмся к нашему аналитику. Он знает, что в марте в Южном регионе 34% заказов отменены. Теперь нужно найти причину:

    Краснодар и Ростов дают львиную долю отмен. Теперь — посмотрим, какие товары отменяют:

    Электроника — лидер по отменам. Аналитик углубляется дальше и проверяет среднюю сумму отменённых заказов по сравнению с выполненными:

    Оказывается, средний чек отменённого заказа — 18 400 руб., а выполненного — 4 200 руб. Клиенты отменяют дорогие заказы. Гипотеза: проблема с доставкой крупногабаритной электронники в Краснодар и Ростов в марте.

    > Группировка и агрегация — это микроскоп аналитика. Они позволяют увидеть закономерности, которые теряются в массиве отдельных записей. Но результат агрегации настолько же хорош, насколько качественны исходные данные.

    А о качестве данных — в следующей статье. Потому что если в исходных 47 тысячах строк есть дубликаты, пропуски и ошибки, то все наши расчёты могут вести к неверным выводам.

    4. Очистка данных: исправление ошибок в истории

    Очистка данных: исправление ошибок в истории

    Аналитик нашёл закономерность: дорогие заказы электроники в Краснодаре и Ростове массово отменяются. Но прежде чем строить гипотезу, он проверяет качество данных — и обнаруживает, что 312 строк имеют пустое значение в столбце delivery_date, 87 записей дублируются, а в 23 случаях дата заказа указана как 31 февраля. Если не исправить эти ошибки, любые выводы окажутся под вопросом.

    Пропущенные значения: невидимые дыры в данных

    Пропуски (NaN — Not a Number) — это отсутствующие значения. Они возникают по разным причинам: сотрудник не заполнил поле, система не передала данные, информация не применима к конкретной записи.

    Первый шаг — обнаружить пропуски. Метод isna() (или его синоним isnull()) возвращает таблицу из True/False, а sum() по ней показывает количество пропусков в каждом столбце:

    Для оценки масштаба проблемы полезно выразить пропуски в процентах:

    Что делать с пропусками — зависит от контекста. Есть три стратегии:

    Удаление. Если пропусков мало (менее 5% данных) и они случайны, строки можно удалить:

    Заполнение. Если данных мало или пропуск не случаен, лучше заполнить значения. Для числовых столбцов — медианой (она устойчива к выбросам), для категориальных — модой (самым частым значением) или специальной меткой:

    Оставить как есть. Иногда пропуск — это информация. Если delivery_date пуст, заказ, возможно, ещё не доставлен. Удаление таких строк исказит картину.

    Дубликаты: повторяющиеся страницы в книге

    Дубликаты — это строки, которые повторяются полностью или частично. Они возникают из-за технических сбоев, повторной выгрузки данных или ошибок при вводе.

    Найти дубликаты помогает метод duplicated():

    Параметр keep определяет, какую из повторяющихся строк считать «оригиналом»:

  • keep='first' — пометить как дубликат все, кроме первого вхождения
  • keep='last' — пометить все, кроме последнего
  • keep=False — пометить все повторяющиеся строки
  • Удаление выполняется методом drop_duplicates():

    Важный нюанс: иногда строки выглядят одинаково, но не являются дубликатами. Например, один и тот же клиент может сделать два одинаковых заказа в один день. Перед удалением всегда проверяйте бизнес-логику.

    Типы данных: приведение к правильному формату

    Как мы выяснили в первой статье, неправильные типы данных — источник ошибок. Вот типичные проблемы и их решения.

    Числа, загруженные как текст. Часто встречается, когда в CSV числа записаны с запятой как разделителем десятичной части или с пробелами как разделителями тысяч:

    Даты, загруженные как строки. Метод pd.to_datetime() преобразует строки в формат даты. Параметр errors='coerce' превращает некорректные даты в NaT (Not a Time) вместо выброса ошибки:

    Именно так обнаруживаются записи с датой 31 февраля — они станут NaT, и их легко найти:

    Категориальные данные. Столбцы с ограниченным набором значений (регион, статус, категория) стоит преобразовать в тип category. Это экономит память и ускоряет операции группировки:

    Выбросы: когда одно значение ломает статистику

    Выбросы — это значения, радикально отличающиеся от остальных. Они могут быть результатом ошибки (запятая не там, лишний ноль) или реальным, но редким явлением.

    Простейший способ обнаружить выбросы — метод describe(), который мы использовали в первой статье. Если максимальное значение в десятки раз превышает 75-й перцентиль, это повод проверить.

    Более формальный подход — межквартильный размах (IQR). Значения за пределами диапазона от до считаются выбросами, где — 25-й перцентиль, — 75-й перцентиль, а :

    Что делать с выбросами — зависит от контекста. Если это ошибка ввода (например, 2300000 вместо 23000), значение нужно исправить. Если это реальный, но редкий заказ, его можно оставить, но анализировать отдельно.

    Практический кейс: очистка перед анализом

    Аналитик выполняет последовательность действий перед тем, как строить финальный отчёт:

    После очистки аналитик уверен: данные корректны, и выводы об отменах в Краснодаре достоверны.

    > Очистка данных — это не рутинная предобработка, а критически важный этап. Как сказал Джордж Фьюэлс: «Чем лучше данные, тем проще анализ. Чем хуже данные, тем изобретательнее должен быть аналитик».

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

    5. Объединение таблиц: связывание фрагментов данных

    Объединение таблиц: связывание фрагментов данных

    Аналитик выяснил причину отмен: дорогая электроника в Краснодар и Ростов не доезжает до клиентов. Но директор спрашивает: «А кто эти клиенты? Это оптовики или розничные покупатели?» Информация о типе клиента хранится в другой таблице — customers.csv. Ответы на бизнес-вопросы редко лежат в одном файле. Нужно уметь объединять таблицы — и вот здесь Pandas раскрывает свою настоящую мощь.

    Почему данные живут в разных таблицах

    Проектирование баз данных строится на принципе нормализации: каждая сущность хранится отдельно. Клиенты — в одной таблице, заказы — в другой, товары — в третьей. Это устраняет дублирование: если клиент сменил адрес, достаточно обновить одну запись в таблице клиентов, а не тысячи строк в таблице заказов.

    Но для анализа нужна единая картина. Именно поэтому аналитик постоянно соединяет таблицы, связывая их по ключу — общему столбцу, который встречается в обеих таблицах. В нашем случае это customer_id.

    Merge: соединение таблиц по ключу

    Функция pd.merge() — основной инструмент объединения. Она работает по аналогии с SQL-запросом JOIN. Базовый синтаксис:

    Здесь on='customer_id' — общий столбец-ключ, а how='inner' — тип соединения. Именно тип соединения определяет, что происходит со строками, у которых нет пары в другой таблице.

    Четыре типа соединения

    Представьте две стопки карточек: стопка заказов и стопка клиентов. Вы берёте карточку заказа и ищете карточку клиента с тем же ID. Тип соединения определяет, что делать, когда совпадения нет.

    Inner join (внутреннее соединение) — оставляет только строки, у которых есть пара в обеих таблицах. Если клиент сделал заказ, но его нет в таблице клиентов — заказ отбрасывается:

    Left join (левое соединение) — сохраняет все строки из левой таблицы, даже если для них нет пары в правой. Недостающие данные заполняются NaN:

    Right join (правое соединение) — зеркальное отражение left join: сохраняет все строки из правой таблицы:

    Outer join (внешнее соединение) — сохраняет строки из обеих таблиц. Это максимально полный результат:

    Выбор типа соединения — это не техническое, а содержательное решение. Оно зависит от того, какой вопрос вы задаёте:

    | Вопрос | Тип соединения | |---|---| | Какие клиенты сделали заказы? | inner | | Все ли заказы имеют информацию о клиенте? | left | | Есть ли клиенты без заказов? | right | | Полная картина по обоим наборам данных? | outer |

    Когда ключи называются по-разному

    Не всегда столбцы-ключи имеют одинаковое название в обеих таблицах. В таблице заказов это может быть customer_id, а в таблице клиентов — id. В этом случае используйте параметры left_on и right_on:

    После такого соединения в результате окажутся оба столбца — и customer_id, и id. Чтобы избежать дублирования, лишний столбец можно удалить:

    Соединение по индексу

    Иногда ключом является не столбец, а индекс DataFrame. Например, таблица клиентов проиндексирована по customer_id. В этом случае используйте параметры left_on и right_index=True:

    Concat: склеивание таблиц по вертикали и горизонтали

    Помимо merge, существует функция pd.concat() — она склеивает таблицы, а не соединяет их по ключу. Это полезно в двух сценариях.

    Вертикальная склейка — когда у вас несколько файлов с одинаковой структурой (например, продажи за каждый месяц в отдельном файле):

    Параметр ignore_index=True сбрасывает старые индексы и создаёт новый последовательный — от 0 до N-1. Без него в итоговой таблице будут повторяющиеся индексы из исходных файлов.

    Горизонтальная склейка — когда нужно добавить столбцы из одной таблицы к другой (при условии совпадения индексов):

    Параметр axis=1 указывает, что склейка идёт по столбцам, а не по строкам.

    Ловушки при объединении

    Объединение таблиц — мощный инструмент, но есть несколько подводных камней.

    Дублирование строк. Если в правой таблице несколько строк с одним ключом, каждая строка левой таблицы «размножится». Например, если у одного клиента два адреса, количество строк в результате удвоится:

    Потеря данных при inner join. Если вы используете inner, а в одной из таблиц нет части ключей, эти данные исчезнут без предупреждения. Всегда проверяйте размеры таблиц до и после объединения:

    Конфликт имён столбцов. Если в обеих таблицах есть столбец с одинаковым названием (но не ключ), Pandas автоматически добавит суффиксы _x и _y. Чтобы задать свои суффиксы:

    Практический кейс: полная картина отмен

    Аналитик соединяет таблицу заказов с таблицей клиентов, чтобы понять профиль отменяющих покупателей:

    Оптовики отменяют чаще всего. Аналитик проверяет дальше:

    Картина прояснилась: оптовики заказывают крупные партии электронники, но отменяют, потому что доставка в Краснодар и Ростов занимает 12 дней вместо обещанных 5. Рекомендация директору: пересмотреть логистику в южных регионах или предложить оптовикам компенсацию за задержку.

    > Объединение таблиц — это финальный пазл анализа. Каждая таблица по отдельности рассказывает часть истории. Только соединив их, вы видите полную картину и можете принимать решения на основе данных, а не интуиции.

    От загрузки первого файла до построения полной аналитической картины — вот путь, который проходит каждый аналитик. Pandas даёт инструменты для каждого этапа: загрузить, осмотреть, отфильтровать, сгруппировать, очистить и объединить. Освоив эти пять шагов, вы сможете превращать хаос сырых данных в структурированные инсайты, на которые можно опираться при принятии решений.