Логотип Зефирнет

Обработка недостающих данных для расширенного машинного обучения

Дата:

На протяжении всей этой статьи вы научитесь распознавать, понимать и вписывать недостающие данные. Мы демонстрируем различные методы вменения в реальной задаче логистической регрессии с использованием Python. Правильная обработка пропущенных данных оказывает улучшающее влияние на выводы и прогнозы. Это нельзя игнорировать.

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

Недостающие данные трудно избежать

Значительная часть данных науки или машинного обучения является очистка данных. Часто при сборе данных в наборе данных появляются некоторые пропущенные значения.

Чтобы понять причину отсутствия данных, давайте смоделируем набор данных с двумя предикторами x1x2и переменная ответа y.

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

потерянная информация

Есть 3 основных типа пропущенных значений, о которых нужно беспокоиться.

Пропал совершенно случайно (MCAR)

MCAR возникает, когда вероятность пропуска значений в переменной одинакова для всех выборок.

Например, когда проводится опрос, а значения просто случайно пропускаются при вводе в компьютер, или респондент решил не отвечать на вопрос.

MCAR не влияет на выводы, сделанные моделью, обученной на таких данных.

Чтобы проиллюстрировать MCAR, мы случайным образом удаляем половину значений для x1 а именно:

## Missing Completely at Random (MCAR) # randomly mark half of x1 samples as missing MCAR
# independend of any information recorded
idx_mcar= np.random.choice([0, 1], size=(100,)) == 1 plt.scatter(x1,y, label='data')
plt.scatter(x1[idx_mcar],y[idx_mcar], label='missing (MCAR)', color='red')
plt.xlabel('x1')
plt.ylabel('y')
plt.legend()
plt.title('Missing Completely at Random (MCAR)');
потерянная информация

Образцы x1 (отмечены красным), которые полностью отсутствуют случайным образом (MCAR), не зависят ни от значений x1, ни от значений любых других измеренных переменных.

Случайно пропал (MAR)

Вероятность случайного пропуска значений в переменной зависит только от доступной информации в других предикторах.

Например, когда мужчины и женщины отвечают на вопрос «брали ли вы когда-нибудь отпуск по уходу за ребенком?», Мужчины склонны игнорировать этот вопрос с разной скоростью по сравнению с женщинами.

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

Мы моделируем MAR, удалив x1 значения в зависимости от x2 ценности. когда x2 имеет значение 1, то соответствующее x1 отсутствует.

## Missing at Random (MAR) # randomly mark half of x1 samples as missing MAR
# depending on value of recorded predictor x2 idx_mar = x2 == 1 fig, ax = plt.subplots(1,2,figsize=(15,5))
ax[0].scatter(x1, y, label='data')
ax[0].scatter(x1[idx_mar], y[idx_mar], label='missing', color='red')
ax[0].set_xlabel('x1')
ax[0].set_ylabel('y')
ax[0].legend()
ax[0].set_title('Missing at Random (MAR)');
ax[1].vlines(x1[x2 == 1], 0, 1, color='black')
ax[1].set_xlabel('x1')
ax[1].set_ylabel('x2')
ax[1].set_title('dependent predictor - measured');
потерянная информация

Образцы x1 (помеченные красным), которые отсутствуют случайным образом (MAR), связаны со значениями другой зависимой и измеренной переменной (x2) внутри набора данных. Когда x2 имеет логическое значение 1, то x1 отсутствует.

Не случайно отсутствует (MNAR)

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

Например, в опросе мошенники с меньшей вероятностью ответят на вопрос, обманывали ли они когда-либо.

MNAR почти невозможно обработать.

К счастью, не должно быть никакого влияния MNAR на выводы, сделанные моделью, обученной на таких данных.

Предполагая, что есть гипотетическая переменная x3 это не было измерено, но определяет, какие x1 значения отсутствуют, мы можем смоделировать MNAR следующим образом.

## Missing Not at Random (MNAR) # randomly mark half of x1 samples as missing MNAR
# depending on unrecorded predictor x3
x3 = np.random.uniform(0, 1, 100)
idx_mnar = x3 > .5 fig, ax = plt.subplots(1,2,figsize=(15,5))
ax[0].scatter(x1, y, label='data')
ax[0].scatter(x1[idx_mnar], y[idx_mnar], label='missing', color='red')
ax[0].set_xlabel('x1')
ax[0].set_ylabel('y')
ax[0].legend()
ax[0].set_title('Missing Not at Random (MNAR)');
ax[1].scatter(x1, x3, color='black')
ax[1].axhline(.5, -3, 3)
ax[1].set_xlabel('x1')
ax[1].set_ylabel('x3')
ax[1].set_title('dependent predictor - not measured');
потерянная информация

x1 выборки (отмечены красным), которые отсутствуют не случайно (MNAR), связаны со значениями другой зависимой переменной (x3) за пределами набора данных. Когда x3 больше 0.5, значение x1 отсутствует.

Вам нравится этот всесторонний образовательный контент по прикладному машинному обучению? Подпишитесь на нашу рассылку Enterprise AI быть предупрежденным, когда мы выпустим новый материал.

3 основных подхода к компенсации недостающих ценностей

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

На практике случайное предположение о пропаже разумно.

Несколько различных подходов к вменению пропущенных значений встречаются в литературе:

1. Импутация с использованием нулевого, среднего, медианного или наиболее частого значения

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

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

2. Вменение с использованием случайно выбранного значения

Это работает путем случайного выбора наблюдаемой записи в переменной и использования ее для расчета отсутствующих значений.

3. Вменение с моделью

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

Ассоциация k Алгоритм ближайшего соседа часто используется для расчета отсутствующего значения на основе того, насколько близко оно напоминает точки в обучающем наборе.

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

Модель на основе прогрессивного вменения использует ранее вмененные пропущенные значения для прогнозирования других пропущенных значений.

Дополнительные методы включают импутацию стохастической регрессии, Многочисленные вмененияДатавигИмпутация методом горячей палубы, Экстраполяция, Интерполяция, Удаление по списку.

потерянная информация

Здесь ничего не пропало - Фото Nika Akin на Pixabay

Практическое руководство по обработке пропущенных значений

Описание данных

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

Этот набор данных описывает данные медицинских карт пациентов индейцев пима и наличие у них диабета в течение пяти лет.

Это проблема бинарной классификации (начало диабета 1 или не 0).

Входные переменные, которые описывают каждого пациента, являются числовыми и имеют различные масштабы.

В списке ниже показаны восемь атрибутов плюс целевая переменная для набора данных:

  • Количество раз, когда пациентка была беременна.
  • 2-часовая концентрация глюкозы в плазме за 2 часа в оральном тесте на толерантность к глюкозе.
  • Диастолическое артериальное давление (мм рт. Ст.).
  • Толщина трехглавой кожной складки (мм).
  • 2-часовой сывороточный инсулин (м.е. / мл).
  • Индекс массы тела.
  • Родословная функция диабета.
  • Возраст (лет).
  • Результат (1 - раннее начало диабета в течение пяти лет, 0 - нет).
import pandas as pd pima_df = pd.read_csv('pima-indians-diabetes.csv') response = 'Outcome'
predictors = pima_df.columns.difference([response]).values pima_df.describe()

потерянная информация

Данные имеют 764 наблюдения и 9 особенностей. Мы сразу видим странные значения для некоторых предикторов.

Беременность колеблется до 17 лет. Артериальное давление, глюкоза, толщина кожи, инсулин и переменные ИМТ включают нули, которые физически неправдоподобны.

Нам придется как-то справиться с этим.

Правильно помечать пропущенные значения

Хотя пропущенные значения обычно кодируются с использованием NaN, Null или None, кажется, что ни одно из наблюдений не помечено таким образом.

потерянная информация

В качестве первого шага мы должны правильно отметить пропущенные значения.

С точки зрения отсутствующих данных, переменные, на которые мы должны обратить самое пристальное внимание, это глюкоза, кровяное давление, SkinThickness, инсулин и ИМТ, и все они содержат 0 среди своих наблюдений.

Быстрый поиск в литературе показывает, что эти особенности не могут иметь физиологическое значение ноль. Наиболее правдоподобным объяснением является то, что отсутствующие наблюдения были как-то заменены на ноль.

Эти замаскированные недостающие данные могут ввести в заблуждение наши более поздние попытки классификации. Мы очистим данные, пометив замаскированные пропущенные значения как NaN.

Переменная ответа, которая должна быть закодирована как 0 или 1, содержала значения с или}, добавленные к 0 и 1. Похоже, ошибка похожа на ошибку, возникающую при чтении или записи CSV-файлов. Решение, которое мы выбираем, состоит в том, чтобы просто удалить персонажей.

Что касается типов данных, предикторы хранятся в виде числа с плавающей запятой, а ответ - в виде объекта. Тип данных с плавающей точкой имеет смысл для ИМТ и функции DiabetesPedigreeFunction. Остальные функции могут быть сохранены как целые числа.

Следующая функция очищает данные и заменяет нули на NaN для пяти обсуждаемых столбцов: GlucoseBloodPressureSkinThicknessInsulinкачества BMI.

def clean_data(df_raw, cols_with_zeros=['Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI'], response = ['Outcome']): df = df_raw.copy() # replace zero with NaN in features df[cols_with_zeros] = df[cols_with_zeros].replace(0, np.nan) # remove and } from response df = df.replace(to_replace=r'\|}', value='', regex=True) # change response data type to int df[response] = df[response].astype('int') return df pima_df_cleaned = clean_data(pima_df)
pima_df_cleaned.head()

потерянная информация

Мы заменили любой ноль и указали пропущенные значения на NaN.

Изучение пропущенных значений

Теперь мы можем рассчитать долю пропущенных значений для каждой функции: 48.56% инсулина, 29.58% SkinThickness, 4.58% кровяного давления, 1.43% ИМТ и 0.65% глюкозы. Остальные функции не имеют пропущенных значений.

print("Proportion of missing values")
missing_values_count = pima_df_cleaned.isna().sum()*100/pima_df_cleaned.shape[0]
features_with_missing_values = missing_values_count[missing_values_count>0].index.values
missing_values_count

потерянная информация

В качестве второго шага нам может потребоваться выполнить некоторые статистические проверки гипотезы о том, что функции отсутствуют в случайном порядке (MAR), полностью отсутствуют в случайном порядке (MCAR) или отсутствуют в случайном порядке (MNAR).

Если присмотреться, кажется, что пропущенные значения для SkinThickness коррелируют с отсутствующими значениями для инсулина. Когда SkinThickness отсутствует, то инсулин также отсутствует.

Кроме того, когда BloodPressure или BMI отсутствуют, вероятность того, что значения инсулина или SkinThickness также будут отсутствовать, выше.

потерянная информация

В качестве третьего шага мы исследуем и выбираем наиболее подходящий метод для обработки пропущенных значений.

Стратегии обработки недостающих данных

Мы продолжим, предоставив функцию Python, в которой реализованы следующие стратегии вменения данных.

Ассоциация стратегия отбрасывания удаляет все наблюдения, где хотя бы одна из характеристик имеет отсутствующее значение (NaN).

Ассоциация средняя стратегия заменяет любое пропущенное значение (NaN) на среднее значение всех значений, доступных для этой функции.

Ассоциация срединная стратегия и самая частая стратегия замените пропущенные значения медианой или наиболее часто появляющимися значениями соответственно.

Ассоциация модельно-ориентированная стратегия использует функции без пропущенных значений для обучения регрессионных моделей kNN.

Мы выбираем kNN, чтобы уловить изменчивость доступных данных. Это было бы не так, если бы мы использовали модель линейной регрессии, которая предсказывала бы пропущенные значения вдоль линии регрессии.

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

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

Вмененные данные могут быть необязательно стандартизированы между 0 и 1. Это может улучшить производительность классификации с использованием регуляризованной логистической регрессии.

Поскольку объекты находятся в другом масштабе (например, возраст и инсулин), а диапазон их значений отличается (например, родословная или инсулин), штраф за усадку может быть неверно рассчитан, если объекты не масштабированы.

from sklearn.impute import SimpleImputer
from sklearn.neighbors import KNeighborsRegressor
from sklearn.preprocessing import MinMaxScaler # function for KNN model-based imputation of missing values using features without NaN as predictors
def impute_model_basic(df): cols_nan = df.columns[df.isna().any()].tolist() cols_no_nan = df.columns.difference(cols_nan).values for col in cols_nan: test_data = df[df[col].isna()] train_data = df.dropna() knr = KNeighborsRegressor(n_neighbors=5).fit(train_data[cols_no_nan], train_data[col]) df.loc[df[col].isna(), col] = knr.predict(test_data[cols_no_nan]) return df # function for KNN model-based imputation of missing values using features without NaN as predictors, # including progressively added imputed features
def impute_model_progressive(df): cols_nan = df.columns[df.isna().any()].tolist() cols_no_nan = df.columns.difference(cols_nan).values while len(cols_nan)>0: col = cols_nan[0] test_data = df[df[col].isna()] train_data = df.dropna() knr = KNeighborsRegressor(n_neighbors=5).fit(train_data[cols_no_nan], train_data[col]) df.loc[df[col].isna(), col] = knr.predict(test_data[cols_no_nan]) cols_nan = df.columns[df.isna().any()].tolist() cols_no_nan = df.columns.difference(cols_nan).values return df # function for imputing missing data according to a given impute_strategy:
# drop_rows: drop all rows with one or more missing values
# drop_cols: drop columns with one or more missing values
# model_basic: KNN-model-based imputation with fixed predictors
# model_progressive: KNN-model-based imputation with progressively added predictors
# mean, median, most_frequent: imputation with mean, median or most frequent values
#
# cols_to_standardize: if provided, the specified columns are scaled between 0 and 1, after imputation
def impute_data(df_cleaned, impute_strategy=None, cols_to_standardize=None): df = df_cleaned.copy() if impute_strategy == 'drop_rows': df = df.dropna(axis=0) elif impute_strategy == 'drop_cols': df = df.dropna(axis=1) elif impute_strategy == 'model_basic': df = impute_model_basic(df) elif impute_strategy == 'model_progressive': df = impute_model_progressive(df) else: arr = SimpleImputer(missing_values=np.nan,strategy=impute_strategy).fit( df.values).transform(df.values) df = pd.DataFrame(data=arr, index=df.index.values, columns=df.columns.values) if cols_to_standardize != None: cols_to_standardize = list(set(cols_to_standardize) & set(df.columns.values)) df[cols_to_standardize] = df[cols_to_standardize].astype('float') df[cols_to_standardize] = pd.DataFrame(data=MinMaxScaler().fit( df[cols_to_standardize]).transform(df[cols_to_standardize]), index=df[cols_to_standardize].index.values, columns=df[cols_to_standardize].columns.values) return df
потерянная информация

Начало диабета? Фотография Мириам на Pixabay

Логистическая регрессия с отсутствующими данными

В этом разделе мы подгоняем модель логистической регрессии к очищенным данным после применения определенной стратегии вменения:

  • отбрасывание строк с пропущенными значениями,
  • отбрасывание столбцов с пропущенными значениями,
  • вменяя пропущенные значения в среднее значение столбца,
  • вменение пропущенных значений с помощью прогнозирования на основе модели,
  • приписывание пропущенных значений с помощью прогрессивного прогнозирования на основе модели.
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegressionCV
from sklearn.metrics import accuracy_score
from timeit import default_timer as timer # function for handling missing values # and fitting logistic regression on clean data
def logistic_regression(data, impute_strategy=None, cols_to_standardize=None, test_size=0.25, random_state=9001): start = timer() # store original columns original_columns = data.columns.difference(['Outcome']) df_imputed = impute_data(data, impute_strategy, cols_to_standardize) train_data, test_data = train_test_split(df_imputed, test_size=test_size, random_state=random_state) # note which predictor columns were dropped or kept kept_columns = df_imputed.columns.difference(['Outcome']) dropped_columns = original_columns.difference(df_imputed.columns) original_columns = original_columns.difference(['Outcome']) # prepare tensors X_train = train_data.drop(columns=['Outcome']) y_train = train_data['Outcome'] X_test = test_data.drop(columns=['Outcome']) y_test = test_data['Outcome'] # model training logistic_model = LogisticRegressionCV(cv=10, penalty='l2', max_iter=1000).fit( X_train, y_train) # model evaluation train_score = accuracy_score(y_train, logistic_model.predict(X_train)) test_score = accuracy_score(y_test, logistic_model.predict(X_test)) duration = timer() - start print("Classification rate on training data: {}".format(train_score)) print("Classification rate on test data: {}".format(test_score)) print("Execution time: {}".format(duration)) return { 'imputation strategy': impute_strategy, 'standardized': cols_to_standardize!=None, 'model': logistic_model, 'train score': train_score, 'test score': test_score, 'execution time (s)': duration } # list to store models' performance lr_results = [] # prepare data
pima_df_cleaned = clean_data(pima_df)
cols_to_standardize=['Age','BMI','BloodPressure','Glucose','Insulin','Pregnancies','SkinThickness','DiabetesPedigreeFunction'] # fit logistic regression for each imputation strategy
# with and without standardizing features
for impute_strategy in ['drop_rows', 'mean', 'model_basic', 'model_progressive']: for cols in [None, cols_to_standardize]: result = logistic_regression(pima_df_cleaned, impute_strategy=impute_strategy, cols_to_standardize=cols) lr_results.append(result) # display logistic regression performance
lr_results_df = pd.DataFrame(lr_results)
lr_results_df.drop(['model'], axis=1).drop_duplicates()

Стоит отметить значительный эффект, который вменение вносит в значения оценочных параметров и их точность.

потерянная информация

Влияние стратегии снижения на точность

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

Это приводит к переобучению в наборе обучающих данных и ухудшению качества прогнозирования в наборе тестовых данных.

Влияние среднего значения / вменения модели на точность

Среднее вменение отсутствующих данных уменьшает переобучение и улучшает прогнозирование в наборе тестовых данных.

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

Влияние вменения на время тренировки

Сложность вычислений оценивается путем измерения совокупного времени выполнения вменения, подбора модели логистической регрессии и прогнозирования.

Время выполнения для подхода, основанного на модели, является максимальным, когда предикторы не стандартизированы. Расчет евклидова расстояния до ближайших соседей требует больше времени выполнения, чем вычисление среднего значения данных.

При работе с очень большим количеством наблюдений мы можем предпочесть среднее вменение за счет более низкой точности классификации.

Влияние вменения на умозаключение

Мы получаем коэффициенты, оцененные нашими регуляризованными моделями логистической регрессии, следующим образом.

# get index of strategies
lr_results_df = pd.DataFrame(lr_results)
strategies = lr_results_df['imputation strategy'] # get a boolean array where True => standardized
standardized = lr_results_df['standardized']
st = lambda s: ' standardized' if s else ''
coefs_ = {}
for key, value in enumerate(strategies): if value == 'drop_cols': # skip pass else: strategy = value + st(standardized[key]) coefs_[strategy] = lr_results_df['model'][key].coef_[0]
coef_df = pd.DataFrame(data=coefs_, index=predictors)
coef_df.T

потерянная информация

Коэффициенты в таблице показывают, как каждый предиктор влияет на вероятность возникновения диабета.

Положительные значения указывают на факторы, способствующие возникновению диабета.

Значения около нуля предполагают, что связанные предикторы не вносят большой вклад в начало диабета.

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

  • Общие стандартизированные коэффициенты больше.
  • Когда строки / столбцы отбрасываются, оценки коэффициентов резко изменяются по сравнению с их значениями, когда отсутствующие данные вменяются средним значением или моделью.
  • Коэффициенты инсулина не сильно различаются между методами вменения. Инсулин имел самый высокий процент пропущенных значений между всеми предикторами. Это говорит о том, что инсулин может отсутствовать совершенно случайно (MCAR).

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

coef_perc_df = coef_df.copy()
cols = coef_df.columns.difference(['drop_rows']).values
for col in cols: coef_perc_df[col] = np.round(100*(coef_df[col]/coef_df['drop_rows']-1))
coef_perc_df[['drop_rows','mean','model_basic','model_progressive']]

потерянная информация

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

Другие столбцы показывают процентное изменение значений коэффициентов после вменения пропущенных значений по сравнению со стратегией отбрасывания.

Возраст, кровяное давление и функция родословной имеют наибольшее процентное изменение между отбрасываемыми рядами и средними / модельными вменениями.

Стратегия отбрасывания обычно не золотой путь.

потерянная информация

Раскраска Софи Мадлен

Заключение

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

Различение пропущенных данных по типам MCAR, MAR и MNAR является существенным, поскольку они могут существенно повлиять на выводы и прогнозы.

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

Компенсация недостающих данных и использование полученных ими знаний является одной из трудоемких частей работы Data Scientist.

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

Эта статья была первоначально опубликована в Medium и повторно опубликовано в TOPBOTS с разрешения автора.

Наслаждайтесь этой статьей? Подпишитесь на дополнительные обновления на прикладной ML.

Мы сообщим вам, когда выпустим больше технического образования.

Источник: https://www.topbots.com/handling-missing-data-for-machine-learning/?utm_source=rss&utm_medium=rss&utm_campaign=handling-missing-data-for-machine-learning

Spot_img

Последняя разведка

Spot_img

Чат с нами

Всем привет! Могу я чем-нибудь помочь?