У меня есть набор данных, содержащий некоторые функции с довольно большим количеством NaN (до 80%). Удаление их, исказило бы мой общий дистрибутив, поэтому мои варианты - установить все NaN на -1 / - 99 или вывести мою непрерывную переменную в группы, что делает ее категориальной.
Поскольку у меня уже есть много категориальных функций, я бы предпочел не делать несколько непрерывных, категоричных тоже. Однако, если я установил NaN на -1 / - 99, это существенно повлияет на результаты, когда я масштабирую эти функции?
Или с другой точки зрения, есть ли способ масштабирования функций, если -1 слишком сильно влияет на его масштабирование?
Я знаю, что вы получили ответ из комментариев выше, но, пытаясь показать новым пользователям scikit-learn, как вы могли бы подойти к такой проблеме, я собрал очень рудиментарное решение, демонстрирующее, как создать настраиваемый трансформатор, который будет справляться с этим:
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.utils.validation import check_array, check_is_fitted
import numpy as np
class NanImputeScaler(BaseEstimator, TransformerMixin):
"""Scale an array with missing values, then impute them
with a dummy value. This prevents the imputed value from impacting
the mean/standard deviation computation during scaling.
Parameters
----------
with_mean : bool, optional (default=True)
Whether to center the variables.
with_std : bool, optional (default=True)
Whether to divide by the standard deviation.
nan_level : int or float, optional (default=-99.)
The value to impute over NaN values after scaling the other features.
"""
def __init__(self, with_mean=True, with_std=True, nan_level=-99.):
self.with_mean = with_mean
self.with_std = with_std
self.nan_level = nan_level
def fit(self, X, y=None):
# Check the input array, but don't force everything to be finite.
# This also ensures the array is 2D
X = check_array(X, force_all_finite=False, ensure_2d=True)
# compute the statistics on the data irrespective of NaN values
self.means_ = np.nanmean(X, axis=0)
self.std_ = np.nanstd(X, axis=0)
return self
def transform(self, X):
# Check that we have already fit this transformer
check_is_fitted(self, "means_")
# get a copy of X so we can change it in place
X = check_array(X, force_all_finite=False, ensure_2d=True)
# center if needed
if self.with_mean:
X -= self.means_
# scale if needed
if self.with_std:
X /= self.std_
# now fill in the missing values
X[np.isnan(X)] = self.nan_level
return X
Способ, которым это работает, заключается в вычислении nanmean
и nanstd
в секции fit
так что значения NaN игнорируются при вычислении статистики. Затем, в секции transform
, после того, как переменные масштабируются и центрируются, оставшиеся значения NaN вменяются значением, которое вы назначили (вы упоминали -99, так что то, что я сделал по умолчанию). Вы всегда можете сломать этот компонент трансформатора в другой трансформатор, но я включил его только для демонстрационных целей.
Здесь мы установим некоторые данные с присутствием NaN:
nan = np.nan
data = np.array([
[ 1., nan, 3.],
[ 2., 3., nan],
[nan, 4., 5.],
[ 4., 5., 6.]
])
И когда мы подходим к скалеру и изучаем средние/стандартные отклонения, вы можете видеть, что они не учитывали значения NaN:
>>> imputer = NanImputeScaler().fit(data)
>>> imputer.means_
array([ 2.33333333, 4. , 4.66666667])
>>> imputer.std_
array([ 1.24721913, 0.81649658, 1.24721913])
Наконец, когда мы преобразуем данные, данные масштабируются и обрабатываются значения NaN:
>>> imputer.transform(data)
array([[ -1.06904497, -99. , -1.33630621],
[ -0.26726124, -1.22474487, -99. ],
[-99. , 0. , 0.26726124],
[ 1.33630621, 1.22474487, 1.06904497]])
Вы даже можете использовать этот шаблон внутри конвейера scikit-learn (и даже сохраняете его на диске):
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
pipe = Pipeline([
("scale", NanImputeScaler()),
("clf", LogisticRegression())
]).fit(data, y)