
Автор статьи — Виктория Ляликова.
На данный момент Python является самым популярным языком программирования, который применяется для анализа данных или в машинном обучении. Сильными сторонами Python являются его модульность и возможность интегрироваться с другими языками программирования.
В науке о данных разведочный анализ данных (exploratory data analysis, EDA) является самым важным этапом в проекте и занимает около 70-80% времени всего проекта. Такой анализ позволяет изучить какие-то свойства данных, найти в них закономерности, аномалии, очистить их, подготовить и построить начальные модели для дальнейшей работы. На этом этапе можно определить вид распределения, оценить основные его параметры, обнаружить выбросы, построить матрицу корреляции признаков и т.д.
И в предварительном анализе достаточно серьезной проблемой является обнаружение недостающих (пропущенных) значений и самым сложным является то, что здесь нет какого-то универсального алгоритма. Для каждой конкретной задачи приходится искать наиболее подходящие методы или их комбинации.
Так как большинство моделей машинного обучения не могут обрабатывать пропущенные значения, значит их нел��зя игнорировать в данных и эту проблему необходимо решать во время предобработки. Самым простым решением является удаление каждого наблюдения, содержащего одно или несколько пропущенных значений. Эта задача быстро и легко выполняется с помощью библиотек Numpy или pandas.
Вместе с этим необходимо стараться воздерживаться от удаления наблюдений с отсутствующими значениями. Их удаление является крайним средством, поскольку тогда алгоритм теряет доступ к полезной информации, содержащейся в непропущенных значениях наблюдений.
Можно выделить следующие основные стратегии замены пропущенных данных подстановочными значениями.
Заменить пропущенные значения средним/медианой. В данном случае необходимо вычислить среднее/медиану имеющихся значений для каждого столбца и вставить то, что получилось в пропущенные ячейки. Данный метод является простым и быстрым, но не работает с качественными переменными. Также невозможно оценить погрешность импутации.
Замена самым часто встречающимся значением или константой. Также является еще одним простым методом импутации. Можно использовать для качественных переменных
Замена данных, используя метод k ближайших соседей или kNN. Является самым популярным методом машинного обучения для решения задач классификации. Основан на оценивании сходства объектов. Используется функция расстояния Евклида

где x_{ik} и x_{jk} — к-е элементы векторов x_{i} и x_{j} соответственно
У какого класса выше значение близости, тот класс и присваивается новому объекту.
Тогда с помощью данного метода можно вычислить значения пропущенных атрибутов на основании дистанций от попавших в область объектов и соответствующих значений этого же атрибута у объектов

где a_{i} — i-ый объект, попавший в область, k_{i} — значение атрибута k у заданного объекта a_{i}, x — новый объект, x_{k} — ый атрибут нового объекта.
Иным словами, выбирается k-точек, которые больше всего похожи на рассматриваемую, и уже на их основании выбирается значение для пустой ячейки.
Данный метод на некоторых наборах данных может быть точнее среднего/медианы или константы, учитывает корреляцию между параметрами. Главным недостатком данного метода является то, что он является вычислительно дорогим, так как требует держать весь набор данных в памяти, чтобы вычислить расстояние между пропущенным значением и каждым отдельным значением.
Множественная импутация данных (MICE). Суть данного метода заключается в том, что импутация каждого значения проводится не один раз, а много. Такой тип замены пропущенных значений позволяет понять насколько надежно или ненадежно предложенное значений. Также, MICE, позволяет работать с переменным разных типов.
Импутация данных с помощью глубоко обучения. Библиотека datawig позволяет восстанавливать недостающие значения за счет тренировки нейронной сети на тех точках, для которых есть все параметры.
Теперь попробуем разобрать некоторые возмож��ости библиотек Phyton, которые помогут нам решить проблему недостающих значений. Для исследования необходимо выбрать набор данных (DataSet). Разнообразные наборы данных можно скачать с сайта (kaggle.com). Для демонстрации возможностей исследуем набор данных с информацией о диабете, где пациентами являются женщины не моложе 21 года индейского происхождения Пима.
Загружаем необходимые библиотеки и датасет
import pandas as pd
import numpy as np
data_diabetes = pd.read_csv('......../datasets/diabetes.csv')
data_diabetes
Данный набор содержит следующие поля: Pregnancies (количество беременностей), Glucose (уровень глюкозы), BloodPressure (давление), SkinThickness (толщина кожной складки трицепса в мм), Insulin (уровень инсулина), BMI (индекс массы тела), DiabetesPedigree (функция наличия диабета у родственников), Age (возраст), Outcome (наличие диабета).
Посмотрим на основные характеристики, каждого признака.
data_diabetes.describe()
Можно увидеть, почти в каждом столбце есть нулевые значения. Посчитаем их количество
(data diabetes ==0).sum()
Можно предположить, что это пропущенные значения, где неизвестное значение было заменено нулем. Преобразуем все нули в значениях 'Glucose','BloodPressure','SkinThickness','Insulin','BMI' на значения NaN и еще раз посчитаем их количество, предварительно сделав копию нашего датасета.
new_data = data_diabetes.copy(deep = True)
colsFix = ['Glucose','BloodPressure','SkinThickness','Insulin','BMI']
new_data [colsFix] = new_data[colsFix].replace(0, np.NaN)
new_data.isnull().sum()
И еще раз посмотрим на характеристики датасета
new_data.describe()
Почти в каждом столбце, кроме max произошли какие-то изменения.
Каким же образом можно проводить импутацию данных? Будем работать только со столбцами SkinThickness и Insulin, так как они имеют больше всего пропущенных значений.
1. В библиотеке Pandas (которая была уже загружена ранее) есть метод fillna(), который как раз позволяе�� заполнить пропущенные значения.
Синтаксис метода следующий
DataFrame.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None)Наверное, нет смысла описывать все параметры этого метода. Описание можно посмотреть здесь.
Важно, что данный метод возвращает объект, в котором заполняются все пропущенные значения. Можно заменить пропущенные значения, например, средним, медианой, самым часто встречающимся значением, константой.
Вычисли эти значения, а затем заменим пропущенные значения медианой используя метод fillna.
Параметр SkinThickness
Медиана | Среднее | Самое часто встречающееся значение |
median_Skin = new_data.Skin.median() median_Skin 29 | mean_Skin= new_data.Skin.mean() mean_Skin 29.153419593345657 | mode_Skin = new_data.Skin.mode() mode_Skin 32 |
Параметр Isulin
median_Insulin = new_data.Insulin. median() median_Insulin 125 | mean_Insulin = new_data.Insulin. mean() mean_Insulin 155.5482233502538 | mode_Insulin = new_data.Insulin. mode() mode_Insulin 105 |
Воспользуемся методом fillna() для восстановления значений
new_data = new_data.fillna({'SkinThickness':median_SkinThickness})
new_data = new_data.fillna({'Insulin':median_Insulin}) В данном случае при использовании метода fillna указывается столбец и значение, которые необходимо заполнить.
Если необходимо заполнить значения в нескольких столбцах сразу, тогда можно сделать, например, так
new_data = new_data.fillna({'Insulin':median_Insulin, 'SkinThickness':median_SkinThickness})2. Библиотека Sklearn имеет класс SimpleImputer, который используется для восстановления пропущенных значений.
Используется следующий синтаксис
SimpleImputer(missingValues, strategy, fill_value)missingValues – здесь мы можем установить разные кодировки пропущенных значений, например, такие как np.nan или pd.NA.
strategy: это данные, которые заменят отсутствующие значения из набора данных, по умолчанию метод значения для этого параметра – среднее. Также можно использовать следующие стратегии: среднее, медиана, наиболее часто встречающееся, константа.
fill_value – константное значение, которое заменит пропущенные значения
Для замены пропущенных значений используется метод fit_transform.
Подробнее про класс SimpleImputer можно посмотреть здесь.
Продемонстрируем работу с данным классом
from sklearn.impute import SimpleImputer #импортируем библиотеку
myImputer = SimpleImputer (strategy= 'mean') #определяем импортер для обработки отсутствующих значений, используется стратегия замены средним значением
myImputer = SimpleImputer (strategy= 'median') # используется стратегия замены медианным значением
myImputer = SimpleImputer (strategy= 'most_frequent') используется стратегия замены наиболее часто встречающимся значением
#используем метод fit_transform для замены пропущенных значений
new_data.Insulin = myImputer.fit_transform(new_data['Insulin'].values.reshape(-1,1))Если необходимо заменить категориальные пропущенные значения, тогда можно использовать стратегию «самое частое значение» или «константа». Только одно замечание: все категориальные признаки должны быть переведены в числовые.
myImputer = SimpleImputer (strategy= 'constant', fill_value='1')
new_data.Outcome = myImputer.fit_transform (new_data['Outcome'].values.reshape(-1,1))Для замены значений в нескольких столбцах в датасете средним/медианой/конcтантой можно использовать следующую команду
new_data[['Insulin','SkinThickness']]=myImputer.fit_transform(new_data[['Insulin','SkinThickness']])3. Класс IterativeImputer библиотеки sklearn реализует многомерные алгоритмы восстановления пропущенных значений, оценивая другие значения в наборе. Данный класс моделирует каждый признак пропущенного значения как функцию от других признаков и использует оценку для замены значений. IterativeImputer фактически итеративно строит модель регрессии, используя подмножества столбцов для прогнозирования отсутствующих значений.
Подробнее про класс IterativeImputer можно посмотреть здесь.
Посмотрим на работу с этим классом
X = new_data.copy(deep = True) #создаем копию датасета
# импортируем библиотеку
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
# определяем импортер
myImputer=IterativeImputer()
# устанавливаем imputer на X
myImputer.fit(X)
#получаем восстановленные данные
myImputer_data = myImputer.transform(X)
# полученные данные преобразовываем в DataFrame
myImputer_data = pd.DataFrame(myImputer_data,columns = new_data.columns)4. Класс KNNImputer библиотеки sklearn обеспечивает восстановление пропущенных значений с использованием метода k – ближайщих соседей.
Подробнее про класс здесь.
# импортируем библиотеку
from sklearn.impute import KNNImputer
#определяем импортер
imputer=KNNImputer(n_neighbors=5, weigths=’uniform’)
#устанавливаем импортер на Х
imputer.fit(X)
# восстанавливаем данные
X1 = imputer.transform(X)
# полученные данные преобразовываем в DataFrame
myImputer_data = pd.DataFrame(X1,columns = new_data.columns)Каждый отсутствующий объект рассчитывается с использованием значений от n_neighbors ближайших соседей у которых есть значение для данного объекта. Характеристики соседей усредняются равномерно или взвешиваются по расстоянию до каждого соседа
Для определения параметра n_neighbors нет какого-то определенного способа, можно определить только экспериментальным путем. Низкое значение параметра может привести к эффекту недообучения модели, а слишком высокое влияет на производительность. Чаще всего наиболее предпочтительным является значение, равное 5.
Посмотрим на сравнительную таблицу методов SimpleImputer, IterativeImputer, KNNImputer

Теперь наши данные готовы к работе.
Приглашаем всех желающих на открытое занятие «Основы ООП в Python». На занятии научимся работать с классами, познакомимся с наследованием, узнаем про мутабельность экземпляров класса, передачу аргументов в инициализатор, наследование, переопределение методов, обращение к методам суперкласса. Регистрация доступна по ссылке.
