Раздел 42 · Аналитика данных с нуля

Проект: EDA магазина «Кубик» от профиля данных до гипотез

middle~30 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Мира · маркетолог магазина настолок «Кубик»

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

В боссе блока D мы отвечали Мире на три конкретных вопроса: кто покупает, какие промокоды окупаются и кто лучшие клиенты. Сегодня вопроса нет, есть данные. Такое исследование называют разведочным анализом, или EDA. Его результат не ответ, а список хороших вопросов с цифрами.

Работаем в pandas и идём тремя фазами, как в боссах: профиль данных, распределения и связи, гипотезы. Портрет клиента и маржу промокодов из босса 31 не пересчитываем, а опираемся на них.

Фаза 1. Профиль данных

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

import pandas as pd

customers = pd.read_csv("customers.csv")
orders = pd.read_csv("orders.csv")
customers.shape, orders.shape   # ((3534, 4), (6555, 6))
ТаблицаСтрокЧто в ней
customers3534аккаунт: почта, город, канал привлечения
orders6555заказ: покупатель, статус, день, промокод, сумма позиций и возвратов
order_items11 283позиции заказов
products122игры: жанр, цена, себестоимость
returns273возвраты
deliveries6597попытки доставки
visits19 361визиты на сайт с UTM-метками

В orders.csv Мира уже свела сумму позиций (items_rub) и сумму возвратов (refund_rub) на уровень заказа, так что таблица одна, а не три.

Профиль столбцов

Дальше по каждому столбцу три вопроса: какой тип, сколько пропусков, сколько разных значений. info() из урока про чистку отвечает на первые два, но удобнее собрать ответ в одну таблицу. Такую таблицу называют профилем данных. Функцию profile(df) ты соберёшь в первом квесте, а вот что она показывает на заказах:

              dtype  missing  missing_pct  unique
customer_id   int64        0          0.0    3038
status       object        0          0.0       4
order_date   object        0          0.0     546
promo_code   object     4520         69.0       9
items_rub     int64        0          0.0    1825
refund_rub    int64        0          0.0     153

Читаем сверху вниз. order_date пришёл строкой: даты надо разобрать, иначе месяц из них не достать. В promo_code 69% пусто, но это не дыра в данных: пусто значит «без промокода». Такой пропуск несёт смысл, и заполнять его нельзя. Аккаунтов с заказами 3038, а в customers их 3534: 496 аккаунтов не сделали ни одного заказа. У клиентов профиль находит пропуски в городе: 94 аккаунта, 2,7%.

Двойники

В профиле клиентов у email 3534 разных значения, ровно по числу строк. Дублей нет? Сравним почту без учёта регистра:

customers["email"].duplicated().sum()              # 0
customers["email"].str.lower().duplicated().sum()  # 19

19 человек завели второй аккаунт, набрав почту с заглавной буквы. Эту болячку мы уже лечили в SQL через lower(email). В pandas лечение то же: человек это почта в нижнем регистре, а не id.

Диапазоны дат

Заказы идут с 1 января 2025 по 30 июня 2026: полтора года, ровно один декабрь. Это пригодится в фазе 2. Ещё одна проверка дат: заказ не может случиться раньше регистрации. Проверим в песочнице на полной базе:

SQL-песочница

База · boardshop

Загружаю редактор…

База загрузится, когда ты выполнишь запрос или откроешь таблицы.

Выполни запрос, чтобы увидеть результат.

Выполни весь запрос кнопкой или Ctrl/Cmd+Enter. Ctrl+Space открывает подсказки. Можно писать несколько команд через точку с запятой, менять данные и открывать транзакции. У каждой песочницы своя база, изменения живут до ухода со страницы. «Сбросить базу» возвращает учебные данные, но не меняет SQL.

Таких заказов 271, и все в тот же день, что регистрация, самое большое расхождение 15 часов 26 минут. Похоже, время регистрации пишет одна система, а время заказа другая. На анализ по дням это не влияет, а вот «время от регистрации до первой покупки» в часах считать нельзя. Причину мы не знаем, поэтому записываем вопрос разработчикам и идём дальше.

Аномалии и правила чистки

orders["status"].value_counts()
delivered    6136
cancelled     364
shipped        43
new            12

364 заказа отменены. shipped и new это заказы последних дней, они ещё едут, их оставляем. Возвраты есть у 273 заказов на 600 525 ₽, и 147 из них вернули целиком. Из 122 игр каталога 4 не продались ни разу.

Из профиля получаются правила, по которым строим чистую таблицу для фазы 2:

  1. отменённые заказы убираем, это не покупки;
  2. людей узнаём по почте в нижнем регистре;
  3. сумма заказа считается после возвратов, items_rub - refund_rub;
  4. пустой промокод значит «без промокода».

Получается 6191 заказ, 2927 человек и 21 274 258 ₽. Те же числа дал SQL в боссе 31, значит, pandas и база сходятся.

урок из курса

Урок из курса «Аналитика данных с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.