Pandas & NumPy Academy · Урок

Интерполяция и продвинутое заполнение

Используйте interpolate() для плавного заполнения на основе времени и определяйте, когда уместнее заполнение средним, а когда медианой.

Урок 4 из 413 шагов

«Интерполяция и продвинутое заполнение» — бесплатный урок Pandas & NumPy Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Pandas & NumPy Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Pandas & NumPy Academy содержит 4 уроков всего.

Когда интерполяция лучше fillna()

Заполнение вперёд и назад переносит ближайшее известное значение, не учитывая тренд данных. Интерполяция оценивает пропущенные значения, предполагая плавный переход между известными значениями — например, если в понедельник температура составляла 20 °C, а в пятницу — 30 °C, интерполяция оценит температуру в среду как 25 °C. Это даёт более реалистичные значения для плавно изменяющихся сигналов, таких как данные датчиков, цены или численность населения.

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'day': [1, 2, 3, 4, 5],
    'temp': [20.0, np.nan, np.nan, np.nan, 30.0]
})

# Linear interpolation fills gaps smoothly
df['temp_interp'] = df['temp'].interpolate(method='linear')
print(df)
#    day  temp  temp_interp
# 0    1  20.0         20.0
# 1    2   NaN         22.5
# 2    3   NaN         25.0
# 3    4   NaN         27.5
# 4    5  30.0         30.0

Методы interpolate()

Pandas поддерживает несколько методов interpolate(). Наиболее распространены 'linear' (равные интервалы между известными значениями), 'time' (учитывает неравные промежутки времени, если задан DatetimeIndex), 'polynomial' (подбирает полиномиальную кривую и требует аргумент order) и 'spline' (гладкий кусочно-полиномиальный метод). Линейный метод — самый безопасный вариант по умолчанию; методы более высокого порядка могут переобучиться на небольших промежутках.

import pandas as pd
import numpy as np

s = pd.Series([0, np.nan, np.nan, 8.0])

print('linear:', s.interpolate('linear').tolist())
# [0.0, 2.6666..., 5.3333..., 8.0]

print('polynomial(2):', s.interpolate('polynomial', order=2).tolist())
# [0.0, 2.222..., 5.111..., 8.0]

Интерполяция time с DatetimeIndex

Если у Вашего Series есть DatetimeIndex с неравными промежутками времени (например, присутствуют только рабочие дни, а выходные пропущены), method='time' выполняет интерполяцию пропорционально фактически прошедшему времени, а не только по позиции. Это точнее линейной интерполяции, которая считает строки равноотстоящими независимо от разрыва в календаре.

import pandas as pd
import numpy as np

# Unequal gaps: Jan 1, Jan 3, Jan 10
idx = pd.to_datetime(['2024-01-01', '2024-01-03', '2024-01-10'])
s = pd.Series([100.0, np.nan, 170.0], index=idx)

# linear treats gaps as equal (position-based)
print(s.interpolate('linear').tolist())  # [100.0, 135.0, 170.0]

# time accounts for actual day count
# Jan 1 to Jan 3 = 2 days, Jan 1 to Jan 10 = 9 days
# fraction: 2/9 of the way from 100 to 170
print(s.interpolate('time').tolist())   # [100.0, 115.55..., 170.0]

Ограничение диапазона интерполяции

Как и ffill(), interpolate() принимает параметр limit, ограничивающий количество подряд идущих позиций NaN для заполнения. Оставшиеся за пределом ограничения значения NaN сохраняются как пропуски. Это не позволяет интерполяции распространяться через очень длинные разрывы, где истинное значение может быть любым; такие разрывы следует помечать для ручной проверки.

import pandas as pd
import numpy as np

s = pd.Series([1.0, np.nan, np.nan, np.nan, np.nan, 10.0])

# Only fill the first 2 NaN positions
filled = s.interpolate('linear', limit=2)
print(filled.tolist())
# [1.0, 2.8, 4.6, NaN, NaN, 10.0]

Выбор заполнения средним или медианой

Заполнение средним и медианой — простые методы, но у них есть важные компромиссы. Среднее чувствительно к выбросам: несколько очень больших значений повышают его, поэтому оно плохо подходит для правосторонне асимметричных распределений, например доходов или цен на дома. Медиана устойчива к выбросам и лучше подходит для асимметричных столбцов. Используйте среднее только тогда, когда данные примерно симметричны и не содержат экстремальных выбросов.

import pandas as pd
import numpy as np

# Skewed income data with an outlier
income = pd.Series([30000, 35000, 32000, 1_000_000, np.nan])

print('Mean:  ', income.mean())    # ~274000 — pulled by outlier
print('Median:', income.median())  # ~33500 — robust choice

# Prefer median for skewed data
filled = income.fillna(income.median())
print(filled.tolist())
# [30000, 35000, 32000, 1000000, 33500.0]

Принцип заполнения KNN

Заполнение методом ближайших соседей (KNN) заменяет пропущенное значение средним (или взвешенным средним) значением k наиболее похожих строк. Похожесть определяется расстоянием по непропущенным признакам. Это многомерная стратегия: она использует информацию из других столбцов, поэтому при коррелированных признаках работает точнее, чем заполнение средним только одного столбца.

KNNImputer из Scikit-learn напрямую работает с массивами NumPy и DataFrames Pandas.

import pandas as pd
import numpy as np
from sklearn.impute import KNNImputer

df = pd.DataFrame({
    'age': [25, 35, np.nan, 45],
    'income': [50000, 70000, 60000, np.nan]
})

imputer = KNNImputer(n_neighbors=2)
df_imputed = pd.DataFrame(
    imputer.fit_transform(df),
    columns=df.columns
)
print(df_imputed.round(1))
#     age   income
# 0  25.0  50000.0
# 1  35.0  70000.0
# 2  30.0  60000.0   <- filled from neighbours
# 3  45.0  65000.0   <- filled from neighbours

Множественное заполнение с IterativeImputer

Множественное заполнение считается эталонным методом обработки пропущенных данных в статистических исследованиях. IterativeImputer из Scikit-learn реализует подход MICE (множественное заполнение с помощью цепных уравнений): он моделирует каждый столбец с пропусками как функцию других столбцов и повторяет заполнение, пока значения не стабилизируются. Такой подход лучше учитывает связи между признаками, чем стратегии для отдельных столбцов.

import pandas as pd
import numpy as np
from sklearn.experimental import enable_iterative_imputer  # noqa
from sklearn.impute import IterativeImputer

df = pd.DataFrame({
    'x1': [1, 2, np.nan, 4, 5],
    'x2': [2, np.nan, 6, 8, 10],
    'y':  [3, 5, 7, np.nan, 11]
})

imp = IterativeImputer(max_iter=10, random_state=0)
df_filled = pd.DataFrame(imp.fit_transform(df), columns=df.columns)
print(df_filled.round(2))

Индикаторные столбцы для пропущенных данных

Вместо того чтобы скрывать факт заполнения значения, рекомендуется добавить двоичный индикаторный столбец, отмечающий строки, в которых до заполнения были пропуски. Это позволяет последующим моделям учитывать саму закономерность пропусков: иногда факт отсутствия значения так же информативен, как и само значение.

import pandas as pd
import numpy as np

df = pd.DataFrame({'salary': [50000, np.nan, 70000, np.nan, 90000]})

# Add indicator before filling
df['salary_was_missing'] = df['salary'].isna().astype(int)

# Then fill
df['salary'] = df['salary'].fillna(df['salary'].median())
print(df)
#    salary  salary_was_missing
# 0  50000.0                   0
# 1  70000.0                   1
# 2  70000.0                   0
# 3  70000.0                   1
# 4  90000.0                   0

Заполнение и утечка данных

Критически важное правило для конвейеров машинного обучения: вычисляйте статистики для заполнения (среднее, медиану, моду) только на обучающем наборе, а затем применяйте те же значения к тестовому набору. Вычисление статистик по всему набору до разделения вызывает утечку данных: модель косвенно видит тестовые данные во время обучения, из-за чего оценка качества становится завышенной. Всегда обучайте средства заполнения на обучающих данных, а затем преобразуйте и обучающий, и тестовый набор.

import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer
from sklearn.model_selection import train_test_split

df = pd.DataFrame({'feature': [1, 2, np.nan, 4, np.nan, 6, 7, 8]})
train, test = train_test_split(df, test_size=0.25, random_state=0)

# Fit ONLY on training data
imp = SimpleImputer(strategy='mean')
train['feature'] = imp.fit_transform(train[['feature']])

# Transform test using training statistics
test['feature'] = imp.transform(test[['feature']])
print('Train mean used:', imp.statistics_[0])

Визуальное сравнение стратегий заполнения

После применения нескольких методов заполнения сравните их влияние, построив рядом распределения исходных и заполненных столбцов. Хорошее заполнение должно как можно лучше сохранять форму исходного распределения (среднее, дисперсию, асимметрию). Заполнение средним сужает распределение, а KNN и MICE обычно сохраняют его лучше.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0)
original = np.random.exponential(scale=5, size=200)
with_nan = original.copy()
with_nan[np.random.choice(200, 40, replace=False)] = np.nan

s = pd.Series(with_nan)

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
s.dropna().hist(ax=axes[0], bins=20, title='Original (no NaN)')
s.fillna(s.mean()).hist(ax=axes[1], bins=20, title='Mean-imputed')
plt.tight_layout()
plt.savefig('imputation_comparison.png')
print('Saved comparison chart')

Выбор подходящей стратегии заполнения

Универсально лучшей стратегии заполнения не существует — правильный выбор зависит от контекста. Используйте среднее или медиану в простых одномерных случаях при небольшом количестве пропусков. Используйте ffill/bfill для временных рядов со стабильными трендами. Выбирайте KNN или IterativeImputer, когда признаки коррелируют и необходимо использовать связи между ними. Используйте константы предметной области (например, 0 для отсутствующего значения или -1 для неизвестного), если пропуск имеет однозначный смысл с точки зрения задачи. Всегда документируйте свой выбор.

# Decision guide (no runnable code)
#
# Missing < 5%  AND data is MCAR?  -> Mean/median fill is fine
# Time series with stable trend?   -> ffill() with limit
# Features are correlated?         -> KNNImputer or IterativeImputer
# Categorical column?              -> Mode fill or 'Unknown' sentinel
# Going into ML model?             -> Add indicator column + fill
# Research / publication quality?  -> Multiple imputation (MICE)
print('Strategy selected based on context')

Быстрая проверка

Проверьте, насколько хорошо Вы поняли интерполяцию и продвинутые методы заполнения.

Итоги урока

В этом уроке Вы узнали, что interpolate() оценивает пропущенные значения, предполагая плавный тренд между известными значениями, method='time' обрабатывает неравные промежутки DatetimeIndex, а продвинутые стратегии, такие как KNNImputer и IterativeImputer, используют другие столбцы для определения значений заполнения. Всегда добавляйте индикаторные столбцы до заполнения и вычисляйте статистики только на обучающем наборе, чтобы избежать утечки данных. Далее мы рассмотрим и преобразуем типы данных столбцов DataFrame.

Можно начать бесплатно

Изучай Python с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
30
Уроки
120

Часто задаваемые вопросы

Урок «Интерполяция и продвинутое заполнение» бесплатный?

Да — полный текст урока «Интерполяция и продвинутое заполнение» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Pandas & NumPy Academy, подпишись на CoddyKit PRO. Курс Pandas & NumPy Academy содержит 4 уроков всего.

Чему я научусь в уроке «Интерполяция и продвинутое заполнение»?

Используйте interpolate() для плавного заполнения на основе времени и определяйте, когда уместнее заполнение средним, а когда медианой. Ты практикуешь Pandas & NumPy Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Pandas & NumPy Academy?

Предыдущий опыт не требуется. Pandas & NumPy Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Интерполяция и продвинутое заполнение»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Pandas & NumPy Academy?

Да. Каждый урок Pandas & NumPy Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Поиск пропущенных значений
  2. Удаление пропущенных значений
  3. Заполнение пропущенных значений
  4. Интерполяция и продвинутое заполнение
← Назад к Pandas & NumPy Academy