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

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

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 методы, которые более интуитивны.

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