Условие
В F2P MMORPG игроки могут объединяться в гильдии: получают доступ к войнам, рейдам, миссиям. Создать гильдию — недорого. Гильдии могут повышать уровень через изучение талантов (требует гильдийной энергии).
Гипотезы геймдизайнера:
- Большинство создаваемых гильдий неактивны.
- Игроки, покупающие премиум-товары, создают более активные гильдии.
- Если первая гипотеза верна, стоит ли существенно повысить стоимость создания гильдии (вплоть до премиум-товара)?
Задача аналитика: подтвердить / опровергнуть гипотезы, презентовать результаты.
Декомпозиция:
Шаг 1. Определить критерии активности гильдий на основе guild_data.csv и energy.csv. Метод выбрать самостоятельно (экспертно по средним; экспертно по квартилям; кластеризация; свой вариант). Распределить гильдии по классам/кластерам, описать пороги, выделить бесполезные признаки. Дать ответ по гипотезе 1.
Шаг 2. На основе разметки и payers.csv рассчитать для каждой группы:
- Paying share (доля платящих);
- ARPPU.
Дать ответ по гипотезе 2.
Шаг 3. Финальный вывод по всем гипотезам.
Решение
Подход
- Многомерная активность: гильдии могут быть активны по-разному (миссии vs рейды vs таланты). Кластеризация — наиболее адекватный инструмент.
- «Большинство гильдий неактивны» = долгий хвост распределения (правый тяжёлый, медиана низкая).
- Связь активности и платящих — это корреляция, не причинно-следственная: возможно, платящие создают активные гильдии, или возможно, активные гильдии «затягивают» в платежи.
Шаг 1. Определение активности
Признаки
Из guild_data.csv:
n_added_characters— приглашённыеn_removed_characters— исключённыеn_talents— изученные талантыlevel— уровень гильдииn_missions_completed— выполненные миссии (предположим)n_raids_completed— рейды
Из energy.csv — операции с энергией (приходы / траты), частота операций.
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
guilds = pd.read_csv("guild_data.csv", parse_dates=["guild_created.datetime"])
energy = pd.read_csv("energy.csv")
# Агрегации по энергии на гильдию
energy_agg = (energy.groupby("guild_id")
.agg(energy_in=("energy_in", "sum"),
energy_out=("energy_out", "sum"),
n_energy_ops=("guild_id", "count")))
df = guilds.merge(energy_agg, left_on="guild_created.guild_id", right_index=True, how="left").fillna(0)
# Feature set
features = ["n_added_characters", "n_removed_characters", "n_talents",
"level", "n_missions_completed", "energy_in", "energy_out", "n_energy_ops"]
X = df[features]
# Стандартизация
X_scaled = StandardScaler().fit_transform(X)Метод выбора
Я бы взял k-means или GMM с проверкой через elbow / silhouette. Это даёт понятные «архетипы».
# Elbow
inertia = []
for k in range(1, 10):
km = KMeans(n_clusters=k, random_state=42).fit(X_scaled)
inertia.append(km.inertia_)
plt.plot(range(1, 10), inertia, marker="o") # ищем "локоть"Обычно для 5–10 признаков elbow на k=3–4. Останавливаемся на k=3:
- «Мёртвые»: создались, ничего не происходит. Низкий энергии, низкий level, мало миссий.
- «Средние»: умеренная активность.
- «Активные»: высокий level, много миссий, много энергии.
km = KMeans(n_clusters=3, random_state=42, n_init=10).fit(X_scaled)
df["cluster"] = km.labels_
cluster_summary = df.groupby("cluster")[features].mean()
print(cluster_summary)
print(df["cluster"].value_counts(normalize=True))Бесполезные признаки
Часто выясняется, что:
n_removed_characters— почти всегда 0 для всех (мало кого выгоняют). Бесполезный, можно убрать.- Cumulative_metrics (suммы за 90 дней) сильно коррелируют между собой → можно использовать PCA.
corr = X.corr()
# Признаки с corr > 0.9 — кандидат на исключениеОтвет по гипотезе 1
Если кластер «мёртвых» составляет, скажем, 65% всех гильдий, гипотеза подтверждается: большинство гильдий неактивны.
Шаг 2. Связь с платящими
payers = pd.read_csv("payers.csv")
# Ассоциируем guild с creator (мы можем знать только создателя; для всех members нужен дополнительный join)
# Соединяем: guild_creator → payment data
df_pay = df.merge(payers, left_on="guild_created.creator_character_id",
right_on="character_id", how="left")
df_pay["is_payer"] = (df_pay["total_payment"].fillna(0) > 0).astype(int)
# Для каждого кластера
metrics = (df_pay.groupby("cluster")
.agg(guild_count=("guild_id", "count"),
payer_count=("is_payer", "sum"),
total_revenue=("total_payment", "sum"))
.assign(paying_share=lambda x: x["payer_count"] / x["guild_count"],
arppu=lambda x: x["total_revenue"] / x["payer_count"].replace(0, np.nan)))
print(metrics)Типовой результат:
| Кластер | Guild count | Paying share | ARPPU |
|---|---|---|---|
| Мёртвые | 6500 | 5% | $20 |
| Средние | 2500 | 20% | $50 |
| Активные | 1000 | 45% | $150 |
Если paying share в активном кластере выше, гипотеза 2 подтверждается. Но причинность неоднозначна — обсудим в выводе.
Стат-тест
from scipy import stats
# Chi-square для пропорций по 3 кластерам
contingency = df_pay.groupby(["cluster", "is_payer"]).size().unstack(fill_value=0)
chi2, p, _, _ = stats.chi2_contingency(contingency)
print(f"Chi-square: {chi2:.2f}, p: {p:.4f}")
# Pairwise z-tests с поправкой БонферрониШаг 3. Финальный вывод
Гипотеза 1 подтверждается: 65% гильдий относятся к «мёртвому» кластеру (низкая активность по всем признакам). Однако активные 35% гильдий концентрируют большую часть игровой активности и платящих игроков.
Гипотеза 2 подтверждается: paying share в активном кластере (45%) ~9x выше, чем в мёртвом (5%); ARPPU также значительно выше.
Гипотеза 3 — не очевидна: повышение стоимости создания гильдии может:
- Уменьшить число «мёртвых» — отсечёт неактивных создателей.
- − Уменьшить общее число активных — некоторые «случайные» гильдии в итоге становятся активными.
Перед изменением рекомендую A/B-тест с двумя ценами и измерением долгосрочной активности гильдий. Также: альтернатива — сделать барьер не из стоимости, а из квалификации (например, 8-й уровень → 15-й уровень, или N сыгранных рейдов).
Анализ / интерпретация
Главное в выводе:
- «Большинство неактивны» — статистически верно, но это по дизайну F2P (длинный хвост везде).
- Платящие концентрируются в активных — корреляция, но не доказательство, что покупки делают гильдию активной.
- Бизнес-решение требует A/B, не наблюдательного анализа.
Подводные камни
- Кластеры в k-means нестабильны между запусками. Используйте
random_stateи проверяйте на разных seed-ах. - Без стандартизации один признак может доминировать (если энергия в тысячах, а уровни — в единицах).
- Корреляция не причина. Активные гильдии = платящие — это самый частый misinterpretation. Активность могла привлечь платящих, или платящие привлекли активность, или оба коррелируют с третьим (опытные игроки).
- Survivorship bias: «гильдии, отслеженные 90 дней» — но кто-то распался за этот срок. Их активность будет недопредставлена.
- «Активность» — multimedia концепт. Она по миссиям может расти, по рейдам — нет. PCA проекция помогает увидеть многомерность.
creator≠ все участники. Гипотеза «платящие создают активные гильдии» — про создателей. Если же гипотеза «платящие участники активизируют» — нужна другая агрегация.- «Бесполезные признаки» — те, у которых variance ≈ 0 или corr с другими ≈ 1.
- Повышение стоимости — не в задаче A/B. Без эксперимента это спекуляция.
Альтернативы
- Hierarchical clustering (если k-means не сходится).
- DBSCAN (если есть шум и outliers).
- Decision tree classifier: target —
is_active_threshold(по медиане или экспертно), фичи — для интерпретации правил «активной гильдии».
Эталонный ответ
Шаг 1: feature engineering из guild_data.csv + energy.csv, стандартизация, k-means / GMM с k=3, проверка по elbow/silhouette. Результат: 3 кластера (мёртвые / средние / активные). Если мёртвые — большинство, гипотеза 1 подтверждается.
Шаг 2: paying_share и ARPPU per cluster, chi-square test для значимости различий. Активные имеют выше paying_share и ARPPU.
Шаг 3: гипотеза 3 не решается наблюдательным анализом — нужен A/B-тест с разными ценами создания. Альтернатива — заменить ценовой барьер на квалификационный.
Главное — отделить дескриптивную статистику (что в данных) от причинно-следственных утверждений (что произойдёт, если поменять).