Всё что нужно знать о распределениях, корреляции и доверительных интервалах — с кодом и примерами из практики.
Три меры центра распределения. Почему медиана лучше среднего при выбросах, и как объяснить это интервьюеру.
Среднее (mean): сумма / количество
• Хорошо: нормальное распределение, нет выбросов
• Плохо: один выброс сильно тянет вверх
• Пример: средняя зарплата — плохая метрика (миллиардеры)
Медиана (median): значение посередине (50-й перцентиль)
• Хорошо: данные с хвостами, скошенное распределение
• Устойчива к выбросам
• Пример: медианная зарплата, медианный чек
Мода (mode): наиболее часто встречающееся значение
• Хорошо: категориальные данные, дискретные переменные
• Пример: самый популярный размер одежды, самый частый город
Правило: если mean >> median → правый хвост → используй median
если mean ≈ median → симметричное → можно mean
SQL:
AVG(amount) AS mean_check
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) AS median_check
Python:
df['amount'].mean()
df['amount'].median()
df['amount'].mode()[0]Меры разброса данных. Что значит «σ = 20» и почему это важно при сравнении групп в A/B тесте.
Дисперсия (Variance):
Среднеквадратичное отклонение от среднего.
Var = Σ(xi - mean)² / n
Стандартное отклонение (Std, σ):
σ = √Var
Имеет ту же размерность что и данные.
Пример: mean=100 ₸, σ=20 ₸ → большинство значений 80–120 ₸
Правило 68-95-99.7 (для нормального распределения):
±1σ → 68% данных
±2σ → 95% данных
±3σ → 99.7% данных
IQR (Interquartile Range):
IQR = Q3 - Q1 (разница 75-го и 25-го перцентиля)
Устойчива к выбросам!
Выброс: x < Q1 - 1.5*IQR или x > Q3 + 1.5*IQR
Python:
df['amount'].std() # σ
df['amount'].var() # дисперсия
df['amount'].quantile([0.25, 0.75])
from scipy.stats import iqr
iqr_val = iqr(df['amount'])Что такое r = 0.7, когда использовать Spearman вместо Pearson, и почему корреляция ≠ причинность.
Pearson (r):
• Линейная связь между двумя числовыми переменными
• r = 1: идеальная прямая, r = 0: нет связи, r = -1: обратная
• Требует нормальности и отсутствия выбросов
Spearman (ρ):
• Монотонная связь (не обязательно линейная)
• Устойчив к выбросам (работает с рангами)
• Используй когда данные ненормальные или есть выбросы
Python:
df[['sessions', 'revenue']].corr() # Pearson
df[['sessions', 'revenue']].corr(method='spearman') # Spearman
from scipy.stats import pearsonr, spearmanr
r, p = pearsonr(df['sessions'], df['revenue'])
print(f'r={r:.3f}, p={p:.4f}')
Ошибки интерпретации:
✗ «r = 0.8 → sessions вызывают revenue»
✓ Есть СВЯЗЬ, но причинность не доказана
Примеры ложной корреляции:
Мороженое и утопания (оба зависят от жаркой погоды)
Количество пожарных и ущерб (больше пожарных = больший пожар)
Правило на собесе: «Correlation does not imply causation»Три ключевых распределения в аналитике. Как понять что данные нормально распределены и что с этим делать.
Нормальное (Gaussian):
Bell curve. Симметрично вокруг среднего.
Применяется: рост, IQ, ошибки измерений.
Проверка нормальности:
from scipy.stats import shapiro, normaltest
stat, p = shapiro(df['amount']) # p > 0.05 → нормальное
# Визуально: QQ-plot
import scipy.stats as stats
stats.probplot(df['amount'], dist='norm', plot=plt)
Биномиальное:
Количество «успехов» в n независимых экспериментах.
Применяется: конверсия (купил/не купил), CTR.
P(k успехов из n) = C(n,k) * p^k * (1-p)^(n-k)
from scipy.stats import binom
binom.pmf(k=5, n=100, p=0.05) # P(ровно 5 покупок из 100 при conv=5%)
Пуассон:
Количество событий за фиксированное время при известной средней частоте.
Применяется: число заказов в час, число обращений в поддержку.
P(k) = (λ^k * e^-λ) / k! где λ = среднее число событий
from scipy.stats import poisson
poisson.pmf(k=3, mu=5) # P(ровно 3 заказа при среднем 5/час)95% CI — что это означает, как считать и как объяснять результаты теста без p-value жаргона.
95% доверительный интервал:
«Если повторить эксперимент 100 раз, в 95 случаях
истинное значение попадёт в этот диапазон»
НЕ означает: «с 95% вероятностью истинное значение в интервале»
Формула для среднего:
CI = mean ± z * (σ / √n)
z = 1.96 для 95% CI
z = 2.58 для 99% CI
Python:
import numpy as np
from scipy import stats
data = df['amount'].values
n = len(data)
mean = np.mean(data)
se = stats.sem(data) # standard error = σ/√n
ci = stats.t.interval(0.95, df=n-1, loc=mean, scale=se)
print(f'95% CI: ({ci[0]:.1f}, {ci[1]:.1f})')
Применение в A/B:
Если CI для разницы (variant - control) НЕ включает 0
→ изменение статистически значимо
Если CI = [+2₸, +50₸] → эффект положительный
Если CI = [-10₸, +15₸] → неопределённо (включает 0)
Bootstrap CI (без предположений о распределении):
from sklearn.utils import resample
boot_means = [np.mean(resample(data)) for _ in range(10000)]
ci = np.percentile(boot_means, [2.5, 97.5])Skewness и kurtosis — быстро понять форму данных до построения графика.
Skewness (асимметрия):
= 0: симметричное распределение
> 0: правый хвост (большие выбросы справа)
Типично для: выручки, времени ожидания, LTV
< 0: левый хвост (большие выбросы слева)
Kurtosis (эксцесс):
= 3 (или 0 в «excess kurtosis»): нормальное
> 3: остроконечное (много выбросов)
< 3: плоское (мало выбросов)
Python:
from scipy.stats import skew, kurtosis
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Статистика для аналитика',
description: 'Среднее, медиана, корреляция, доверительные интервалы — с кодом и примерами.',
openGraph: { title: 'Статистика для аналитика', description: 'Среднее, медиана, корреляция, доверительные интервалы — с кодом и примерами.' },
};
print(f'Skewness: {skew(df["amount"]):.2f}')
print(f'Kurtosis: {kurtosis(df["amount"]):.2f}')
Интерпретация для аналитика:
Skewness > 1 → данные сильно скошены
→ median > mean или mean > median
→ лучше использовать median, Mann-Whitney, log-transform
Log-преобразование для нормализации правого хвоста:
df['log_amount'] = np.log1p(df['amount']) # log(x+1)