**Контекст:** Задание с собеса в продуктовую команду. Есть агрегированные метрики поведения пользователей — нужно разбить их на 4-6 осмысленных сегментов для персонализации (рассылки, рекомендации, UX).
**Данные:**
[см. код в задании]
**Задание:**
1. Провести KMeans-сегментацию с K=5.
2. Обязательно **стандартизация** (StandardScaler) — метрики в разных шкалах.
3. Вернуть `DataFrame` с оригинальными колонками + `segment` (0..4) + `segment_name` (человеко-читаемое).
4. Интерпретировать центроиды: описать каждый сегмент одним предложением.
5. Проверить что cluster sizes не слишком дисбалансированы (ни один < 5%).
Структура для ориентира — реальные значения из эталонного решения.
import pandas as pd
import numpy as np
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
def segment_users(df: pd.DataFrame, n_clusters: int = 5, random_state: int = 42) -> pd.DataFrame:
features = [
'days_since_signup', 'sessions_per_week', 'avg_session_duration',
'total_purchases', 'avg_order_value', 'days_since_last_visit',
'features_used'
]
X = df[features].fillna(0).values
# Стандартизация — иначе avg_order_value (тысячи) забьёт sessions_per_week (единицы)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Log-трансформация для skewed распределений
# avg_order_value и total_purchases часто имеют длинный хвост
skewed_cols = ['avg_order_value', 'total_purchases']
for col in skewed_cols:
idx = features.index(col)
X_scaled[:, idx] = np.log1p(np.abs(X_scaled[:, idx])) * np.sign(X_scaled[:, idx])
# KMeans
model = KMeans(n_clusters=n_clusters, random_state=random_state, n_init=10)
labels = model.fit_predict(X_scaled)
# Inverse transform центроидов для интерпретации в оригинальных единицах
centroids_original = scaler.inverse_transform(model.cluster_centers_)
centroids_df = pd.DataFrame(centroids_original, columns=features)
# Автоматическая генерация названий по доминантным фичам
segment_names = interpret_segments(centroids_df)
result = df.copy()
result['segment'] = labels
result['segment_name'] = result['segment'].map(segment_names)
return result, centroids_df
def interpret_segments(centroids: pd.DataFrame) -> dict:
"""
Правило-ориентированное название сегментов по центроидам.
В продакшне — обсуждают с PM и маркетингом.
"""
names = {}
for idx, row in centroids.iterrows():
if row['total_purchases'] > 20 and row['avg_order_value'] > 5000:
names[idx] = 'VIP: high frequency + high AOV'
elif row['days_since_last_visit'] > 30:
names[idx] = 'Churned: 30+ days inactive'
elif row['days_since_signup'] < 30 and row['total_purchases'] < 2:
names[idx] = 'New, unactivated'
elif row['sessions_per_week'] > 10 and row['total_purchases'] < 5:
names[idx] = 'Engaged browsers (high sessions, low purchases)'
elif row['total_purchases'] >= 5 and row['avg_order_value'] < 2000:
names[idx] = 'Regular low-AOV buyers'
else:
names[idx] = f'Segment {idx}'
return names
# Использование:
segmented, centroids = segment_users(users, n_clusters=5)
# Проверка баланса сегментов
sizes = segmented['segment'].value_counts(normalize=True) * 100
print('Размеры сегментов (%):')
print(sizes.round(1))
# Характеристики центроидов
print('\nЦентроиды в оригинальных единицах:')
print(centroids.round(1))
clustering kmeans pca segmentation sklearn
Это задание для уровня Senior. Senior-уровень — глубокое понимание темы, опыт решения нестандартных задач, обсуждение trade-off на собеседовании.
Подобные задания в категории «Python» регулярно дают на собеседованиях аналитика данных в Яндекс, Сбер, Ozon, Авито, Тинькофф, Wildberries, T-Bank, X5, ВТБ и других крупных IT-компаниях. Тематика: clustering, kmeans, pca, segmentation, sklearn.
На реальном собеседовании на подобную задачу отводится 30-60 минут с обсуждением подходов, оптимизаций и trade-off. Для тренировки рекомендуем сначала решить самостоятельно, потом сверить с эталонным решением и подсказками.
На zasqlpython.ru есть 530+ Python задачи с проверкой через Pyodide, конспекты Python и pandas, AI мок-собеседование с разбором ваших ответов.
← Все задания