[Перевод] NoNa: Алгоритм заполнения отсутствующих данных
Мой первый open-source продукт.
GitHub — AbdualimovTP/nona: библиотека для заполнения пропущенных значений с использованием методов искусственного интеллекта
pip install nona
В реальных наборах данных пропущенные значения создают проблему для дальнейшей обработки. Большую ценность имеет подстановка или заполнение отсутствующих значений. К сожалению, стандартные «ленивые» методы, такие как простое использование медианы столбца или среднего значения, не всегда работают должным образом.
В 2021-ом году ко мне пришла идея создания алгоритма на основе методов машинного обучения с прогнозированием по каждому столбцу с пропусками. Данную идею я воплотил сначала схематично на бумаге.
Изображение автора
Суть алгоритма заключается в заполнении пропусков различными методами машинного обучения. Циклом проходим по всем столбцам, если столбец с пропущенными значениями — останавливаемся и делаем этот столбец — target. Предыдущие столбцы делим на X_train, X_test. X_test будет соответствовать пропущенным значениям. Значения Y_train берем из столбца (на котором остановились в цикле) в которых нет пропусков. Обучаем X_train и y_train на выбранном нами методе, например гребневой регрессии (Ridge regression). Делаем предсказания на X_test, заполняем пропущенные значения в столбце предсказанными значениями. Так делаем по всем столбцам с пропусками.
Данный алгоритм я регулярно использовал на практике для заполнения пропущенных значений.
В 2023-м году решил на его основе написать библиотеку для Python и провести сравнение с иными методами заполнения пропусков.
Преимущества метода:
Простое и быстрое заполнение пропущенных значений.
Кастомизация используемых методов машинного обучения.
Высокая точность прогнозирования.
Установка
Исходный код в настоящее время размещен на GitHub по адресу: GitHub — AbdualimovTP/nona: библиотека для заполнения пропущенных значений с использованием методов искусственного интеллекта
Двоичные установщики для последней выпущенной версии доступны в каталоге пакетов Python (PyPI)
# PyPI
pip install nona
Зависимости
Быстрый старт
» Из коробки» используем гребневую регрессию, чтобы заполнить пробелы в столбцах где необходимо решить задачу регрессии, и RandomForestClassifier для задачи классификации.
# Загружаем библиотеку
from nona.nona import nona
# Подготовьте ваш датасет, только численные значения в датасете
# Заполняем пропущенные значения
nona(YOUR_DATA)
Повышение точности алгоритма
Вы можете вставить в функцию иные методы машинного обучения. Они должны поддерживать простую реализацию fit и predict.
Параметры алгоритма:
data: подготовленный набор данных
algreg: Алгоритм регрессии для прогнозирования отсутствующих значений в столбцах
algclass: Алгоритм классификации для прогнозирования пропущенных значений в столбцах
# Загружаем библиотеку
from nona.nona import nona
# Подготовьте ваш датасет, только численные значения в датасете
# Заполняем пропущенные значения
nona(data=YOUR_DATA, algreg=make_pipeline(StandardScaler(with_mean=False), Ridge(alpha=0.1)), algclass=RandomForestClassifier(max_depth=2, random_state=0))
Сравнение с иными методами заполнения пропусков
Сравниваемые методы:
Baseline — заполнение средним по столбцу.
KNN — заполнение пропущенных значений с использованием k-ближайших соседей. Отсутствующие значения каждой выборки заполняются с использованием среднего значения ближайших соседей n_neighbors, найденных в обучающем наборе.
MICE — Использование класса IterativeImputer sklearn, который моделирует каждую функцию с отсутствующими значениями как функцию других функций и использует эту оценку для заполнения. Он делает это в итерированном циклическом режиме: на каждом шаге столбец признаков обозначается как выход y, а другие столбцы признаков обрабатываются как входы X. Регрессор подходит для (X, y) для известного y. Затем регрессор используется для предсказания пропущенных значений y. Это делается для каждого признака итеративно, а затем повторяется для раундов заполнения max_iter. Возвращаются результаты финального раунда заполнения.
MissForest — алгоритм заполнения данных на основе машинного обучения, который работает на основе алгоритма Random Forest.
NoNA — мой алгоритм «постолбцового» заполнения пропусков при помощи различных методов машинного обучения.
Для работы я взял Framingham heart study dataset доступный на Kaggle.
На данном датасете мы будем моделировать пропуски в объёме 10%, 20%, 30%, 40%, 50%, 70%, 90% пропущенных значений. Заполнять их описанными методами (каждым по отдельности). И сравнивать результаты с истинными значениями. На выходе получим среднеквадратичную ошибку (RMSE).
for i in [0.1, 0.2, 0.3, 0.4, 0.5, 0.7, 0.9]:
# we create a random matrix the size of a dataset, randomly fill it with gaps and zeros
randomMatrixNA = np.random.choice([0,np.NaN], (data.shape[0], data.shape[1]), p=[1-i, i])
# fill dataset with missing values
dataWithNA = data + randomMatrixNA
# create datasets with filled gaps
# fill in the middle
Baseline_1_mean = dataWithNA.fillna(dataWithNA.mean())
print(f'Baseline_MEAN, {i*100}, RMSE:' , np.round(mean_squared_error(data, Baseline_1_mean, squared=False), 2))
dataFrameRMSE.loc['Baseline_MEAN'][f'{int(i*100)}%'] = np.round(mean_squared_error(data, Baseline_1_mean, squared=False), 2)
# KNN
imputer = KNNImputer(n_neighbors=15)
KNN = imputer.fit_transform(dataWithNA)
dataFrameRMSE.loc['KNN'][f'{int(i*100)}%'] = np.round(mean_squared_error(data, KNN, squared=False), 2)
print(f'KNN, {i*100}, RMSE:' , np.round(mean_squared_error(data, KNN, squared=False), 2))
# MICE
mice = IterativeImputer(max_iter=10, random_state=0)
MICE = mice.fit_transform(dataWithNA)
dataFrameRMSE.loc['MICE'][f'{int(i*100)}%'] = np.round(mean_squared_error(data, MICE, squared=False), 2)
print(f'MICE, {i*100}, RMSE:' , np.round(mean_squared_error(data, MICE, squared=False), 2))
# MISSFOREST
missforest = MissForest(random_state=0, verbose=0)
MISSFOREST = missforest.fit_transform(dataWithNA)
dataFrameRMSE.loc['MISSFOREST'][f'{int(i*100)}%'] = np.round(mean_squared_error(data, MISSFOREST, squared=False), 2)
print(f'MISSFOREST, {i*100}, RMSE:' , np.round(mean_squared_error(data, MISSFOREST, squared=False), 2))
# nona_Base
dataWithNA_NonaBase = dataWithNA.copy(deep=True)
nona(dataWithNA_NonaBase)
dataFrameRMSE.loc['NONA'][f'{int(i*100)}%'] = np.round(mean_squared_error(data, dataWithNA_NonaBase, squared=False), 2)
print(f'NONA, {i*100}, RMSE:' , np.round(mean_squared_error(data, dataWithNA_NonaBase, squared=False), 2))
Результаты
10% | 20% | 30% | 40% | 50% | 70% | 90% | |
Baseline — MEAN | 2.67 | 3.8 | 4.7 | 5.66 | 6.4 | 7.4 | 8.43 |
KNN | 2.48 | 3.7 | 4.57 | 5.55 | 6.35 | 7.47 | 8.49 |
MICE | 2.12 | 3.17 | 4.59 | 5.41 | 5.94 | 7.33 | 8.61 |
MISSFOREST | 2.26 | 3.36 | 4.31 | 5.33 | 6.15 | 8.06 | 9.85 |
NONA | 2.24 | 3.35 | 4.28 | 5.16 | 5.83 | 7.12 | 8.43 |
Неплохие результаты для работы алгоритма «из коробки».
На 30%, 40%, 50%, 70%, 90% алгоритм NONA показал лучшую RMSE на данном датасете со смоделированными пропусками. На 10%, 20% второе место, первое за MICE.
В дальнейшем планирую проверить точность заполнения на иных датасетах. Также вижу возможности для улучшения качествf прогнозирования. Планирую реализовать в следующих версиях библиотеки.