← Назад к вопросам

Какие знаешь условия использования Т-теста?

2.0 Middle🔥 231 комментариев

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

T-тест: условия применения и использование в аналитике

T-тест — это статистический тест для сравнения средних значений между двумя группами. Это один из самых важных инструментов в A/B тестировании. Рассмотрю условия применения и частые ошибки.

1. Когда использовать T-тест

T-тест используется для:

  • Сравнение средних значений между двумя группами (тест vs контроль)
  • Проверка гипотезы о том, что две группы имеют разные средние
  • Определение статистической значимости различий

Пример: изменили цену плана с $99 на $89. Различается ли средний LTV пользователей в обеих группах?

2. Условия применения T-теста (Critical!)

Условие 1: Нормальность распределения (Normality)

Суть: данные в каждой группе должны быть близки к нормальному распределению (bell curve).

Проверка:

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

# Данные: средний время использования в контрольной группе (минуты)
control_data = np.array([2, 3, 5, 4, 6, 5, 7, 4, 3, 8, 5, 4, 6, 7, 5])

# Тест Шапиро-Уилка (Shapiro-Wilk test)
statistic, p_value = stats.shapiro(control_data)

if p_value > 0.05:
    print(f"Data is normally distributed (p={p_value:.3f})")
else:
    print(f"Data is NOT normally distributed (p={p_value:.3f})")

# Визуализация
plt.hist(control_data, bins=10, edgecolor='black')
plt.axvline(np.mean(control_data), color='red', linestyle='--', label='Mean')
plt.legend()
plt.show()

Что делать, если не нормально?

  • Применить логарифмическое преобразование (log transform)
  • Использовать Mann-Whitney U тест (non-parametric альтернатива)
  • Увеличить размер выборки (при n > 30 центральная теорема лимита спасает нас)

Условие 2: Независимость наблюдений (Independence)

Суть: каждое наблюдение должно быть независимым от остальных.

Примеры нарушения:

  • Один пользователь в обеих тест и контроль группах (они зависимы!)
  • Один IP адрес в нескольких пользователях (bot трафик)
  • Временная корреляция (если вчера было много регистраций, сегодня тоже будет)

Проверка:

# Правильное распределение: каждый пользователь только в одной группе
test_group = df[df['group'] == 'test']['user_id'].unique()
control_group = df[df['group'] == 'control']['user_id'].unique()

intersection = set(test_group) & set(control_group)
if len(intersection) == 0:
    print("Groups are independent ✓")
else:
    print(f"WARNING: {len(intersection)} users in both groups!")

Условие 3: Однородность дисперсии (Homogeneity of Variance)

Суть: дисперсия (вариативность) в обеих группах должна быть примерно одинаковой.

Пример нарушения:

  • Контрольная группа: LTV = 50 ± 5 (мало вариативности)
  • Тест группа: LTV = 55 ± 50 (много вариативности) → Различные дисперсии, t-тест неподходящ

Проверка (Левина тест):

from scipy.stats import levene

control_ltv = [45, 48, 52, 50, 49, 51, 48, 52]
test_ltv = [52, 70, 35, 55, 80, 50, 40, 60]  # Большая вариативность

statistic, p_value = levene(control_ltv, test_ltv)

if p_value > 0.05:
    print(f"Variances are homogeneous (p={p_value:.3f})")
    test_type = "T-test (standard)"
else:
    print(f"Variances are NOT homogeneous (p={p_value:.3f})")
    test_type = "T-test (Welch, не предполагает равные дисперсии)"

print(f"Use: {test_type}")

Что делать, если дисперсии не одинаковые?

  • Использовать Welch's t-test (не предполагает равные дисперсии)
  • Это вариант обычного t-теста, который более robust

Условие 4: Размер выборки (Sample Size)

Минимальный размер:

  • Если данные нормальны: >= 20 наблюдений на группу
  • Если данные не совсем нормальны: >= 30 наблюдений на группу
  • Золотой стандарт: >= 50 на группу

Пример ошибки:

# НЕПРАВИЛЬНО: слишком мало данных
control = [100, 120, 110]  # 3 наблюдения
test = [115, 125, 130]      # 3 наблюдения
# t-тест неподходящ! результат может быть случайностью

# ПРАВИЛЬНО: достаточно данных
control = [100, 102, 98, 105, 103, ..., 101] * 30  # 30+ наблюдений
test = [115, 118, 112, 120, 116, ..., 114] * 30     # 30+ наблюдений

3. Виды T-тестов

Вид 1: Independent Samples T-test (Независимые выборки)

Когда: сравнение двух РАЗНЫХ групп пользователей

Пример: контроль (стара цена) vs тест (новая цена)

from scipy.stats import ttest_ind

control_revenue = [100, 110, 95, 105, 98, 102, 108, 99]
test_revenue = [120, 125, 115, 128, 122, 130, 118, 125]

# Обычный t-тест
t_stat, p_value = ttest_ind(control_revenue, test_revenue)
print(f"t-statistic: {t_stat:.3f}")
print(f"p-value: {p_value:.4f}")

if p_value < 0.05:
    print("Разница статистически значима (p < 0.05) ✓")
else:
    print("Разница НЕ значима, может быть случайностью (p >= 0.05)")

# Welch's t-test (если дисперсии разные)
t_stat_welch, p_value_welch = ttest_ind(control_revenue, test_revenue, equal_var=False)

Вид 2: Paired Samples T-test (Зависимые выборки)

Когда: сравнение ОДНИХ И ТЕХ ЖЕ пользователей ДО и ПОСЛЕ

Пример: LTV пользователя до обновления UI vs после обновления

from scipy.stats import ttest_rel

# Один пользователь, до и после
user_ltv_before = [100, 150, 80, 120, 110, 95, 130]
user_ltv_after = [120, 170, 100, 140, 135, 115, 150]

# Paired t-test
t_stat, p_value = ttest_rel(user_ltv_before, user_ltv_after)
print(f"p-value: {p_value:.4f}")

if p_value < 0.05:
    print("LTV увеличился после обновления (статистически значимо)")

Вид 3: One-sample T-test (Одна выборка)

Когда: сравнение группы с известным значением

Пример: средний LTV пользователя = 100. Отличается ли новая когорта пользователей?

from scipy.stats import ttest_1samp

new_cohort_ltv = [105, 115, 95, 110, 108, 120, 100, 112]
known_mean = 100

t_stat, p_value = ttest_1samp(new_cohort_ltv, known_mean)

if p_value < 0.05:
    print(f"Новая когорта отличается от ожидаемого LTV={known_mean}")

4. Интерпретация результатов

p-value < 0.05: разница статистически значима p-value >= 0.05: разница может быть случайностью

# Пример результата
t_stat = 2.35
p_value = 0.032
df = 28  # degrees of freedom

print(f"""\nРезультаты t-теста:
t-статистика: {t_stat:.3f}
P-value: {p_value:.4f}
Степени свободы: {df}

Вывод: p-value = {p_value:.4f} < 0.05
→ Есть статистически значимая разница между группами
→ Вероятность, что это случайность: {p_value*100:.2f}%
""")

5. Частые ошибки

Ошибка 1: p-hacking (многократное тестирование)

# НЕПРАВИЛЬНО: смотреть p-value каждый день
for day in range(1, 30):
    subset = data[data['day'] <= day]
    t_stat, p_value = ttest_ind(subset['control'], subset['test'])
    if p_value < 0.05:
        print(f"Значимо на день {day}!")
        break  # ← BIAS! Вероятность false positive растёт

# ПРАВИЛЬНО: запланировать тест заранее
# Рассчитать нужный sample size
# Дождаться когда достигли нужный n
# Потом один раз провести тест

Ошибка 2: использование t-теста на пропорции

# НЕПРАВИЛЬНО: t-тест на конверсию (0 или 1)
test_conversions = [0, 1, 1, 0, 1, 1, 0, ...]
control_conversions = [0, 0, 1, 0, 0, 1, ...]
t_stat, p_value = ttest_ind(test_conversions, control_conversions)  # ← ОШИБКА!

# ПРАВИЛЬНО: использовать Chi-square тест или Z-тест
from scipy.stats import chi2_contingency
contingency_table = [
    [test_conversions.count(1), test_conversions.count(0)],
    [control_conversions.count(1), control_conversions.count(0)]
]
chi2, p_value, dof, expected = chi2_contingency(contingency_table)

Ошибка 3: не проверить предположения

# ПРАВИЛЬНЫЙ ПРОЦЕСС:

# 1. Проверить нормальность
stat, p = shapiro(data)
if p < 0.05:
    print("Данные не нормальны, может нужен Mann-Whitney U тест")

# 2. Проверить однородность дисперсии
stat, p = levene(group1, group2)
if p < 0.05:
    # Использовать Welch's t-test
    t_stat, p_value = ttest_ind(group1, group2, equal_var=False)
else:
    # Обычный t-тест
    t_stat, p_value = ttest_ind(group1, group2)

# 3. Интерпретировать результат

6. Практический пример: A/B тест цены

import numpy as np
from scipy import stats

# Данные: средний LTV при разных ценах
control_price = 99
control_ltv = np.array([95, 105, 100, 98, 102, 101, 99, 103, 97, 100] * 10)

test_price = 89
test_ltv = np.array([108, 112, 110, 105, 115, 109, 107, 111, 106, 113] * 10)

print(f"Контрольная группа (цена ${control_price}):")
print(f"  Среднее LTV: ${control_ltv.mean():.2f}")
print(f"  Стд. отклонение: ${control_ltv.std():.2f}")
print(f"  n = {len(control_ltv)}")

print(f"\nТестовая группа (цена ${test_price}):")
print(f"  Среднее LTV: ${test_ltv.mean():.2f}")
print(f"  Стд. отклонение: ${test_ltv.std():.2f}")
print(f"  n = {len(test_ltv)}")

print(f"\nРазница в средних: ${(test_ltv.mean() - control_ltv.mean()):.2f}")

# Проверить предположения
print("\n=== Проверка предположений ===")

# Нормальность
stat_ctrl, p_ctrl = stats.shapiro(control_ltv)
stat_test, p_test = stats.shapiro(test_ltv)
print(f"\nНормальность (Shapiro-Wilk):")
print(f"  Контроль: p={p_ctrl:.4f} {'✓' if p_ctrl > 0.05 else '✗'}")
print(f"  Тест: p={p_test:.4f} {'✓' if p_test > 0.05 else '✗'}")

# Однородность дисперсии
stat_levene, p_levene = stats.levene(control_ltv, test_ltv)
print(f"\nОднородность дисперсии (Levene test):")
print(f"  p={p_levene:.4f} {'✓' (equal variance)' if p_levene > 0.05 else '✗ (unequal)'} ")

# T-тест
t_stat, p_value = stats.ttest_ind(control_ltv, test_ltv, equal_var=(p_levene > 0.05))

print(f"\n=== T-тест результаты ===")
print(f"t-статистика: {t_stat:.4f}")
print(f"p-value: {p_value:.6f}")
print(f"\nВыводы:")

if p_value < 0.05:
    direction = "выше" if test_ltv.mean() > control_ltv.mean() else "ниже"
    lift = abs((test_ltv.mean() - control_ltv.mean()) / control_ltv.mean() * 100)
    print(f"✓ LTV при цене ${test_price} статистически значимо {direction}")
    print(f"  Lift: +{lift:.1f}%")
    print(f"  Вероятность ошибки (false positive): {p_value*100:.2f}%")
else:
    print(f"✗ Разница не статистически значима")
    print(f"  Нельзя сделать вывод на основе текущих данных")
    print(f"  Может потребоваться больше пользователей или больше времени")

Заключение

T-тест — мощный инструмент для Product Analyst'а, но его нужно использовать правильно. Ключные моменты:

  1. Проверить предположения перед использованием
  2. Выбрать правильный тип (independent, paired, one-sample)
  3. Быть осторожным с p-hacking (не смотреть результаты каждый день)
  4. Правильно интерпретировать p-value (это не вероятность, что гипотеза верна)
  5. Планировать тест заранее (sample size, significance level)

Когда есть сомнения, лучше проконсультироваться со статистиком или использовать Bayesian методы, которые более интуитивны.