Нашли ошибку?
Скачать чистый
мини-проект≈ 25 минупражнения: 0 из 6

Итоговый проект: выручка

Продажи «Кофейной лавки» за 2025 год — загрузить числа и текст из одного CSV, посчитать выручку, средний чек, выручку по категориям, товарам и городам одной операцией np.bincount с весами и разобрать «грязную» выгрузку службы доставки с пропусками.

Python запустится при первом выполнении

Итоговый проект курса. У интернет-магазина «Кофейная лавка» — все продажи за 2025 год: файл data/shop_orders.csv, 2448 строк. Владелец хочет понять, сколько магазин заработал и на чём. Ответим только средствами NumPy — всем, что изучили за курс, и одним новым приёмом: суммами по группам. А в конце разберём выгрузку службы доставки — с пропусками, как это бывает в жизни.

[ ]Файл продаж
with open("data/shop_orders.csv") as f:
    lines = f.read().splitlines()
print(len(lines) - 1, "строк")
for line in lines[:4]:
    print(line)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
2448 строк
order_id,date,customer_id,city,channel,category,product,price,quantity
10001,2025-01-01,C095,Москва,маркетплейс,Аксессуары,Кофемолка ручная,3200,2
10001,2025-01-01,C095,Москва,маркетплейс,Чай,Травяной сбор 50 г,260,1
10002,2025-01-01,C059,Москва,приложение,Сладости,Шоколад горький,180,1

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

Шаг 1. Выручка и средний чек

Числовые столбцы загружает знакомый np.loadtxt с usecols= — можно передать несколько номеров сразу. Текстовые столбцы он тоже умеет читать, если сказать dtype=str: получится массив строк.

[ ]Упражнение 1Выручка за год решено

Загрузите цену (столбец 7) и количество (столбец 8) и сохраните в revenue выручку каждой строки, а в total — выручку за год. Затем загрузите номера заказов (столбец 0) и сохраните в n_orders число разных заказов, а в avg_check — средний чек: выручка за год на один заказ.

import numpy as np

path = "data/shop_orders.csv"
revenue = ...
total = ...
n_orders = ...
avg_check = ...

Шаг 2. Суммы по группам

Сколько принесла каждая категория? Можно пройтись по категориям циклом и складывать выручку по маске. Но есть приём без цикла из двух шагов.

Коды групп. np.unique(a, return_inverse=True) возвращает не только уникальные значения, но и второй массив — для каждого элемента a номер его значения среди уникальных. Так текст превращается в целые коды:

[ ]Категории → коды
sample = np.array(["чай", "кофе", "чай", "какао", "кофе"])
names, codes = np.unique(sample, return_inverse=True)
print(names)
print(codes)
print(names[codes])       # коды → обратно в названия
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
['какао' 'кофе' 'чай']
[2 1 2 0 1]
['чай' 'кофе' 'чай' 'какао' 'кофе']

Суммы по кодам. np.bincount из модуля о случайных числах считает, сколько раз встретился каждый код. С параметром weights= он вместо единиц складывает веса: np.bincount(codes, weights=revenue) — сумма выручки по каждому коду. Порядок сумм — порядок уникальных названий:

[ ]bincount с весами
amounts = np.array([100, 250, 50, 80, 300])    # суммы тех же пяти покупок из sample
print(np.bincount(codes))
print(np.bincount(codes, weights=amounts))
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
[1 2 2]
[ 80. 550. 150.]
[ ]Упражнение 2Выручка по категориям решено

Загрузите столбец категорий (5) в category. Сохраните в cat_names названия категорий, в cat_revenue — выручку каждой категории, а в cat_share — долю каждой категории в выручке года.

category = ...
cat_names = ...
cat_revenue = ...
cat_share = ...

Кофе — больше половины выручки (58 %), чай — ещё 18 %.

Пояснение с ответом появится, когда решите упражнение «Выручка по категориям».
Вопрос

Что выведет print(np.bincount(np.array([0, 2, 0]), weights=np.array([5.0, 1.0, 3.0])))?

Шаг 3. Лучшие и худшие товары

[ ]Упражнение 3Пятёрка лидеров решено

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

top5 = ...
worst = ...

Шаг 4. Города

[ ]Упражнение 4Выручка по городам решено

Загрузите столбец городов (3) и сохраните в city_names названия, в city_revenue — выручку каждого города, а в moscow_share — долю Москвы в выручке года.

city_names = ...
city_revenue = ...
moscow_share = ...

Шаг 5. Грязная выгрузка: доставка

Данные о продажах были чистыми. Так бывает не всегда. Служба доставки прислала свою выгрузку — двумя файлами, за первое и второе полугодие: data/delivery_h1.csv и data/delivery_h2.csv. В каждом по строке на заказ: номер заказа, срок доставки в днях и оценка покупателя от 1 до 5.

[ ]Выгрузка службы доставки
with open("data/delivery_h1.csv") as f:
    lines = f.read().splitlines()
print(len(lines) - 1, "заказов в первом полугодии")
for line in lines[:6]:
    print(line)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
816 заказов в первом полугодии
order_id,days,rating
10001,4,
10002,4,4
10003,2,5
10004,3,5
10005,4,3

В файлах есть пустые поля: оценку ставят не все покупатели, а срок курьеры иногда не записывают. Это работа для приёмов из модуля о пропусках: np.genfromtxt, np.isnan, nan-функции, склейка массивов.

[ ]Упражнение 5Собрать и оценить потери решено

Загрузите оба файла и склейте их в одну таблицу delivery — сначала первое полугодие, потом второе. Сохраните в gaps число пропусков в каждом столбце, а в avg_days — средний срок доставки по заказам, где он известен.

delivery = ...
gaps = ...
avg_days = ...

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

Пояснение с ответом появится, когда решите упражнение «Собрать и оценить потери».
[ ]Упражнение 6Опоздания и оценки решено

Сохраните в late_share долю заказов, которые везли дольше 5 дней, — среди заказов с известным сроком. В avg_rating — среднюю оценку среди поставленных. В fast_rating и slow_rating — среднюю оценку заказов, доставленных за 3 дня и быстрее, и заказов, которые везли дольше 5 дней.

days = delivery[:, 1]
rating = delivery[:, 2]
late_share = ...
avg_rating = ...
fast_rating = ...
slow_rating = ...

[ ]Итоги года
print(f"выручка за год: {total:,.0f} ₽".replace(",", " "))
print(f"заказов: {n_orders}, средний чек: {avg_check:.0f} ₽")
for name, share in zip(cat_names, np.round(cat_share * 100, 1)):
    print(f"  {name}: {share} %")
print(f"доставка: в среднем {avg_days:.1f} дня, дольше 5 дней — {late_share * 100:.0f} % заказов")
print(f"оценка быстрых доставок {fast_rating:.1f}, медленных {slow_rating:.1f}")
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
выручка за год: 3 301 420 ₽
заказов: 1576, средний чек: 2095 ₽
  Аксессуары: 5.3 %
  Кофе: 58.0 %
  Посуда: 11.7 %
  Сладости: 6.7 %
  Чай: 18.4 %
доставка: в среднем 4.3 дня, дольше 5 дней — 21 % заказов
оценка быстрых доставок 4.2, медленных 2.7
Итог появится, когда решите все упражнения урока, — в нём ответы. Ячейку можно и выполнить.

Выводы

За 2025 год «Кофейная лавка» заработала 3.3 млн ₽ с 1576 заказов, средний чек — около 2100 ₽. Основа бизнеса — кофе: 58 % выручки и вся пятёрка лидеров. Почти 40 % денег приносит Москва, ещё четверть — Петербург. Доставка в среднем занимает 4.3 дня, но каждый пятый заказ везут дольше 5 дней — и оценивают такие заказы на 2.7 вместо 4.2 у быстрых: скорость доставки — первое, что стоит улучшать. Всё это — без единого цикла по строкам: коды групп np.unique(…, return_inverse=True) и суммы np.bincount(…, weights=…). В курсе pandas такие вопросы решает одна строка groupby — и вы будете понимать, что происходит внутри.

Выводы с ответами появятся, когда решите все упражнения урока.

Подробнее в справочнике

Нашли ошибку?