← Назад к вопросам
Является ли подстановка средних значений вместо пропусков допустимой? Почему?
1.3 Junior🔥 181 комментариев
#Pandas и обработка данных#Машинное обучение
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Подстановка средних значений: допустимо ли?
Краткий ответ: Это зависит от контекста. Подстановка средних (mean imputation) — это простой, но рискованный метод, который в большинстве случаев вводит смещение в анализ. Он может быть допустимым только при очень специфических условиях.
Проблемы с подстановкой средних значений
1. Уменьшение дисперсии
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Оригинальные данные
np.random.seed(42)
original_data = np.random.normal(100, 15, 1000) # среднее 100, стдев 15
# Создаём пропуски (MCAR)
mask = np.random.random(1000) < 0.2 # 20% пропусков
data_with_missing = original_data.copy()
data_with_missing[mask] = np.nan
# Заполняем средним
mean_value = np.nanmean(data_with_missing)
imputed_data = data_with_missing.copy()
imputed_data[np.isnan(imputed_data)] = mean_value
print(f"Оригинальные данные:")
print(f" Среднее: {np.mean(original_data):.2f}")
print(f" Стдев: {np.std(original_data):.2f}")
print(f" Дисперсия: {np.var(original_data):.2f}")
print(f"\nДанные после заполнения средним:")
print(f" Среднее: {np.mean(imputed_data):.2f}")
print(f" Стдев: {np.std(imputed_data):.2f}")
print(f" Дисперсия: {np.var(imputed_data):.2f}")
print(f"\nПотеря дисперсии: {(1 - np.var(imputed_data)/np.var(original_data)) * 100:.1f}%")
# Визуализация
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
axes[0].hist(original_data, bins=50, alpha=0.7, edgecolor='black')
axes[0].set_title('Оригинальные данные')
axes[0].axvline(np.mean(original_data), color='red', linestyle='--', label='Среднее')
data_incomplete = data_with_missing[~np.isnan(data_with_missing)]
axes[1].hist(data_incomplete, bins=50, alpha=0.7, edgecolor='black')
axes[1].set_title('Данные с пропусками (видимые значения)')
axes[1].axvline(np.nanmean(data_with_missing), color='red', linestyle='--')
axes[2].hist(imputed_data, bins=50, alpha=0.7, edgecolor='black')
axes[2].set_title('После подстановки средних')
axes[2].axvline(np.mean(imputed_data), color='red', linestyle='--')
plt.tight_layout()
plt.show()
Результат: Дисперсия уменьшается на 10-30% (зависит от доли пропусков).
2. Смещение стандартных ошибок
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
# Данные для регрессии
np.random.seed(42)
n = 500
X = np.random.normal(0, 1, n)
y_true = 2 * X + np.random.normal(0, 0.5, n)
# Вводим пропуски в y (30%)
missing_indices = np.random.choice(n, int(0.3 * n), replace=False)
y_with_missing = y_true.copy()
y_with_missing[missing_indices] = np.nan
# Вариант 1: Удаляем пропуски
valid_mask = ~np.isnan(y_with_missing)
X_clean = X[valid_mask].reshape(-1, 1)
y_clean = y_with_missing[valid_mask]
model_clean = LinearRegression().fit(X_clean, y_clean)
print(f"После удаления пропусков:")
print(f" Коэффициент: {model_clean.coef_[0]:.4f}")
print(f" R-squared: {model_clean.score(X_clean, y_clean):.4f}")
# Вариант 2: Подстановка средних
mean_y = np.nanmean(y_with_missing)
y_imputed = y_with_missing.copy()
y_imputed[missing_indices] = mean_y
model_imputed = LinearRegression().fit(X.reshape(-1, 1), y_imputed)
print(f"\nПосле подстановки средних:")
print(f" Коэффициент: {model_imputed.coef_[0]:.4f}")
print(f" R-squared: {model_imputed.score(X.reshape(-1, 1), y_imputed):.4f}")
print(f"\nПроблема: R-squared выглядит выше, но это иллюзия")
print(f"(заполняем среднее значение y, что увеличивает объяснённую дисперсию)")
3. Исключение из анализа важной информации
# Если данные MNAR (пропуски зависят от самой переменной)
# Например: люди не хотят указывать низкий доход
np.random.seed(42)
n = 1000
# Истинный доход
income = np.random.gamma(2, 2, n) * 30000 # распределение с длинным хвостом
# Пропуски MNAR: люди с низким доходом реже отвечают
missing_prob = 1 / (1 + np.exp(income/50000 - 1.5)) # логистическая функция
missing_mask = np.random.random(n) < missing_prob
income_observed = income.copy()
income_observed[missing_mask] = np.nan
print(f"Истинный средний доход: ${np.mean(income):.0f}")
print(f"Наблюдаемый средний доход (видимые данные): ${np.nanmean(income_observed):.0f}")
print(f"Средний доход после подстановки средних: ${np.nanmean(income_observed):.0f}")
print(f"\nОшибка смещения: ${(np.nanmean(income_observed) - np.mean(income)):.0f}")
print(f"\nДаже подстановка среднего видимых данных неправильна!")
print(f"Истинное среднее пропущенных значений (которые мы не видим):")
print(f" ${np.mean(income[missing_mask]):.0f}")
print(f"Это намного ниже, чем видимые значения!")
Когда подстановка средних МОЖЕТ быть допустимой
1. Очень маленькая доля пропусков (< 1-2%)
print("При < 1-2% пропусков:")
print("- Влияние на дисперсию минимально")
print("- Смещение коэффициентов регрессии маленькое")
print("- Если данные MCAR, смещение приблизительно нулевое")
print("\nНО: всё ещё лучше использовать более продвинутые методы")
2. Описательная статистика для быстрого анализа
print("Для разведочного анализа (EDA):")
print("- Можно подставить средние для быстрого просмотра")
print("- Помни про ограничения метода")
print("- Для финального анализа используй лучшие методы")
3. Данные MCAR с очень маленькой корреляцией пропусков с другими переменными
print("Редкий случай:")
print("- Доказанная MCAR механизм")
print("- Пропуски абсолютно случайны")
print("- Не коррелируют с целевой переменной")
print("\nВ таком случае подстановка среднего даёт несмещённые оценки")
print("(но всё ещё имеет проблемы с дисперсией и стандартными ошибками)")
Почему это плохо
Проблема 1: Неправильные стандартные ошибки
# Модель недооценивает неопределённость
from scipy.stats import t
print("После подстановки средних:")
print("- Дисперсия ошибок уменьшается")
print("- Стандартные ошибки коэффициентов становятся меньше")
print("- p-values становятся меньше (false confidence)")
print("- Доверительные интервалы слишком узкие")
print("\nРезультат: статистически значимые результаты,")
print("которые на самом деле шум из-за подстановки!")
Проблема 2: Нарушение распределения
from scipy.stats import normaltest
# Если подставляешь среднее в 30% данных
# Распределение становится мультимодальным
data_imputed_many = imputed_data.copy()
print(f"\nОригинальное распределение:")
print(f" Нормальное? Тест: статистика = {normaltest(original_data)[0]:.2f}")
print(f"\nДанные с импутацией средних (30% пропусков):")
print(f" Нормальное? Тест: статистика = {normaltest(imputed_data)[0]:.2f}")
print(f" Видно два пика: нормальное распределение + пик на среднем значении")
Лучшие альтернативы
1. Удаление (Deletion)
df_clean = df.dropna()
print("Плюсы: просто, честно")
print("Минусы: теряем данные")
print("Когда: пропусков < 5%, данные MCAR")
2. KNN Imputation
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=5)
df_imputed = pd.DataFrame(
imputer.fit_transform(df),
columns=df.columns
)
print("Плюсы: учитывает соседей, лучше сохраняет дисперсию")
print("Минусы: медленнее, требует расчёта расстояний")
3. Итеративное заполнение
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
imputer = IterativeImputer()
df_imputed = pd.DataFrame(
imputer.fit_transform(df),
columns=df.columns
)
print("Плюсы: учитывает зависимости между переменными")
print("Минусы: может быть slow, есть гиперпараметры")
4. Множественная импутация
print("Создаём несколько версий данных с разными импутациями")
print("Обучаем модель на каждой версии")
print("Усредняем результаты")
print("\nПлюсы: учитывает неопределённость импутации")
print("Минусы: вычислительные затраты")
Практический вывод
print("НИКОГДА не подставляй средние, если:")
print("✗ Пропусков > 5%")
print("✗ Нужны правильные стандартные ошибки")
print("✗ Используешь статистические тесты")
print("✗ Подозреваешь MNAR механизм")
print("✗ Переменная коррелирует с целевой")
print()
print("Только подставляй средние, если:")
print("✓ Пропусков < 1% И")
print("✓ Разведочный анализ (не финальный результат) И")
print("✓ Даёшь явное предупреждение о методе")
Вывод: Подстановка средних — это быстрый способ справиться с пропусками, но для серьёзного анализа используй KNN, итеративную импутацию или множественную импутацию. Честные результаты важнее скорости кодирования.