Динамическое ценообразование — эластичность спроса

Senior Python Marketplace

Условие задания

**Контекст:** Маркетплейс хочет оптимизировать цены на основе ценовой эластичности спроса для максимизации выручки.

**Данные:** DataFrame `price_history` — колонки: `date`, `product_id`, `category`, `price`, `units_sold`, `competitor_price`, `day_of_week`, `is_holiday` (0/1), `promotion` (none/small/big).

**Задание:**
1. Оцените ценовую эластичность спроса для каждой категории: E = (% change Q) / (% change P)
2. Постройте кривую спроса и найдите оптимальную цену (максимизирующую revenue)
3. Учтите конкурентные цены: кросс-эластичность
4. Рассчитайте потенциальный uplift выручки от оптимизации

Пример данных

Структура для ориентира — реальные значения из эталонного решения.

import pandas as pd
import numpy as np
from scipy import optimize
import statsmodels.formula.api as smf

np.random.seed(42)
categories = ['Electronics', 'Clothing', 'Food', 'Home']
base_prices = {'Electronics': 5000, 'Clothing': 2000, 'Food': 500, 'Home': 3000}
elasticities_true = {'Electronics': -1.5, 'Clothing': -2.0, 'Food': -0.5, 'Home': -1.2}

records = []
for date in pd.date_range('2023-01-01', '2024-06-30', freq='D'):
    for cat in categories:
        base_p = base_prices[cat]
        # Случайные вариации цены ±30%
        price = base_p * np.random.uniform(0.7, 1.3)
        competitor_price = base_p * np.random.uniform(0.8, 1.2)

        # Спрос зависит от цены (с эластичностью), конкурента, дня недели
        elasticity = elasticities_true[cat]
        base_demand = 100
        price_effect = (price / base_p) ** elasticity
        comp_effect = (competitor_price / base_p) ** (-elasticity * 0.3)  # кросс-эластичность
        dow_effect = 1.2 if date.dayofweek >= 5 else 1.0

        units = max(1, int(base_demand * price_effect * comp_effect * dow_effect
                          * np.random.lognormal(0, 0.15)))

        records.append({
            'date': date, 'category': cat, 'price': round(price, 2),
            'units_sold': units, 'competitor_price': round(competitor_price, 2),
            'day_of_week': date.day_name(),
            'is_weekend': int(date.dayofweek >= 5),
        })

df = pd.DataFrame(records)
df['revenue'] = df['price'] * df['units_sold']
df['ln_price'] = np.log(df['price'])
df['ln_units'] = np.log(df['units_sold'])
df['ln_comp_price'] = np.log(df['competitor_price'])

print(f"Записей: {len(df)}, период: {df['date'].min().date()} — {df['date'].max().date()}")

# 1. Оценка эластичности через log-log регрессию
print("\n=== Ценовая эластичность по категориям ===")
elasticities = {}

for cat in categories:
    subset = df[df['category'] == cat]
    model = smf.ols('ln_units ~ ln_price + ln_comp_price + is_weekend', data=subset).fit()

    own_e = model.params['ln_price']
    cross_e = model.params['ln_comp_price']
    r2 = model.rsquared

    elasticities[cat] = own_e
    e_type = 'эластичный' if abs(own_e) > 1 else 'неэластичный'
    print(f"\n  {cat}:")
    print(f"    Own-price elasticity: {own_e:.3f} ({e_type})")
    print(f"    Cross-price elasticity: {cross_e:.3f}")
    print(f"    R²: {r2:.3f}, true E: {elasticities_true[cat]}")

# 2. Оптимальная цена (максимизация revenue)
print("\n=== Оптимальные цены ===")

for cat in categories:
    E = elasticities[cat]
    current_p = df[df['category'] == cat]['price'].median()
    current_q = df[df['category'] == cat]['units_sold'].median()

    # Кривая спроса: Q(P) = Q0 * (P / P0) ^ E
    Q0 = current_q
    P0 = current_p

    def neg_revenue(p):
        q = Q0 * (p / P0) ** E
        return -(p * q)

    result = optimize.minimize_scalar(neg_revenue, bounds=(P0 * 0.5, P0 * 2.0), method='bounded')
    optimal_p = result.x
    optimal_rev = -result.fun
    current_rev = current_p * current_q

    # Аналитическое решение: P_opt = P * E / (1 + E)
    analytical_p = current_p * E / (1 + E) if E < -1 else current_p

    uplift = (optimal_rev / current_rev - 1) * 100
    print(f"\n  {cat}:")
    print(f"    Текущая цена: {current_p:,.0f} → Оптимальная: {optimal_p:,.0f} (analytical: {analytical_p:,.0f})")
    print(f"    Revenue: {current_rev:,.0f} → {optimal_rev:,.0f} (uplift: {uplift:+.1f}%)")

# 3. Общий потенциальный uplift
print("\n=== Потенциальный uplift ===")
total_current = df.groupby('category')['revenue'].sum()
total_uplift = 0
for cat in categories:
    E = elasticities[cat]
    current_p = df[df['category'] == cat]['price'].median()
    P0 = current_p

    optimal_p = optimize.minimize_scalar(
        lambda p: -(p * (df[df['category']==cat]['units_sold'].median() * (p/P0)**E)),
        bounds=(P0 * 0.5, P0 * 2), method='bounded'
    ).x

    ratio = optimal_p / current_p
    cat_uplift = total_current[cat] * ((ratio ** (1 + E)) - 1)
    total_uplift += cat_uplift
    print(f"  {cat}: price change {(ratio-1)*100:+.1f}%, revenue uplift {cat_uplift:+,.0f} руб.")

print(f"\nОбщий потенциальный uplift: {total_uplift:+,.0f} руб. ({total_uplift/total_current.sum()*100:+.1f}%)")

Темы

pandas scipy оптимизация эластичность pricing регрессия

Подсказки

Все тестовые задания →

Частые вопросы

Какой уровень знаний нужен для задачи "Динамическое ценообразование — эластичность спроса"?

Это задание для уровня Senior. Senior-уровень — глубокое понимание темы, опыт решения нестандартных задач, обсуждение trade-off на собеседовании.

На каких собеседованиях встречается такая задача?

Подобные задания в категории «Python» регулярно дают на собеседованиях аналитика данных в Яндекс, Сбер, Ozon, Авито, Тинькофф, Wildberries, T-Bank, X5, ВТБ и других крупных IT-компаниях. Тематика: pandas, scipy, оптимизация, эластичность, pricing.

Сколько времени даётся на решение?

На реальном собеседовании на подобную задачу отводится 30-60 минут с обсуждением подходов, оптимизаций и trade-off. Для тренировки рекомендуем сначала решить самостоятельно, потом сверить с эталонным решением и подсказками.

Где ещё потренироваться по теме «Python»?

На zasqlpython.ru есть 482 Python задачи с проверкой через Pyodide, конспекты Python и pandas, AI мок-собеседование с разбором ваших ответов.

← Все задания