Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/289.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在sklearn管道中实现分类变量的KNN插补_Python_Encoding_Scikit Learn_Pipeline_Imputation - Fatal编程技术网

Python 在sklearn管道中实现分类变量的KNN插补

Python 在sklearn管道中实现分类变量的KNN插补,python,encoding,scikit-learn,pipeline,imputation,Python,Encoding,Scikit Learn,Pipeline,Imputation,我正在使用sklearn的管道转换器实现一个预处理管道。我的管道包括sklearn的KNIMPUTER估计器,我想用它来估算数据集中的分类特征。(我的问题类似于此线程,但它不包含我问题的答案:) 我知道在插补之前必须对分类特征进行编码,这就是我遇到的问题。使用标准的label/ordinal/onehot编码器,当尝试用缺少的值(np.nan)对分类特征进行编码时,会出现以下错误: ValueError: Input contains NaN 我通过创建一个自定义编码器,将np.nan替换为“

我正在使用sklearn的管道转换器实现一个预处理管道。我的管道包括sklearn的KNIMPUTER估计器,我想用它来估算数据集中的分类特征。(我的问题类似于此线程,但它不包含我问题的答案:)

我知道在插补之前必须对分类特征进行编码,这就是我遇到的问题。使用标准的label/ordinal/onehot编码器,当尝试用缺少的值(np.nan)对分类特征进行编码时,会出现以下错误:

ValueError: Input contains NaN
我通过创建一个自定义编码器,将np.nan替换为“Missing”,成功地“绕过”了它:

class CustomEncoder(BaseEstimator, TransformerMixin):
    def __init__(self):
        self.encoder = None

    def fit(self, X, y=None):
        self.encoder = OrdinalEncoder()
        return self.encoder.fit(X.fillna('Missing'))

    def transform(self, X, y=None):
        return self.encoder.transform(X.fillna('Missing'))

    def fit_transform(self, X, y=None, **fit_params):
        self.encoder = OrdinalEncoder()
        return self.encoder.fit_transform(X.fillna('Missing'))

preprocessor = ColumnTransformer([
    ('categoricals', CustomEncoder(), cat_features),
    ('numericals', StandardScaler(), num_features)],
    remainder='passthrough'
)

pipeline = Pipeline([
    ('preprocessing', preprocessor),
    ('imputing', KNNImputer(n_neighbors=5))
])
然而,在这种情况下,我无法找到一种合理的方法,在使用KNI计算机输入之前,将编码的“缺失”值设置回np.nan

我已经读到,我可以在这个线程上使用OneHotEncoder transformer手动完成这项工作:,但是,我想再次在管道中实现所有这些,以自动化整个预处理阶段

有人做到了吗?有人会推荐替代方案吗?用KNN算法插补可能不值得费心,我应该用一个简单的插补器来代替吗


提前感谢您的反馈

恐怕这行不通。如果对分类数据进行热编码,丢失的值将被编码到新的二进制变量中,KNIMPUTER将无法处理它们,因为:

  • 它一次作用于每一列,而不是一个热编码列的完整集合
  • 不会再有任何失踪的人需要处理了
无论如何,您可以使用scikit learn输入缺失的分类变量:

  • 您可以使用using
    strategy=“most_frequency”
    :这将使用每列中最频繁的值替换缺少的值,无论它们是字符串还是数字数据
  • 使用时有一定限制:您必须首先将分类特征转换为数字特征,同时保留
    NaN
    值(请参见:),然后您可以使用
    KNIMPUTER
    仅使用最近邻作为替换(如果您使用多个相邻,则会呈现一些无意义的平均值)。例如:
  • 为混合数据使用并复制一个数字(但必须分别处理数字和分类特征)。例如:

  • 对于任何感兴趣的人,我成功地实现了一个自定义标签编码器,该编码器忽略np.nan并与sklearn管道转换器兼容,类似于Luca Massaron在github repo上实现的LEncoder:


    顺便说一句,如果您希望将所有这些实现到Scikit学习管道中,您可以看看我的表格数据深度学习管道:我认为LEncoder类就是您所寻找的:-)感谢您的回复和链接Luca。是的,我想用普通编码器实现上面提到的解决方案2)。我的想法是,KNN插补会比SimpleImpute插补给我更好的结果,但我不确定如何真正评估。还有第三种方法,基于Scikit learn中的实验函数:迭代插补器,它可以复制MissForest(参见:),一种能够处理数字和分类缺失值的方法。我对答案进行了编辑。MissForest方法不仅能够处理混合型变量,而且在插补方面更可靠,无论是在随机缺失(MAR)和非随机缺失(MNAR)的情况下,这通常是使用准实验数据的更常见的情况。非常有趣。我认为我确实应该探索解决方案3)。老实说,我没听说过,这不是一种常见的插补策略吗?这种方法有缺点吗?我想它会慢一些。作为第二个链接线程的后续操作,这里有一个可用于管道的变压器
        import numpy as np
        import pandas as pd
        from sklearn.preprocessing import LabelEncoder
        from sklearn.impute import KNNImputer
        
        df = pd.DataFrame({'A': ['x', np.NaN, 'z'], 'B': [1, 6, 9], 'C': [2, 1, np.NaN]})
        
        df = df.apply(lambda series: pd.Series(
            LabelEncoder().fit_transform(series[series.notnull()]),
            index=series[series.notnull()].index
        ))
        
        imputer = KNNImputer(n_neighbors=1)
        imputer.fit_transform(df)
        
        In:
            A   B   C
        0   x   1   2.0
        1   NaN 6   1.0
        2   z   9   NaN
        
        Out:
        array([[0., 0., 1.],
               [0., 1., 0.],
               [1., 2., 0.]])
    
        import numpy as np
        import pandas as pd
        from sklearn.preprocessing import LabelEncoder
        from sklearn.experimental import enable_iterative_imputer
        from sklearn.impute import IterativeImputer
        from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
        
        df = pd.DataFrame({'A': ['x', np.NaN, 'z'], 'B': [1, 6, 9], 'C': [2, 1, np.NaN]})
        
        categorical = ['A']
        numerical = ['B', 'C']
        
        df[categorical] = df[categorical].apply(lambda series: pd.Series(
            LabelEncoder().fit_transform(series[series.notnull()]),
            index=series[series.notnull()].index
        ))
        
        print(df)
        
        imp_num = IterativeImputer(estimator=RandomForestRegressor(),
                                   initial_strategy='mean',
                                   max_iter=10, random_state=0)
        imp_cat = IterativeImputer(estimator=RandomForestClassifier(), 
                                   initial_strategy='most_frequent',
                                   max_iter=10, random_state=0)
        
        df[numerical] = imp_num.fit_transform(df[numerical])
        df[categorical] = imp_cat.fit_transform(df[categorical])
        
        print(df)
    
    class CustomEncoder(BaseEstimator, TransformerMixin):
        def __init__(self):
            self.encoders = dict()
    
        def fit(self, X, y=None):
            for col in X.columns:
                le = LabelEncoder()
                le.fit(X.loc[X[col].notna(), col])
                le_dict = dict(zip(le.classes_, le.transform(le.classes_)))
    
                # Set unknown to new value so transform on test set handles unknown values
                max_value = max(le_dict.values())
                le_dict['_unk'] = max_value + 1
    
                self.encoders[col] = le_dict
            return self
    
        def transform(self, X, y=None):
            for col in X.columns:
                le_dict = self.encoders[col]
                X.loc[X[col].notna(), col] = X.loc[X[col].notna(), col].apply(
                    lambda x: le_dict.get(x, le_dict['_unk'])).values
            return X
    
        def fit_transform(self, X, y=None, **fit_params):
            self.fit(X, y)
            return self.transform(X, y)