**Контекст:** Маркетплейс хочет оптимизировать цены на основе ценовой эластичности спроса для максимизации выручки.
**Данные:** 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. Для тренировки рекомендуем сначала решить самостоятельно, потом сверить с эталонным решением и подсказками.
На zasqlpython.ru есть 482 Python задачи с проверкой через Pyodide, конспекты Python и pandas, AI мок-собеседование с разбором ваших ответов.
← Все задания