6.4. Вставка пропущенных значений
По разным причинам многие реальные наборы данных содержат пропущенные значения, часто закодированные как пробелы, NaN или другие заполнительные символы. Такие наборы данных, однако, несовместимы с оценщиками scikit-learn, которые предполагают, что все значения в массиве являются числовыми и что все они имеют и сохраняют значение. Основная стратегия использования неполных наборов данных — это отбрасывание целых строк и/или столбцов, содержащих пропущенные значения. Однако это влечёт потерю данных, которые могут быть ценными (даже если они неполные). Лучшая стратегия — это вставка пропущенных значений, то есть их вычисление по известной части данных. См. запись в глоссарии на вставке.
6.4.1. Унивариатная и многовариатная вставка
Один тип алгоритмов вставки — это унивариатный, который вставляет значения в i-й размерности признака, используя только непропущенные значения в этой размерности признака (например, SimpleImputer). В отличие от этого, многовариатные алгоритмы вставки используют весь набор доступных размерностей признаков для оценки пропущенных значений (например, IterativeImputer).
6.4.2. Унивариатная вставка признаков
Класс SimpleImputer предоставляет базовые стратегии для вставки пропущенных значений. Пропущенные значения могут быть вставлены с заданным константным значением или с использованием статистических данных (среднего, медианы или наиболее часто встречающегося значения) каждого столбца, в котором находятся пропущенные значения. Этот класс также позволяет использовать различные кодировки пропущенных значений.
Следующий фрагмент демонстрирует, как заменить пропущенные значения, закодированные как np.nan, средним значением столбцов (ось 0), которые содержат пропущенные значения:
>>> import numpy as np >>> from sklearn.impute import SimpleImputer >>> imp = SimpleImputer(missing_values=np.nan, strategy='mean') >>> imp.fit([[1, 2], [np.nan, 3], [7, 6]]) SimpleImputer() >>> X = [[np.nan, 2], [6, np.nan], [7, 6]] >>> print(imp.transform(X)) [[4. 2. ] [6. 3.666...] [7. 6. ]]
Класс SimpleImputer также поддерживает разреженные матрицы:
>>> import scipy.sparse as sp >>> X = sp.csc_matrix([[1, 2], [0, -1], [8, 4]]) >>> imp = SimpleImputer(missing_values=-1, strategy='mean') >>> imp.fit(X) SimpleImputer(missing_values=-1) >>> X_test = sp.csc_matrix([[-1, 2], [6, -1], [7, 6]]) >>> print(imp.transform(X_test).toarray()) [[3. 2.] [6. 3.] [7. 6.]]
Обратите внимание, что этот формат не предназначен для неявного хранения пропущенных значений в матрице, потому что он будет её уплотнять во время преобразования. Пропущенные значения, закодированные как 0, должны использоваться с плотной входной матрицей.
Класс SimpleImputer также поддерживает категориальные данные, представленные строковыми значениями или pandas категориями, при использовании стратегии 'most_frequent' или 'constant'.
>>> import pandas as pd >>> df = pd.DataFrame([["a", "x"], ... [np.nan, "y"], ... ["a", np.nan], ... ["b", "y"]], dtype="category") ... >>> imp = SimpleImputer(strategy="most_frequent") >>> print(imp.fit_transform(df)) [['a' 'x'] ['a' 'y'] ['a' 'y'] ['b' 'y']]
Для другого примера использования см. Вставка пропущенных значений перед построением оценщика.
6.4.3. Многовариатная вставка признаков
Более сложный подход — использование класса IterativeImputer, который моделирует каждый признак с пропущенными значениями как функцию других признаков и использует эту оценку для вставки. Он делает это итерационным способом, поочерёдно: на каждом шаге столбец признака обозначается как выход y и другие столбцы признаков обрабатываются как вход X. Регрессор обучается на (X,
y) для известных y. Затем регрессор используется для предсказания пропущенных значений y. Это делается для каждого признака итерационным способом, а затем повторяется для max_iter раундов вставки. Возвращаются результаты последнего раунда вставки.
Примечание
Этот оценщик пока ещё находится на стадии экспериментальной разработки: значения по умолчанию или детали поведения могут измениться без предварительного уведомления. Для стабилизации IterativeImputer необходимо решить следующие проблемы: критерии сходимости (#14338) и оценщики по умолчанию (#13286). Для использования необходимо явно импортировать enable_iterative_imputer.
>>> import numpy as np >>> from sklearn.experimental import enable_iterative_imputer >>> from sklearn.impute import IterativeImputer >>> imp = IterativeImputer(max_iter=10, random_state=0) >>> imp.fit([[1, 2], [3, 6], [4, 8], [np.nan, 3], [7, np.nan]]) IterativeImputer(random_state=0) >>> X_test = [[np.nan, 2], [6, np.nan], [np.nan, 6]] >>> # the model learns that the second feature is double the first >>> print(np.round(imp.transform(X_test))) [[ 1. 2.] [ 6. 12.] [ 3. 6.]]
Как SimpleImputer, так и IterativeImputer можно использовать в конвейере, чтобы создать составной оценщик, поддерживающий вставку. См. Вставка пропущенных значений перед построением оценщика.
6.4.3.1. Гибкость IterativeImputer
В экосистеме R для работы с данными существует много хорошо зарекомендовавших себя пакетов для вставки: Amelia, mi, mice, missForest и т.д. missForest популярен и оказывается частным случаем различных последовательных алгоритмов вставки, которые можно реализовать с помощью IterativeImputer, передав различные регрессоры для предсказания пропущенных значений признаков. В случае missForest этот регрессор — случайный лес. См. Вставка пропущенных значений с вариантами IterativeImputer.
6.4.3.2. Многократная против одиночной вставки
В статистическом сообществе принято выполнять многократные вставки, генерируя, например, m отдельные вставки для одной матрицы признаков. Каждая из этих m вставок затем пропускается через последующую аналитическую цепочку (например, разработка признаков, кластеризация, регрессия, классификация). m результаты анализа (например, ошибки валидации на отложенных данных) позволяют аналитику понять, как результаты анализа могут отличаться в зависимости от неопределённости, обусловленной пропущенными значениями. Эта практика называется многократной вставкой.
Наша реализация IterativeImputer вдохновлена пакетом R MICE (многократная вставка с цепными уравнениями) [1], но отличается от него тем, что возвращает одну вставку вместо нескольких. Однако, IterativeImputer также можно использовать для многократных вставок, применяя её многократно к одному и тому же набору данных с различными начальными значениями генератора случайных чисел, когда sample_posterior=True. См. [2], глава 4 для более подробного обсуждения многократной и одиночной вставок.
Вопрос о том, насколько полезной является одиночная или многократная вставка в контексте предсказания и классификации, когда пользователя не интересует измерение неопределённости, обусловленной пропущенными значениями, до сих пор остаётся открытым.
Обратите внимание, что вызов метода transform класса IterativeImputer не допускается для изменения количества образцов. Поэтому многократные вставки нельзя выполнить одним вызовом transform.
6.4.3.3. Список литературы
6.4.4. Медианное заполнение пропущенных значений
Класс KNNImputer предоставляет возможность заполнения пропущенных значений с использованием метода k ближайших соседей. По умолчанию используется метрика евклидова расстояния, поддерживающая пропущенные значения, nan_euclidean_distances, для поиска ближайших соседей. Каждое пропущенное значение признака заполняется значениями из n_neighbors ближайших соседей, имеющих значение для данного признака. Значения признаков соседей усредняются равномерно или взвешиваются в зависимости от расстояния до каждого соседа. Если у образца пропущено несколько признаков, то соседи для этого образца могут быть разными в зависимости от конкретного импутируемого признака. Когда количество доступных соседей меньше n_neighbors, и расстояния до набора обучения не определены, для импутации используется среднее значение признака из набора обучения. Если имеется хотя бы один сосед с определенным расстоянием, то во время импутации используется взвешенное или невзвешенное среднее оставшихся соседей. Если признак всегда пропущен в наборе обучения, он удаляется во время transform. Для получения дополнительной информации о методологии, см. ссылку [OL2001].
Следующий фрагмент кода демонстрирует, как заменить пропущенные значения, закодированные как np.nan, используя среднее значение признака из двух ближайших соседей образцов с пропущенными значениями:
>>> import numpy as np
>>> from sklearn.impute import KNNImputer
>>> nan = np.nan
>>> X = [[1, 2, nan], [3, 4, 3], [nan, 6, 5], [8, 8, 7]]
>>> imputer = KNNImputer(n_neighbors=2, weights="uniform")
>>> imputer.fit_transform(X)
array([[1. , 2. , 4. ],
[3. , 4. , 3. ],
[5.5, 6. , 5. ],
[8. , 8. , 7. ]])
Для другого примера использования см. Заполнение пропущенных значений перед построением оценщика.
Список литературы
6.4.5. Сохранение постоянного количества признаков
По умолчанию импутаторы scikit-learn будут удалять полностью пустые признаки, то есть столбцы, содержащие только пропущенные значения. Например:
>>> imputer = SimpleImputer()
>>> X = np.array([[np.nan, 1], [np.nan, 2], [np.nan, 3]])
>>> imputer.fit_transform(X)
array([[1.],
[2.],
[3.]])
Первый признак в X, содержащий только np.nan был удален после импутации. Хотя этот признак не будет полезен в задачах прогнозирования, удаление столбцов изменит форму X, что может быть проблематично при использовании импутаторов в более сложной pipeline машинного обучения. Параметр keep_empty_features предоставляет возможность сохранения пустых признаков путем импутации постоянным значением. В большинстве случаев это постоянное значение равно нулю:
>>> imputer.set_params(keep_empty_features=True)
SimpleImputer(keep_empty_features=True)
>>> imputer.fit_transform(X)
array([[0., 1.],
[0., 2.],
[0., 3.]])
6.4.6. Маркировка импутированных значений
Трансформер MissingIndicator полезен для преобразования набора данных в соответствующую бинарную матрицу, указывающую на наличие пропущенных значений в наборе данных. Это преобразование полезно в сочетании с импутацией. При использовании импутации сохранение информации о том, какие значения были пропущены, может быть информативным. Обратите внимание, что как SimpleImputer, так и IterativeImputer имеют булевый параметр add_indicator (False по умолчанию), который при установке в значение True обеспечивает удобный способ объединения выходных данных трансформера MissingIndicator с выходными данными импутатора.
NaN обычно используется в качестве заполнителя для пропущенных значений. Однако это навязывает тип данных float. Параметр missing_values позволяет указать другой заполнитель, например, целое число. В следующем примере мы будем использовать -1 в качестве пропущенных значений:
>>> from sklearn.impute import MissingIndicator
>>> X = np.array([[-1, -1, 1, 3],
... [4, -1, 0, -1],
... [8, -1, 1, 0]])
>>> indicator = MissingIndicator(missing_values=-1)
>>> mask_missing_values_only = indicator.fit_transform(X)
>>> mask_missing_values_only
array([[ True, True, False],
[False, True, True],
[False, True, False]])
Параметр features используется для выбора признаков, для которых создается маска. По умолчанию это 'missing-only', который возвращает маску импутатора признаков, содержащих пропущенные значения в момент fit:
>>> indicator.features_ array([0, 1, 3])
Параметр features может быть установлен в значение 'all' для возврата всех признаков, содержат ли они пропущенные значения или нет:
>>> indicator = MissingIndicator(missing_values=-1, features="all")
>>> mask_all = indicator.fit_transform(X)
>>> mask_all
array([[ True, True, False, False],
[False, True, False, True],
[False, True, False, False]])
>>> indicator.features_
array([0, 1, 2, 3])
При использовании MissingIndicator в Pipeline, обязательно используйте FeatureUnion или ColumnTransformer для добавления признаков-индикаторов к обычным признакам. Сначала мы получим набор данных iris, и добавим к нему некоторые пропущенные значения.
>>> from sklearn.datasets import load_iris >>> from sklearn.impute import SimpleImputer, MissingIndicator >>> from sklearn.model_selection import train_test_split >>> from sklearn.pipeline import FeatureUnion, make_pipeline >>> from sklearn.tree import DecisionTreeClassifier >>> X, y = load_iris(return_X_y=True) >>> mask = np.random.randint(0, 2, size=X.shape).astype(bool) >>> X[mask] = np.nan >>> X_train, X_test, y_train, _ = train_test_split(X, y, test_size=100, ... random_state=0)
Теперь мы создаем FeatureUnion. Все признаки будут импутированы с помощью SimpleImputer, для того чтобы классификаторы могли работать с этими данными. Кроме того, он добавляет индикаторные переменные из MissingIndicator.
>>> transformer = FeatureUnion(
... transformer_list=[
... ('features', SimpleImputer(strategy='mean')),
... ('indicators', MissingIndicator())])
>>> transformer = transformer.fit(X_train, y_train)
>>> results = transformer.transform(X_test)
>>> results.shape
(100, 8)
Конечно, мы не можем использовать трансформер для создания прогнозов. Мы должны обернуть это в Pipeline с классификатором (например, DecisionTreeClassifier), чтобы иметь возможность делать прогнозы.
>>> clf = make_pipeline(transformer, DecisionTreeClassifier()) >>> clf = clf.fit(X_train, y_train) >>> results = clf.predict(X_test) >>> results.shape (100,)
6.4.7. Оценщики, обрабатывающие значения NaN
Некоторые оценщики разработаны для обработки значений NaN без предобработки. Ниже приведен список этих оценщиков, классифицированных по типу (кластеризация, регрессия, классификация, преобразование):
-
Оценщики, допускающие значения NaN для типа
cluster: -
Оценщики, допускающие значения NaN для типа
regressor: -
Оценщики, допускающие значения NaN для типа
classifier: -
Оценщики, допускающие значения NaN для типа
transformer:- IterativeImputer
- KNNImputer
- MaxAbsScaler
- MinMaxScaler
- MissingIndicator
- OneHotEncoder
- OrdinalEncoder
- PowerTransformer
- QuantileTransformer
- RandomTreesEmbedding
- RobustScaler
- SimpleImputer
- StackingClassifier
- StackingRegressor
- StandardScaler
- TargetEncoder
- VarianceThreshold
- VotingClassifier
- VotingRegressor
© 2007–2025 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.6/modules/impute.html