**Контекст:** Медиа-платформа хочет сегментировать аудиторию по паттернам потребления контента для персонализации рекомендаций.
**Данные:** DataFrame `sessions` — колонки: `user_id`, `session_id`, `session_start`, `session_end`, `pages_viewed`, `videos_watched`, `device` (mobile/desktop/tablet), `referrer` (organic/social/direct/email).
**Задание:**
1. Рассчитайте фичи по пользователям: avg/median session duration, sessions per week, avg pages/session, % mobile
2. Кластеризуйте пользователей KMeans (StandardScaler + Elbow)
3. Опишите сегменты и дайте рекомендации по контент-стратегии
Структура для ориентира — реальные значения из эталонного решения.
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
np.random.seed(42)
n_users = 3000
n_sessions = 40000
# Три типа пользователей (для реалистичности)
user_types = np.random.choice(['casual', 'regular', 'power'], n_users, p=[0.5, 0.35, 0.15])
user_map = {uid: utype for uid, utype in zip(range(n_users), user_types)}
records = []
for i in range(n_sessions):
uid = np.random.randint(0, n_users)
utype = user_map[uid]
if utype == 'casual':
duration = np.random.exponential(3) # короткие сессии
pages = np.random.poisson(2)
videos = np.random.poisson(0.5)
device = np.random.choice(['mobile', 'desktop', 'tablet'], p=[0.7, 0.2, 0.1])
elif utype == 'regular':
duration = np.random.exponential(12)
pages = np.random.poisson(6)
videos = np.random.poisson(2)
device = np.random.choice(['mobile', 'desktop', 'tablet'], p=[0.5, 0.4, 0.1])
else:
duration = np.random.exponential(30)
pages = np.random.poisson(15)
videos = np.random.poisson(5)
device = np.random.choice(['mobile', 'desktop', 'tablet'], p=[0.3, 0.6, 0.1])
start = pd.Timestamp('2024-01-01') + pd.Timedelta(hours=np.random.randint(0, 2160))
records.append({
'user_id': uid, 'session_id': i,
'session_start': start,
'session_end': start + pd.Timedelta(minutes=max(0.5, duration)),
'pages_viewed': max(1, pages),
'videos_watched': max(0, videos),
'device': device,
'referrer': np.random.choice(['organic', 'social', 'direct', 'email'], p=[0.4, 0.3, 0.2, 0.1]),
})
sessions = pd.DataFrame(records)
sessions['duration_min'] = (
(sessions['session_end'] - sessions['session_start']).dt.total_seconds() / 60
).round(2)
# 1. Фичи по пользователям
user_features = sessions.groupby('user_id').agg(
avg_duration=('duration_min', 'mean'),
median_duration=('duration_min', 'median'),
session_count=('session_id', 'count'),
avg_pages=('pages_viewed', 'mean'),
avg_videos=('videos_watched', 'mean'),
mobile_share=('device', lambda x: (x == 'mobile').mean()),
).round(3)
# Сессий в неделю (период данных ~90 дней = ~13 недель)
user_features['sessions_per_week'] = (user_features['session_count'] / 13).round(2)
print("Описательная статистика фичей:")
print(user_features.describe().round(2))
# 2. Кластеризация
feature_cols = ['avg_duration', 'median_duration', 'sessions_per_week',
'avg_pages', 'avg_videos', 'mobile_share']
X = user_features[feature_cols].values
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Elbow method
inertias = []
K_range = range(2, 8)
for k in K_range:
km = KMeans(n_clusters=k, random_state=42, n_init=10)
km.fit(X_scaled)
inertias.append(km.inertia_)
print("\nElbow method:")
for k, inertia in zip(K_range, inertias):
bar = '#' * int(inertia / max(inertias) * 40)
print(f" k={k}: {inertia:>10.0f} {bar}")
# Финальная кластеризация k=3
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
user_features['cluster'] = kmeans.fit_predict(X_scaled)
# 3. Профили
profiles = user_features.groupby('cluster')[feature_cols].mean().round(2)
profiles['size'] = user_features['cluster'].value_counts().sort_index()
profiles['share_pct'] = (profiles['size'] / len(user_features) * 100).round(1)
print("\nПрофили кластеров:")
print(profiles)
segment_names = {}
for cluster in range(3):
p = profiles.loc[cluster]
if p['avg_duration'] < 5 and p['sessions_per_week'] < 2:
name = 'Casual Browsers'
rec = 'Short-form контент, push-уведомления, быстрые форматы'
elif p['avg_duration'] > 15 and p['avg_videos'] > 3:
name = 'Power Users'
rec = 'Эксклюзивный контент, подписка Premium, community'
else:
name = 'Regular Readers'
rec = 'Персонализированные дайджесты, вовлекающие серии статей'
segment_names[cluster] = name
print(f"\nCluster {cluster} — {name} ({int(p['size'])} users, {p['share_pct']}%):")
print(f" Avg session: {p['avg_duration']:.1f} мин, {p['sessions_per_week']:.1f} сессий/нед")
print(f" Pages: {p['avg_pages']:.1f}, Videos: {p['avg_videos']:.1f}, Mobile: {p['mobile_share']*100:.0f}%")
print(f" Рекомендация: {rec}")
pandas sklearn KMeans вовлечённость session StandardScaler elbow
Это задание для уровня Middle. Для middle-аналитиков с опытом 1-3 года, требует уверенного владения темой и понимания edge cases.
Подобные задания в категории «Python» регулярно дают на собеседованиях аналитика данных в Яндекс, Сбер, Ozon, Авито, Тинькофф, Wildberries, T-Bank, X5, ВТБ и других крупных IT-компаниях. Тематика: pandas, sklearn, KMeans, вовлечённость, session.
На реальном собеседовании на подобную задачу отводится 15-30 минут — оцениваются подход, корректность, обработка edge cases. Для тренировки рекомендуем сначала решить самостоятельно, потом сверить с эталонным решением и подсказками.
На zasqlpython.ru есть 482 Python задачи с проверкой через Pyodide, конспекты Python и pandas, AI мок-собеседование с разбором ваших ответов.
← Все задания