6.4. Восстановление пропущенных значений

По разным причинам многие наборы данных реального мира содержат пропущенные значения, часто закодированные как пробелы, NaN или другие заполнители. Однако такие наборы данных несовместимы с оценщиками scikit-learn, которые предполагают, что все значения в массиве являются числовыми, и что все они имеют и имеют значение. Основная стратегия использования неполных наборов данных — отбрасывать целые строки и / или столбцы, содержащие пропущенные значения. Однако это происходит ценой потери данных, которые могут быть ценными (хотя и неполными). Лучшая стратегия — это вменять недостающие значения, т. Е. Вывести их из известной части данных. См. Статью о восстановлении в Глоссарии общих терминов и элементов API .

6.4.1. Одномерное и многомерное восстановление

Одним из типов алгоритма восстановления является одномерный, который вменяет значения в i-м измерении признака, используя только не пропущенные значения в этом измерении признака (например, impute.SimpleImputer). Напротив, многомерные алгоритмы восстановления используют весь набор доступных измерений характеристик для оценки недостающих значений (например, impute.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 также поддерживает категорические данные , представленные в виде строковых значений или панд categoricals при использовании '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 ) и использование случайного состояния ( # 15611 ). Чтобы использовать его, вам необходимо явно импортировать 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.4. Ссылки

Стеф ван Бюрен, Карин Гротуис-Оудсхорн (2011). «Мыши: многомерное вычисление цепными уравнениями в R». Журнал статистического программного обеспечения 45: 1-67.

Родерик Дж. А. Литтл и Дональд Б. Рубин (1986). «Статистический анализ с отсутствующими данными». John Wiley & Sons, Inc., Нью-Йорк, Нью-Йорк, США.

6.4.5. Расчет ближайших соседей

Класс KNNImputer обеспечивает восстановления для заполнения пропущенных значений с использованием подхода к-ближайших соседей. По умолчанию 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. ]])

OL2001 Ольга Троянская, Майкл Кантор, Гэвин Шерлок, Пэт Браун, Тревор Хасти, Роберт Тибширани, Дэвид Ботштейн и Расс Б. Альтман, Методы оценки недостающих значений для микрочипов ДНК, БИОИНФОРМАТИКА Vol. 17 нет. 6, 2001 Страницы 520-525.

6.4.6. Маркировка вмененных значений

Трансформатор MissingIndicator является полезным для преобразования набора данных в соответствующем бинарную матрицу , указывающую на наличие пропущенных значений в наборе данных. Это преобразование полезно в сочетании с восстановлением. При использовании восстановления сохранение информации о пропущенных значениях может быть информативным. Обратите внимание, что оба параметра SimpleImputer и IterativeImputer имеют логический параметр add_indicatorFalse по умолчанию), который, если установлен в, True обеспечивает удобный способ объединения выхода MissingIndicator трансформатора с выходом импьютера.

NaN обычно используется в качестве заполнителя для пропущенных значений. Однако он заставляет тип данных быть плавающим. Параметр 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 в a 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 классификатор (например, a DecisionTreeClassifier), чтобы иметь возможность делать прогнозы.

>>> clf = make_pipeline(transformer, DecisionTreeClassifier())
>>> clf = clf.fit(X_train, y_train)
>>> results = clf.predict(X_test)
>>> results.shape
(100,)