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

Чему равно p-value для выбросов

1.8 Middle🔥 71 комментариев
#Статистика и A/B тестирование

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

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

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

Чему равно p-value для выбросов

Это хороший вопрос, который проверяет понимание связи между статистикой и определением выбросов. Ответ: нет универсального значения p-value для выбросов, оно зависит от конкретного статистического теста, который вы используете.

Что такое p-value?

P-value — это вероятность наблюдать такое же или более экстремальное значение при условии, что нулевая гипотеза верна:

p-value = P(получить такие данные | H0 верна)

Типичные пороги:

  • p-value < 0.05 → отклоняем H0 (это выброс/аномалия)
  • p-value >= 0.05 → не отклоняем H0 (это нормально)

Примеры вычисления p-value для выбросов

1. Z-тест (для нормального распределения)

import numpy as np
from scipy.stats import norm

data = np.array([1, 2, 3, 4, 5, 100])  # 100 — явный выброс

mean = np.mean(data[:-1])
std = np.std(data[:-1], ddof=1)

# Z-score для последней точки
z_score = (100 - mean) / std

# P-value (двусторонний тест)
p_value = 2 * (1 - norm.cdf(abs(z_score)))

print(f"Z-score: {z_score:.2f}")
print(f"P-value: {p_value:.4e}")  # Очень маленькое значение
# Результат: p-value << 0.05 → 100 — выброс

2. Тест Grubbs (для нормального распределения)

Тест Grubbs специально разработан для обнаружения одного выброса:

from scipy.stats import t

def grubbs_test(data, alpha=0.05):
    """Тест Grubbs для выбросов"""
    n = len(data)
    mean = np.mean(data)
    std = np.std(data, ddof=1)
    
    # Самая далёкая от среднего точка
    z_scores = np.abs((data - mean) / std)
    max_z_idx = np.argmax(z_scores)
    max_z = z_scores[max_z_idx]
    
    # Статистика Grubbs
    t_stat = (n - 1) / np.sqrt(n) * np.sqrt(max_z**2 / (n - 2 + max_z**2))
    
    # P-value из t-распределения
    p_value = 2 * (1 - t.cdf(t_stat, n - 2))
    
    return t_stat, p_value, max_z_idx

data = np.array([1, 2, 3, 4, 5, 100])
t_stat, p_val, idx = grubbs_test(data)
print(f"Grubbs test: p-value = {p_val:.4e}")
print(f"Выброс на индексе {idx}: {data[idx]}")

3. Modified Z-Score (более робастный)

Использует медиану и MAD (Median Absolute Deviation) вместо среднего и стд:

def modified_z_score(data, threshold=3.5):
    """Modified Z-score — более робастен к выбросам"""
    median = np.median(data)
    mad = np.median(np.abs(data - median))
    
    if mad == 0:
        modified_z = np.zeros_like(data, dtype=float)
    else:
        modified_z = 0.6745 * (data - median) / mad
    
    outlier_indices = np.where(np.abs(modified_z) > threshold)[0]
    p_values = 2 * (1 - norm.cdf(np.abs(modified_z[outlier_indices])))
    
    return outlier_indices, p_values

data = np.array([1, 2, 3, 4, 5, 100])
idx, p_vals = modified_z_score(data)
print(f"Выбросы с p-values: {list(zip(data[idx], p_vals))}")

4. Тест Rosner (для нескольких выбросов)

from scipy.stats import t

def rosner_test(data, k=2, alpha=0.05):
    """Rosner test для обнаружения до k выбросов"""
    outliers = []
    data_copy = data.copy().astype(float)
    n = len(data_copy)
    
    for i in range(k):
        mean = np.mean(data_copy)
        std = np.std(data_copy, ddof=1)
        
        z_scores = np.abs((data_copy - mean) / std)
        max_idx = np.argmax(z_scores)
        max_z = z_scores[max_idx]
        
        # Rosner статистика
        R = (n - i) / np.sqrt(n - i - 1) * np.sqrt(max_z**2 / (n - i - 2 + max_z**2))
        
        # Критическое значение
        t_crit = t.ppf(1 - alpha / (2 * (n - i)), n - i - 2)
        
        if R > t_crit:
            outliers.append((data[max_idx], R))
            data_copy = np.delete(data_copy, max_idx)
    
    return outliers

data = np.array([1, 2, 3, 4, 5, 100, 105])
outliers = rosner_test(data, k=2)
print(f"Найденные выбросы: {outliers}")

Практическое применение

from scipy.stats import zscore

def detect_outliers_by_pvalue(data, p_threshold=0.05):
    """Обнаружить выбросы, используя Z-тест"""
    mean = np.mean(data)
    std = np.std(data, ddof=1)
    
    z_scores = (data - mean) / std
    p_values = 2 * (1 - norm.cdf(np.abs(z_scores)))
    
    outlier_mask = p_values < p_threshold
    
    return outlier_mask, p_values

data = np.array([1, 2, 3, 4, 5, 100, 2.5, 3.5])
outliers, p_values = detect_outliers_by_pvalue(data, p_threshold=0.05)

print("Обнаружение выбросов (p-value < 0.05):")
for val, p_val, is_outlier in zip(data, p_values, outliers):
    status = "ВЫБРОС" if is_outlier else "нормально"
    print(f"  {val:7.1f}: p-value = {p_val:.4e} [{status}]")

Связь между Z-score и p-value

Z-score | p-value (2-sided)
--------|------------------
1.96    | 0.0500 (5%)
2.58    | 0.0100 (1%)
3.00    | 0.0027 (0.3%)
3.29    | 0.0010 (0.1%)

На практике часто используют:

  • Z-score > 3 (соответствует p-value ≈ 0.003)
  • Z-score > 2.5 (соответствует p-value ≈ 0.012)
  • p-value < 0.05 (95% уверенность)
  • p-value < 0.01 (99% уверенность)

Выбор метода обнаружения выбросов

МетодP-value базисКогда использоватьПреимущества
Z-scoreНормальноеНормальные данныеПростой, быстрый
Grubbst-тестОдин выбросСпециализированный
Rosnert-тестНесколько выбросовЛучше для множественных
Modified ZНорм. + MADАсимметричные данныеРобастен к выбросам
IQRНе требуетБыстро без расчётовПростой, часто используется

Ключевые выводы

  1. Нет универсального p-value для выбросов — зависит от теста

  2. Общие пороги:

    • p-value < 0.05 → вероятно выброс (95% уверенность)
    • p-value < 0.01 → скорее выброс (99% уверенность)
  3. P-value показывает: Вероятность наблюдать такое значение при условии, что это нормальное число (не выброс)

  4. На практике часто используют:

    • Z-score > 3 (простой и распространённый подход)
    • IQR метод (более робастен)
    • Грубс-тест (если предполагаете нормальность)
  5. Не путайте:

    • P-value — это вероятность при H0, а не вероятность быть выбросом
    • Разные методы могут давать разные p-values для одних и тех же данных
Чему равно p-value для выбросов | PrepBro