Раздел 42 · Аналитика данных с нуля

Какой тест выбрать: z, хи-квадрат, Уэлч, Манн-Уитни, парный тест и бутстрэп

middle~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Ильва · геймдизайнер «Эпохи Корон»

У меня пять вопросов про патчи, и мне нужен ответ на каждый, а не одно «p меньше 0,05». Маги в 1.4 стали сильнее? Фракции в 1.4 вообще ровные? Матчи стали короче? Маги закрывают игру быстрее? И правда ли в 2v2 все расслабляются и кликают медленнее?

В прошлом уроке мы разобрали, как устроен любой тест: нулевая гипотеза, статистика, её распределение без эффекта и p-value как доля хвоста. Каркас у всех тестов один. Разные у них две вещи: какую статистику считают и что предполагают о данных. Ошибиться можно в обеих, поэтому тест выбирают по метрике и по тому, как собраны данные.

Пять вопросов Ильвы дают пять разных тестов. Работаем с базой empires, как в боссе про магов. Для винрейта берём там же определённые чистые строки: ранговые матчи без зеркальных.

Доля в двух группах: z-тест

Первый вопрос про долю: сколько игр из сыгранных маги выиграли. В чистых строках патча 1.3 у магов 386 побед из 815 игр, 47,4%. На 1.4 уже 697 из 1356, 51,4%. Плюс 4 пункта.

Это ровно ситуация из урока 54: две группы, у каждой доля, сравниваем через z-тест для двух долей.

import numpy as np
from scipy import stats

x_t, n_t = 697, 1356   # 1.4
x_c, n_c = 386, 815    # 1.3
p = (x_t + x_c) / (n_t + n_c)
se = np.sqrt(p * (1 - p) * (1 / n_t + 1 / n_c))
z = (x_t / n_t - x_c / n_c) / se
z, 2 * stats.norm.sf(abs(z))   # (1.823, 0.068)

p = 0,068. Без эффекта такая или большая разница получается случайно в 7 случаях из 100. Порог в 5% не пройден: что маги в рейтинговой игре стали сильнее, не доказано.

Доля в нескольких группах: хи-квадрат

Второй вопрос: ровные ли фракции на 1.4. Групп уже пять, и сравнивать их попарно z-тестом значит устроить 10 сравнений. Тогда хоть одно «значимое» выпадет случайно, о таких ловушках поговорим в уроке про подводные камни A/B. Нужен один тест на всю таблицу сразу.

Соберём таблицу: строка на фракцию, два столбца, победы и поражения. Запрос можно править: поменяй версию на '1.2' и сравни.

SQL-песочница

База · empires

Загружаю редактор…

База загрузится, когда ты выполнишь запрос или откроешь таблицы.

Выполни запрос, чтобы увидеть результат.

Выполни весь запрос кнопкой или Ctrl/Cmd+Enter. Ctrl+Space открывает подсказки. Можно писать несколько команд через точку с запятой, менять данные и открывать транзакции. У каждой песочницы своя база, изменения живут до ухода со страницы. «Сбросить базу» возвращает учебные данные, но не меняет SQL.

Если фракции равны, у каждой должно быть около 50% побед. В чистых строках так и задумано: в каждом незеркальном матче побед ровно столько же, сколько поражений. Значит, у Империи с её 963 играми ждём 481,5 победы, а видим 489. Такие ожидаемые числа называют ожидаемыми частотами.

Тест хи-квадрат складывает по всем клеткам (наблюдаемое - ожидаемое) ** 2 / ожидаемое. Чем дальше таблица от «все равны», тем больше сумма. В scipy его считает одна функция:

table = [
    [489, 474],   # Империя
    [697, 659],   # Орден Магов
    [475, 501],   # Северные Кланы
    [398, 443],   # Лесной Союз
    [416, 398],   # Нежить
]
result = stats.chi2_contingency(table)
result.statistic, result.pvalue, result.dof   # (4.80, 0.309, 4)
result.expected_freq[0]                        # array([481.5, 481.5])

dof это степени свободы: (5 - 1) * (2 - 1) = 4. p = 0,31: на 1.4 разница между фракциями похожа на шум. А на патче 1.2 та же таблица даёт хи-квадрат 15,4 и p = 0,004: там Северные Кланы выигрывали 54%, а Нежить 46%, и это уже не случайность. Тот же вывод, что в боссе про магов, только теперь одним числом на всю таблицу.

Две детали chi2_contingency. Первая: на вход идут счётчики, а не доли, и каждый игрок или матч должен лежать ровно в одной клетке. Вторая: для таблицы 2 × 2 функция по умолчанию включает поправку Йейтса и чуть завышает p-value. Без неё хи-квадрат для двух долей совпадает с z-тестом: chi2_contingency([[697, 659], [386, 429]], correction=False) даёт 3,32, это 1,823 в квадрате, и тот же p = 0,068. С поправкой p = 0,075.

урок из курса

Урок из курса «Аналитика данных с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.