Собесов

Aigrind — анализ активности гильдий и связь с платящими игроками

Кейсы и метрикиКластеризация и проверка гипотезСложнаяJunior

Условие

В F2P MMORPG игроки могут объединяться в гильдии: получают доступ к войнам, рейдам, миссиям. Создать гильдию — недорого. Гильдии могут повышать уровень через изучение талантов (требует гильдийной энергии).

Гипотезы геймдизайнера:

  1. Большинство создаваемых гильдий неактивны.
  2. Игроки, покупающие премиум-товары, создают более активные гильдии.
  3. Если первая гипотеза верна, стоит ли существенно повысить стоимость создания гильдии (вплоть до премиум-товара)?

Задача аналитика: подтвердить / опровергнуть гипотезы, презентовать результаты.

Декомпозиция:

Шаг 1. Определить критерии активности гильдий на основе guild_data.csv и energy.csv. Метод выбрать самостоятельно (экспертно по средним; экспертно по квартилям; кластеризация; свой вариант). Распределить гильдии по классам/кластерам, описать пороги, выделить бесполезные признаки. Дать ответ по гипотезе 1.

Шаг 2. На основе разметки и payers.csv рассчитать для каждой группы:

  • Paying share (доля платящих);
  • ARPPU.

Дать ответ по гипотезе 2.

Шаг 3. Финальный вывод по всем гипотезам.

Решение

Подход

  1. Многомерная активность: гильдии могут быть активны по-разному (миссии vs рейды vs таланты). Кластеризация — наиболее адекватный инструмент.
  2. «Большинство гильдий неактивны» = долгий хвост распределения (правый тяжёлый, медиана низкая).
  3. Связь активности и платящих — это корреляция, не причинно-следственная: возможно, платящие создают активные гильдии, или возможно, активные гильдии «затягивают» в платежи.

Шаг 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, не наблюдательного анализа.

Подводные камни

  1. Кластеры в k-means нестабильны между запусками. Используйте random_state и проверяйте на разных seed-ах.
  2. Без стандартизации один признак может доминировать (если энергия в тысячах, а уровни — в единицах).
  3. Корреляция не причина. Активные гильдии = платящие — это самый частый misinterpretation. Активность могла привлечь платящих, или платящие привлекли активность, или оба коррелируют с третьим (опытные игроки).
  4. Survivorship bias: «гильдии, отслеженные 90 дней» — но кто-то распался за этот срок. Их активность будет недопредставлена.
  5. «Активность» — multimedia концепт. Она по миссиям может расти, по рейдам — нет. PCA проекция помогает увидеть многомерность.
  6. creator ≠ все участники. Гипотеза «платящие создают активные гильдии» — про создателей. Если же гипотеза «платящие участники активизируют» — нужна другая агрегация.
  7. «Бесполезные признаки» — те, у которых variance ≈ 0 или corr с другими ≈ 1.
  8. Повышение стоимости — не в задаче 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-тест с разными ценами создания. Альтернатива — заменить ценовой барьер на квалификационный.

Главное — отделить дескриптивную статистику (что в данных) от причинно-следственных утверждений (что произойдёт, если поменять).

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти