Вопрос или проблема
Я пытался решить Серию игр Kaggle 2024 и столкнулся с некоторыми столбцами с пропущенными значениями. Для этого я попытался выполнить имитацию внутри конвейера. Но имитация не работает, я проверял это несколько раз, но не могу понять, почему это происходит.
Не могли бы вы помочь мне с этим?
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OrdinalEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split
data = pd.read_csv('/kaggle/input/playground-series-s4e8/train.csv')
data.drop([
'stem-root',
'veil-type',
'veil-color',
'spore-print-color',
], axis = 1, inplace = True) #более 80% null
length = data.shape[0]
acceptable_cols = [col for col in data.columns
if data[col].isnull().sum()/data.shape[0]*100 <= 1]
trainSet = data[acceptable_cols].copy().sample(frac = 0.1, random_state = 0)
X = trainSet.drop(['class', 'id'], axis = 1)
y = trainSet['class']
cat_cols = X.select_dtypes('object').columns
num_cols = list(set(X.columns) - set(cat_cols)) ###################Выбор числовых столбцов
transformations = ColumnTransformer(transformers = [
('oe', OrdinalEncoder(), cat_cols),
('impute & scale', Pipeline([
('impute', SimpleImputer()), ############################# Импутер
('scaler', StandardScaler())
]), num_cols)
])
pipeline = Pipeline([
('transformations', transformations),
('gbr', GradientBoostingClassifier(verbose = True))
])
X_train, X_val, y_train, y_val = train_test_split(X, y)
pipeline.fit(X_train, y_train) ###########################строка, где возникает ошибка
pipeline.score(X_val, y_val)
Всегда отображается ошибка:
Input X содержит NaN.
GradientBoostingClassifier не принимает пропущенные значения, закодированные как NaN. Для контролируемого обучения вы можете рассмотреть возможность использования sklearn.ensemble.HistGradientBoostingClassifier и Regressor, которые принимают пропущенные значения, закодированные как NaN. В противном случае возможно предварительно обработать данные, например, используя трансформер импутера в конвейере или удалив образцы с пропущенными значениями. См. https://scikit-learn.org/stable/modules/impute.html Вы можете найти список всех оценщиков, которые обрабатывают значения NaN, на следующей странице: https://scikit-learn.org/stable/modules/impute.html#estimators-that-handle-nan-values
Спасибо заранее за помощь.
Я думаю, что вы сделали это слишком сложным. Решение этой проблемы может заключаться в выполнении всего по отдельным шагам в конвейере:
ohe = ColumnTransformer([('категориальный', OrdinalEncoder(), cat_cols)], remainder="passthrough")
simple_imputer = SimpleImputer()
std_scaler = ColumnTransformer([('числовой', StandardScaler(), num_cols)], remainder="passthrough")
clf = GradientBoostingClassifier(verbose=True)
pipe = Pipeline(steps = [('encoder', ohe),
('imputer', simple_imputer),
('scaler', std_scaler),
('model', clf)])
Тем не менее, если вы хотите, чтобы это "совершенно" работало, вам нужно будет указать столбцы для кодировщика и маштабировщика по их индексам, а не по именам, как вы делаете это сейчас. Например,
std_scaler = ColumnTransformer([('числовой', StandardScaler(), [1, 7, 8])], remainder="passthrough")
где вы масштабируете столбцы с индексами 1, 7 и 8. Я не знаю конкретные индексы столбцов, которые вы хотите закодировать или масштабировать, поэтому вам просто нужно будет вставить их.
Ответ или решение
Проблема с потерянными значениями в конвейере sklearn может возникнуть по нескольким причинам, когда вы пытаетесь использовать имputation (восстановление) в процессе обработки данных. Давайте тщательно разберемся в вашем коде, чтобы понять, что именно могло пойти не так.
Основная проблема:
Ошибка, с которой вы столкнулись:
Input X contains NaN.
GradientBoostingClassifier does not accept missing values encoded as NaN natively...
указывает на то, что ваш конвейер не очищает данные от NaN-значений должным образом до передачи в классификатор.
Анализ вашего кода:
-
Создание преобразователя столбцов (ColumnTransformer):
В вашем коде вы используетеColumnTransformer
, чтобы применитьOrdinalEncoder
к категориальным данным, а затем применяетеSimpleImputer
иStandardScaler
в другой пайплайне для числовых данных. Однако вызовSimpleImputer
не осуществляется правильно, так как у вас есть небольшой синтаксический вакуум в некоторой логике. -
Не совсем корректное использование Pipeline:
Ваша текущая структура создаетPipeline
внутриColumnTransformer
, что может усложнить процесс. Также у вас есть ошибка с использованием&
, что, скорее всего, является результатом неаккуратного копирования кода из HTML. Вам следует использовать просто&
, если нужно.
Как исправить:
- Убедитесь, что применяете Импьютер перед передачей в классификатор. Импьюта должна выполняться для всех недостающих значений, прежде чем передавать данные для обучения модели.
- Измените ваш
ColumnTransformer
, чтобы использоватьSimpleImputer
правильно. Вместо использования вложенного пайплайна необходимо обработать численные и категориальные данные отдельно и затем соединить их.
Вот оптимизированная версия вашего кода:
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OrdinalEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split
# Загрузка и предварительная обработка данных
data = pd.read_csv('/kaggle/input/playground-series-s4e8/train.csv')
data.drop(columns=['stem-root', 'veil-type', 'veil-color', 'spore-print-color'], inplace=True)
acceptable_cols = [col for col in data.columns if data[col].isnull().sum()/data.shape[0]*100 <= 1]
trainSet = data[acceptable_cols].copy().sample(frac=0.1, random_state=0)
X = trainSet.drop(['class', 'id'], axis=1)
y = trainSet['class']
# Определение столбцов
cat_cols = X.select_dtypes('object').columns.tolist()
num_cols = [col for col in X.columns if col not in cat_cols]
# Создание конвейера
pipeline = Pipeline(steps=[
('preprocessing', ColumnTransformer(transformers=[
('cat', Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('encoder', OrdinalEncoder())
]), cat_cols),
('num', Pipeline(steps=[
('imputer', SimpleImputer(strategy='mean')),
('scaler', StandardScaler())
]), num_cols)
])),
('classifier', GradientBoostingClassifier(verbose=True))
])
# Разделение на тренировочные и валидационные данные
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=0)
# Обучение модели
pipeline.fit(X_train, y_train)
score = pipeline.score(X_val, y_val)
print(f'Accuracy: {score:.2f}')
Объяснение изменений:
- Структура пайплайна: Теперь у вас есть отдельный пайплайн для обработки категориальных и числовых данных в
ColumnTransformer
. - Импьютация:
SimpleImputer
теперь обрабатывает недостающие значения в отдельных пайплайнах для категориальных и числовых данных. - Метод обработки: Подобраны стратегии импьютации, что может быть полезно в зависимости от типа ваших данных. Например, для категориальных значений используется стратегия 'most_frequent', а для числовых - 'mean'.
Заключение:
С этими изменениями бизнес-логика процесса обработки данных должна работать должным образом, и вы сможете избежать ошибок, связанных с NaN значениями. Если у вас останутся вопросы или потребуется дополнительная помощь, не стесняйтесь задавать их!