Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Шиномонтаж методом Симпсона: как красивая статистика чуть не убила премиум-сервис

Дата публикации: 10-08-2026 04:45:25

Что делать, если средний чек премиального автосервиса оказался в 4 раза меньше планового? Первое «очевидное» решение руководства — немедленно избавиться от дешёвой мелочёвки вроде сезонного шиномонтажа и хранения колес, которая перегружает мастеров и портит красивую статистику.Звучит логично? Абсолютно. Вот только глубокий дата-анализ показал, что такое «оптимизаторское» решение лишило бы компанию доброй трети валовой выручки.В этой статье мы разбираем реальный кейс анализа массива из сотен тысяч заказов за 10 лет работы крупного автодилера. 

Основное содержимое страницы с новостью.

Заведите себе красивую корзину исключительно для красивых отчетов

Вечер, пятница. Мы сидим и читаем красивый аналитический отчет распечатанный на глянцевой бумаге. Выбрана приятная глазу палитра, графики все в одном стиле, — прямо как модный журнал. Такое жалко выбрасывать сразу, поэтому читаю до конца. В отчете поднимается проблема низкого среднего чека и предлагается исключить из ассортимента дешевые «нерентабельные» услуги.

Хорошо, что под столом все таки есть специальная красивая корзина для таких случаев, так что я с уважением кладу отчет туда. Но что с ним не так? Ведь логично же, что убирая все что ниже арифметического среднего, мы будем поднимать это среднее все выше и выше, и в итоге придем к Золотому Эльдорадо.

В этой статье я хочу поделиться реальным кейсом анализа базы данных за последние 10 лет работы крупного автомобильного дилера. У нас в распоряжении многолетний массив из сотен тысяч транзакций (на языке автодилеров это называется заказ-нарядами), у нас есть история продаж автомобилей и детальные расшифровки по работам, запчастям и поведению клиентов.

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

Постараюсь ниже рассказать без воды:

  • Почему слепое верующее отношение к среднему чеку приводит к катастрофе в маркетинге (и при чем тут Симпсон).

  • Если разделить клиентов на «своих» (купивших авто у нас) и «чужих», то почему их поведение кардинально отличается.

  • Главная интрига: почему автовладельцы, регулярного приезжающие на «дешевую» сезонную переобувку, за свой жизненный цикл (LTV) приносят компании в 3 раза больше денег, чем те, кто заезжает исключительно на редкий крупный ремонт.

Давайте теперь посмотрим на детали, в самом прямом смысле этого слова!

Шаг 1. Отсекаем шум и смотрим на макропоказатели

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

По каждому заказу у нас теперь есть основные характеристики (колонки в таблице):

  • номер заказ-наряда,

  • дата создания заказа мастером приемщиком,

  • фактическая дата начала ремонта,

  • дата завершения,

  • хешированный VIN-номер автомобиля

  • сумма заказа.

Первым делом импортируем необходимые библиотеки и наведем порядок. Так же в рамках этой публикации нам придется убрать реальные цифры и перевести все в проценты:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.ticker import FuncFormatter

# Загружаем выгрузку заказ-нарядов
data = pd.read_csv('data/orders_2015_2024.csv', sep=';')

# Приводим колонки с датами к формату datetime
date_columns = ['date_created', 'date_started', 'date_updated', 'date_completed']
for col in date_columns:
    data[col] = pd.to_datetime(data[col], format='%d.%m.%Y %H:%M')

# АНОНИМИЗАЦИЯ: Смотрим на структуру базы в относительных величинах (%)
total_records = len(data)
canceled_orders = data['sum_paid'].isna().sum()

print(f"Общая структура сырой базы: 100.0%")
print(f"Доля незавершенных/неоплаченных заказов: {(canceled_orders / total_records) * 100:.1f}%")

# Отфильтровываем только завершенные и оплаченные заказы
completed_orders = data[
    (data['date_completed'].notna()) & 
    (data['sum_paid'].notna())
].copy()

print(f"Доля успешно обработанных заказов для анализа: {(len(completed_orders) / total_records) * 100:.1f}%")
Общая структура сырой базы: 100.0%
Доля незавершенных/неоплаченных заказов: 8.4%
Доля успешно обработанных заказов для анализа: 91.6%

Незавершенных зависших заказов в базе 8% (отказы клиентов, технические дубли, ошибки менеджеров при вводе заказов). В этот раз не будем разбираться, почему эти заказы зависли — отнесем это к «мелким недоразумениям», одной рукой прикроем глаза, а второй просто выкинем их из выборки и больше не будем о них вспоминать.

У нас остается чуть более 91% от исходного датасета. А этого достаточно. Сотни тысяч заказ-нарядов. Сервис не простаивал эти годы без дела. А это значит, что наше исследование будет репрезентативным, а статистические выбросы очевидными.

Динамика выручки за 10 лет

Если бы мы хотели оценить качество руководства компании, то мы бы в первую очередь посмотрели на прибыль. Но раз у мы разбираем вопрос средних чеков, то мы будем смотреть на выручку:

# Группируем данные по месяцам даты завершения заказа
monthly_revenue = (
    completed_orders.set_index("date_completed")
    .groupby(pd.Grouper(freq="ME"))["sum_paid"]
    .sum()
    .reset_index()
)
base_value = monthly_revenue["sum_paid"].iloc[0]
monthly_revenue["revenue_pct"] = (monthly_revenue["sum_paid"] / base_value) * 100

Посмотреть код для настройки и отрисовки графика выручки
# Пусть график выглядит красиво
sns.set_theme(style="white")  # Убираем стандартную серую сетку Seaborn
plt.rcParams["font.family"] = "sans-serif"  # Чистый шрифт без засечек

fig, ax = plt.subplots(figsize=(11, 5.5), dpi=150)

# Построение графика
sns.lineplot(
    data=monthly_revenue,
    x="date_completed",
    y="revenue_pct",
    color="#0F62FE",  # Современный технологичный синий (IBM Design)
    linewidth=2.5,
    marker="o",
    markersize=7,
    markerfacecolor="#0F62FE",
    markeredgecolor="white",
    markeredgewidth=1.5,
    ax=ax,
)

# Тонкая настройка сетки и границ (десеткация)
ax.grid(
    axis="y", linestyle="--", color="#E0E0E0", linewidth=0.7
)  # Только горизонтальные линии
sns.despine(left=True, bottom=False)  # Удаляем верхнюю, правую и левую границы

# 5. Оформление текста (Выравнивание по левому краю для заголовка)
ax.text(
    0.0,
    1.12,
    "Динамика ежемесячной выручки",
    fontsize=16,
    fontweight="bold",
    color="#161616",
    transform=ax.transAxes,
)
ax.text(
    0.0,
    1.06,
    "Относительные показатели продаж компании (Базовый месяц = 100%)",
    fontsize=11,
    color="#525252",
    transform=ax.transAxes,
)

# Убираем стандартные крупные подписи осей (все понятно из контекста)
ax.set_xlabel("", fontsize=11, color="#525252", labelpad=10)
ax.set_ylabel("", fontsize=11, color="#525252")

# Форматирование дат на оси X
ax.tick_params(axis="both", which="major", labelsize=10, colors="#525252")
fig.autofmt_xdate(rotation=0, ha="center")  # Красивое выравнивание дат без наклона

# Форматирование оси Y
def percent_format(x, pos):
    return f"{int(x)}%"


ax.yaxis.set_major_formatter(FuncFormatter(percent_format))

# Аккуратные подписи значений (выводим через одну, но оставляем последнюю)
step = 2  # Шаг отображения: 2 = через одну, 3 = через две и так далее
total_points = len(monthly_revenue)

for i, row in monthly_revenue.iterrows():
    # Условие: выводим точку, если она кратна шагу ИЛИ если это самый последний месяц
    if (i % step == 0) or (i == total_points - 1):

        # Небольшая проверка: если последняя точка встает слишком близко к предыдущей по шагу,
        # можно пропустить предпоследнюю подпись во избежание наложения
        if (i == total_points - 1) and (
            (i - 1) % step == 0
        ) and (total_points > 1):
            # (Опционально) этот блок автоматически предотвратит наложение
            # последней точки на предпоследнюю, если они идут подряд
            pass

        ax.text(
            row["date_completed"],
            row["revenue_pct"] + 4,  # Смещение вверх относительно линии
            f"{int(row['revenue_pct'])}%",
            ha="center",
            va="bottom",
            fontsize=9,
            fontweight="semibold",
            color="#393939",
        )
plt.tight_layout()
plt.show()
Динамика выручки по месяцам

Динамика выручки по месяцам

Все как на ладони, и пока никаких сюрпризов:

  1. Сезонность: Каждый год в начале (январь–февраль) стартует с просадки. Клиент позволяет коллективу отойти от праздников. В конце года короткий всплеск в ноябре и потом снова просадка.

  2. Устойчивый рост до 2022 года: Несмотря на сезонные спады, в целом дела идут все лучше и лучше. Восходящий тренд заканчивается ажиотажным спросом в период конца 2021 — начало 2022 года.

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


Ну а что же со средним чеком?

Для того, чтобы соответствовать стабильному росту, который мы видим выше, средний чек тоже вероятно рос? Проверим эту гипотезу. Нам снова придется перевести абсолютные финансовые показатели в относительный формат: примем средний чек за первый год наблюдений за 100%, а для остальных лет посчитаем индекс роста.

# Считаем средний чек по годам
economics_metrics = (
    completed_orders.set_index("date_completed")
    .groupby(pd.Grouper(freq="YE"))
    .agg(
        total_revenue=("sum_paid", "sum"),
        average_check=("sum_paid", "mean"),
        total_orders=("order_id", "count"),
    )
    .reset_index()
)

economics_metrics["year"] = economics_metrics["date_completed"].dt.year

# ==========================================
# АНОНИМИЗАЦИЯ: Расчет индекса среднего чека
# ==========================================
base_check = economics_metrics["average_check"].iloc[0]  # Первый год = 100%

for idx, row in economics_metrics.iterrows():
    # Считаем процент относительно базового года
    check_index = (row["average_check"] / base_check) * 100
    print(
        f"Год {int(row['year'])}: Индекс среднего чека = {check_index:.1f}%"
    )
Год 2015: Индекс среднего чека = 100.0%
Год 2017: Индекс среднего чека = 110.1%
Год 2020: Индекс среднего чека = 120.0%
Год 2022: Индекс среднего чека = 124.1%
Год 2024: Индекс среднего чека = 138.2%

И тут мы натыкаемся на первый сюрприз. Как может расти выручка без роста среднего чека?

У нас автодилер, работающий исключительно с премиум. Цены растут, выручка растет. А средний чек даже не догоняет инфляцию. К 2024 году индекс среднего чека вырос всего до 138% от уровня 2015 года.

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


Шаг 2. Разбор номенклатуры: кто портит статистику

По каждому заказ-наряду у нас есть список работ. Посмотрим самые частые работы за 10 лет. И снова нам придется перевести абсолютные цифры в проценты от общего объема оказанных услуг:

# Загружаем детальную историю по работам и запчастям внутри заказов
orders_breakdowns = pd.read_csv('data/orders_breakdowns_historical.csv', sep=';')

# Объединяем с базой завершенных заказов
completed_orders_detailed = completed_orders.merge(orders_breakdowns, on='order_id')

# Считаем долю каждой услуги от общего объема всех выполненных строк
top_items_pct = (completed_orders_detailed['item_id'].value_counts(normalize=True).head(10) * 100)

print("Топ-10 самых частых услуг в сервисе за 10 лет (в % от общего объема работ):")
print(top_items_pct.round(2).astype(str) + '%')
Топ-10 самых частых услуг в сервисе за 10 лет (в % от общего объема работ):
40-0001-02    12.2%  # Сезонное хранение комплекта колес
00-8030-01    12.2%  # Подготовка автомобиля к работе
54-1011-01     5.5%  # Входящий тест электрооборудования
40-1450-99     2.8%  # Снятие и установка рабочих колес
40-2070-99     2.8%  # Балансировка колес
40-2440-99     2.7%  # Замена шин (демонтаж/монтаж)
42-0004-01     2.6%  # Проверка тормозной системы
40-2181-01     2.5%  # Балансировочные грузики
00-9015-00     1.8%  # Осмотр ходовой части
54-0650-01     1.6%  # Поддержание напряжения бортовой сети при диагностике

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

В базе найдутся десятки тысяч чеков на мелкие фиксированные суммы, где кроме кода 40-0001-02 (Сезонное хранение) больше ничего нет.

И тут мы подходим к тому, с чего я начал эту статью.

Частые мелкие суммы в чеках «тянут» арифметическое среднее вниз. В итоге мы видим скромную сумму усреднённого чека отдела автосервиса и думаем, что зарабатываем мало на одном клиенте.

💡 Аналитическая пауза. Привет, Симпсон!

d5f799d34f14074039c030d8ee7dd4c2.jpg

Эдвард Симпсоон (1922–2019)

В 1951 году, он математически описал феномен, который сегодня называют Парадоксом Симпсона (Simpson’s paradox). Это ситуация в анализе данных, когда в отдельных группах наблюдается одна четкая тенденция, но стоит нам объединить эти группы в общий массив, как тренд полностью исчезает или переворачивается на противоположный. В бизнесе этот парадокс — главный враг топ-менеджмента. Когда руководство смотрит на «средний чек» или «общую маржинальность» по компании, агрегированные метрики маскируют реальное положение дел. Чтобы не совершить фатальную ошибку, аналитик обязан делить данные на когорты и сегменты — только там скрываются истинные инсайты.

Представим, что мы никогда не слышали об Эдварде Симпсоне и сделаем то, до чего руки чешутся: проведем следующий эксперимент. Выделим коды услуг шиномонтажа и очистим от них датасет, чтобы посмотреть на скорректированный средний чек «чистого» ремонта. И снова, мы не будем «палить» суммы в рублях, а рассчитаем коэффициент разрыва — во сколько раз средний чек сложного ремонта превышает валовый средний чек компании в каждом конкретном году.

# Коды услуг, относящиеся к шиномонтажу и хранению
tire_services = ['40-0001-02', '40-2070-99', '40-2440-99', '40-1450-99', '40-2181-01']

# Находим заказ-наряды, в которых есть шиномонтажные работы
orders_with_tires = set(completed_orders_detailed[
    completed_orders_detailed['item_id'].isin(tire_services)
]['order_id'])

# АНОНИМИЗАЦИЯ: Фильтруем через квантиль, чтобы не зашивать в код абсолютную сумму в рублях
# Отсекаем транзакции ниже 25-го перцентиля, убирая мелкий экспресс-шум
price_threshold = completed_orders['sum_paid'].quantile(0.25)

# Разделяем заказы: исключаем шиномонтаж и ультра-мелкие чеки
complex_repairs = completed_orders[
    ~completed_orders['order_id'].isin(orders_with_tires) & 
    (completed_orders['sum_paid'] > price_threshold)
].copy()

# Считаем исходный и скорректированный средний чек по годам
yearly_raw = completed_orders.groupby(completed_orders['date_completed'].dt.year)['sum_paid'].mean()
yearly_adjusted = complex_repairs.groupby(complex_repairs['date_completed'].dt.year)['sum_paid'].mean()

# Считаем коэффициент превышения (разрыв между чистым ремонтом и валовым чеком)
adjusted_gap = yearly_adjusted / yearly_raw

print("Превышение скорректированного чека над базовым (коэффициент):")
for year in:
    print(f"Год {year}: Чистый ремонт выше базового в {adjusted_gap[year]:.2f} раз(а)")
Превышение скорректированного чека над базовым (коэффициент):
Год 2020: Чистый ремонт выше базового в 1.98 раз(а)
Год 2021: Чистый ремонт выше базового в 2.11 раз(а)
Год 2022: Чистый ремонт выше базового в 2.23 раз(а)
Год 2023: Чистый ремонт выше базового в 2.35 раз(а)
Год 2024: Чистый ремонт выше базового в 2.40 раз(а)

Ну вот и наш красивый глянцевый отчет, который отправился в корзину!

Мы отсекли сезонную рутину. И теперь скорректированный средний чек ремонта вырос более чем в два раз. И мы снова премиальный автосервис!


Искушение классического маркетолога

Глядя на эти цифры, неопытный аналитик или горячий директор по маркетингу бежит к руководству с сильной (как ему кажется) идеей:

«Смотрите! Сезонное обслуживание забивает существенную часть мощности ремзоны, загружает подъемники и мастеров, а приносит копейки! Давайте закроем услугу хранения колес и шиномонтаж, а освободившиеся посты отдадим под сложный ремонт, где средний чек на порядок выше! Мы поднимем выручку в разы!»

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

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

Посмотрим чуть более детально: а кто эти клиенты? Они что, приезжают только на переобувку?

Шаг 3. «Свои» vs «Чужие» и парадокс шиномонтажа

Теперь мы объединим данные отдела продаж и отдела сервиса. Свяжем заказ-наряды с базой проданных автомобилей по VIN-номерам и разделим клиентов на две группы:

  1. «Свои» — купили автомобиль в нашем дилерском центре.

  2. «Чужие» — купили авто в другом месте, но приехали к нам на обслуживание.

# Маркируем заказы
completed_orders_detailed["purchased_vehicle"] = (
    completed_orders_detailed["purchase_date"].notna()
)

# Считаем абсолютные значения
sales_service_sync = (
    completed_orders_detailed.groupby("purchased_vehicle")
    .agg(
        total_orders=("order_id", "nunique"),
        unique_customers=("vin_number", "nunique"),
    )
    .reset_index()
)

# Переводим в проценты от общего итога
total_orders_sum = sales_service_sync["total_orders"].sum()
total_customers_sum = sales_service_sync["unique_customers"].sum()

sales_service_sync["total_orders (%)"] = (
    (sales_service_sync["total_orders"] / total_orders_sum) * 100
).round(1)
sales_service_sync["unique_customers (%)"] = (
    (sales_service_sync["unique_customers"] / total_customers_sum) * 100
).round(1)

# Маппинг названий
sales_service_sync["purchased_vehicle"] = sales_service_sync[
    "purchased_vehicle"
].map({True: "Купил авто у нас", False: "Купил в другом месте"})

# Выводим только анонимизированные колонки
print(
    sales_service_sync[
        ["purchased_vehicle", "total_orders (%)", "unique_customers (%)"]
    ]
)

purchased_vehicle

total_orders (%)

unique_customers (%)

Купил в другом месте

60.8

70.4

Купил авто у нас

39.2

29.6

Интересный факт: «чужих» клиентов в сервисе больше по количеству, но «свои» клиенты намного активнее — в среднем на одного «своего» клиента приходится 11.5 заказов за время владения машиной против 7.4 заказов у клиента со стороны.

Сравнение лояльности двух типов клиентов

Сравнение лояльности двух типов клиентов

Кроме того, процент удержания (Retention Rate) среди «своих» стабильно выше на 12–15%. Покупка машины у дилера создает мощный инерционный запас лояльности.

Так может самое важное — это продавать больше автомобилей, и тогда и работы у сервиса будет больше? Все таки 12%-15% разница.


А теперь — главный эксперимент статьи

Давайте скрестим две метрики: факт использования шиномонтажа/хранения колес и суммарную выручку (LTV), которую клиент принес компании за все 10 лет наблюдений.

Наш красивый отчет настаивал: «Копеечный шиномонтаж снижает средний чек, загружает подъемники и не приносит прибыли».

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

# 1. Находим VIN-номера клиентов, которые ХОТЯ БЫ РАЗ пользовались шиномонтажом/хранением
tire_clients_vins = set(
    completed_orders_detailed[
        completed_orders_detailed["item_id"].isin(tire_services)
    ]["vin_number"]
)

# 2. Считаем суммарные траты каждого уникального клиента за все 10 лет
client_ltv = (
    completed_orders.groupby("vin_number")["sum_paid"].sum().reset_index()
)

# 3. Разделяем клиентов на две группы
client_ltv["uses_tire_service"] = client_ltv["vin_number"].isin(
    tire_clients_vins
)

# 4. Считаем средний LTV на одного клиента в каждой группе
ltv_summary = client_ltv.groupby("uses_tire_service")["sum_paid"].mean()

# ==========================================
# АНОНИМИЗАЦИЯ И РАСЧЕТ ОТНОСИТЕЛЬНЫХ МЕТРИК
# ==========================================
base_ltv = ltv_summary[False]  # База для сравнения (клиенты БЕЗ шиномонтажа)
target_ltv = ltv_summary[True]  # Целевая группа (клиенты С шиномонтажом)

# Рассчитываем кратность (во сколько раз больше)
ltv_multiplier = target_ltv / base_ltv

# Рассчитываем прирост в процентах
ltv_growth_pct = (ltv_multiplier - 1) * 100

# Вывод анонимизированных результатов в консоль
print(
    f"Относительный LTV клиента С шиномонтажом (база БЕЗ шиномонтажа = 1.0x): {ltv_multiplier:.2f}x"
)
print(
    f"Прирост среднего LTV при использовании шиномонтажа: +{ltv_growth_pct:.1f}%"
)
Относительный LTV клиента С шиномонтажом (база БЕЗ шиномонтажа = 1.0x): 3.09x
Прирост среднего LTV при использовании шиномонтажа: +209.4%

Ну вот и оно! И это «оно» даже не зависит от того, купили ли машину у нас.

* Базовый клиент (не пользуется шиномонтажом) за всё время работы с сервисом приносит компании фиксированную «условную единицу» дохода.

* Лояльный клиент (хотя бы раз заехал на шиномонтаж или сдал колеса на хранение) окупает базовые показатели более чем в 3 раза! В масштабах всей клиентской базы этот «скромный» разрыв превращается в колоссальную недополученную прибыль с каждого автомобиля, который уезжает менять колеса в сторонние мастерские.

# Переводим абсолютный LTV в относительный индекс (Индекс 1.0x)
base_ltv = ltv_summary[False]  # База — клиенты БЕЗ шиномонтажа
ltv_index_true = ltv_summary[True] / base_ltv
ltv_index_false = 1.0  # Базовая точка всегда 1.0

# Подготовка данных для визуализации
incomes = [ltv_index_true, ltv_index_false]
labels = ["Пользуются шиномонтажом", "Не пользуются шиномонтажом"]
Посмотреть код визуализации относительного LTV
# Корпоративная палитра: фокусный синий (IBM) и нейтральный серый для сравнения
colors = ["#0F62FE", "#8D8D8D"]

# Настройка чистого журнального стиля
sns.set_theme(style="white")
plt.rcParams["font.family"] = "sans-serif"

fig, ax = plt.subplots(figsize=(9, 5), dpi=150)  # Высокое разрешение для медиа

# Построение столбчатой диаграммы
bars = ax.bar(labels, incomes, color=colors, width=0.45)

# Тонкая настройка сетки и десеткация (удаление рамок)
ax.grid(axis="y", linestyle="--", color="#E0E0E0", linewidth=0.7)
sns.despine(left=True, bottom=False)  # Оставляем только линию под столбцами

# Оформление заголовка с выравниванием по левому краю
ax.text(
    -0.15,
    1.15,
    "Влияние дополнительных услуг на удержание клиентов",
    fontsize=16,
    fontweight="bold",
    color="#161616",
    transform=ax.transAxes,
)
ax.text(
    -0.15,
    1.08,
    "Сравнение среднего LTV за 10 лет (Относительный индекс, группа без услуг = 1.0x)",
    fontsize=11,
    color="#525252",
    transform=ax.transAxes,
)

# Очищаем подписи осей, так как вся информация есть в названии
ax.set_ylabel("")
ax.tick_params(axis="both", which="major", labelsize=11, colors="#525252")

# Форматирование оси Y в формат индексов (1.0x, 1.5x и т.д.)
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, pos: f"{x:.1f}x"))

# Динамический расчет верхнего лимита, чтобы подписи не упирались в край
ax.set_ylim(0, max(incomes) * 1.2)

# Аккуратные подписи значений прямо над столбиками
for bar in bars:
    yval = bar.get_height()
    ax.text(
        bar.get_x() + bar.get_width() / 2,
        yval + (max(incomes) * 0.03),  # Смещение пропорционально высоте графика
        f"{yval:.2f}x",
        ha="center",
        va="bottom",
        fontsize=11,
        fontweight="bold",
        color="#161616",
    )

# Текстовая аннотация разрыва в LTV
# Автоматически считаем разницу в процентах для точности текста
pct_difference = int((ltv_index_true - 1.0) * 100)

# Добавляем текстовый блок с инсайтом
# Координаты (x=0.5, y=max(incomes)*0.7) помещают текст между двумя столбиками
ax.text(
    0.5,
    max(incomes) * 0.72,
    f"Клиенты, воспользовавшиеся\nшиномонтажом хотя бы раз,\nгенерируют в среднем на {pct_difference}%\nбольше выручки за свой LTV",
    fontsize=10,
    fontweight="normal",
    color="#525252",
    ha="center",
    va="center",
    bbox=dict(
        boxstyle="round,pad=0.6",
        facecolor="#F4F4F4",
        edgecolor="none",
        alpha=0.9,
    ),
)

# Рисуем аккуратную стрелку-указатель от текста к целевому столбцу
ax.annotate(
    "",
    xy=(0.0, ltv_index_true * 0.8),  # Куда указывает стрелка (на синий столбец)
    xytext=(
        0.28,
        max(incomes) * 0.72,
    ),  # Откуда идет стрелка (от границы текста)
    arrowprops=dict(
        arrowstyle="->",
        color="#0F62FE",
        lw=1.2,
        connectionstyle="arc3,rad=-0.15",  # Элегантный легкий изгиб
    ),
)

plt.tight_layout()
plt.show()
Как дополнительные услуги повышают LTV

Как дополнительные услуги повышают LTV


Что получилось в итоге

А в итоге получилось, что простая переобувка дает гигантский буст к итоговой выручке компании.

И тут уже даже больше не математика, а поведенческие сценарии (Customer Journey):

  1. Триггер регулярного контакта (Habit Loop): Весна и осень — все едут менять резину. Если колеса у нас, то клиент «заскочит» к нам как минимум дважды в год. Пусть даже для ремонта проколов он обращается просто куда ближе.

  2. Точка входящего осмотра (Trojan Horse): Мы при каждом шиномонтаже предлагаем бесплатную диагностику (те самые массовые коды 00-8030-01 и 42-0004-01).

  3. Конверсия в сложный ремонт: В 40% случаев при снятии колес мастер обнаруживает износ колодок, потёки амортизаторов, трещины сайлентблоков или необходимость замены тормозных дисков. Это не навязанные услуги, это порой может спасти жизнь. Машина уже висит на подъемнике. Согласовать доработку сразу не покидая сервис намного проще.

Уберите шиномонтаж — и вы разрушите привычку регулярного визита. Клиент переобуется за углом. Там же ему посоветуют «своего» слесаря, и на сложный ремонт с чеком в 50 000+ рублей он к вам больше не вернется.

Какие выводы мы сделали и как нас спасла аналитика

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

Видно, что изначально мы:
  1. Допустили ошибку изолировав услуги: В бизнесе существуют так называемые «якорные» продукты (Anchor Services / Loss Leaders). Они не генерируют моментальную маржу. Эти продукты создают регулярные точки контакта с клиентом и формируют привычку возвращаться.

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

  3. А еще нам помогла воронка допродаж (Cross-sell): Для нас оказалось жизненно-важно при каждой сезонной переобувке делать бесплатной базовую диагностику (тормоза, ходовая, аккумуляторы). Это не «подарок клиенту». Это главный инструмент генерации высокомаржинальных заказов.

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


И напоследок немного интерактива!

Было бы интересно узнать, а вы сталкивались в своей практике с подобными парадоксами, когда «дешевые» услуги на поверку оказывались главными драйверами LTV?

Ну и чисто по-человечески: как сезонный шиномонтаж обычно проходит лично у вас?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

0%Переобуваюсь у дилера/в крупном сервисе + сдаю на хранение (и обычно соглашаюсь на рекомендованный ремонт).0

37.5%Переобуваюсь в проверенной шиномонтажке «за углом», а ремонтируюсь строго в другом месте.3

62.5%Меняю колеса сам в гараже/на даче, никому не доверяю.5

12.5%Я пришел сюда просто почитать, где лучше переобувать машину.1

Проголосовали 8 пользователей. Воздержались 2 пользователя.

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Чем грозит обслуживание автомобилях в дешевых СТО08.404-08-2026
2Почему всесезоные шины — это маркетинговый трюк014.9311-12-2025
3Почему дорожают ремонт и техобслуживание автомобилей в России: крупная сеть автосервисов все объяснила012.2207-07-2026
4Россиянам рассказали, как сэкономить на техобслуживании автомобиля2630-06-2026
5Как решить TSP для 10 000 точек БЕЗ прыжков: метод «Динамического Шампура» с инерцией012.3905-02-2026
6El enemigo silencioso de los diésel: cómo evitar una factura de hasta 6.000 euros en el taller011.2317-07-2026
7El enemigo silencioso de los diésel: cómo evitar una factura de hasta 6.000 euros en el taller011.2317-07-2026
8El enemigo silencioso de los diésel: cómo evitar una factura de hasta 6.000 euros en el taller011.2317-07-2026
9El enemigo silencioso de los diésel: cómo evitar una factura de hasta 6.000 euros en el taller011.2317-07-2026
10Гараж и автомобиль: как организовать пространство, экономить на обслуживании и избежать типичных ошибок5801-01-1970

Классификация: Мнения. Схожих патентов: 0. Схожих новостей: 10. Тональность: 0. Информативность: 13.11. Источник: pythondigest.ru.