【问题标题】:How to output Pandas object from sklearn pipeline如何从 sklearn 管道输出 Pandas 对象
【发布时间】:2019-06-06 14:26:43
【问题描述】:

我构建了一个管道,该管道采用已拆分为分类和数字列的 pandas 数据框。我正在尝试在我的结果上运行 GridSearchCV,并最终查看 GridSearchCV 选择的最佳性能模型的重要性排名特征。我遇到的问题是 sklearn 管道输出 numpy 数组对象并在此过程中丢失任何列信息。因此,当我去检查模型中最重要的系数时,我留下了一个未标记的 numpy 数组。

我已阅读到构建自定义转换器可能是解决此问题的一种可能,但我自己没有任何经验。我也研究过利用 sklearn-pandas 包,但我犹豫是否尝试实现一些可能不会与 sklearn 并行更新的东西。任何人都可以建议他们认为解决此问题的最佳途径吗?我也对任何涉及 pandas 和 sklearn 管道应用的文献持开放态度。

我的管道:

# impute and standardize numeric data 
numeric_transformer = Pipeline([
    ('impute', SimpleImputer(missing_values=np.nan, strategy="mean")),
    ('scale', StandardScaler())
])

# impute and encode dummy variables for categorical data
categorical_transformer = Pipeline([
    ('impute', SimpleImputer(missing_values=np.nan, strategy="most_frequent")),
    ('one_hot', OneHotEncoder(sparse=False, handle_unknown='ignore'))
])

preprocessor = ColumnTransformer(transformers=[
    ('num', numeric_transformer, numeric_features),
    ('cat', categorical_transformer, categorical_features)
])

clf = Pipeline([
    ('transform', preprocessor),
    ('ridge', Ridge())
])

交叉验证:

kf = KFold(n_splits=4, shuffle=True, random_state=44)

cross_val_score(clf, X_train, y_train, cv=kf).mean()

网格搜索:

param_grid = {
    'ridge__alpha': [.001, .1, 1.0, 5, 10, 100]
}

gs = GridSearchCV(clf, param_grid, cv = kf)
gs.fit(X_train, y_train)

检查系数:

model = gs.best_estimator_
predictions = model.fit(X_train, y_train).predict(X_test)
model.named_steps['ridge'].coef_

这是在 seaborn "mpg" 数据集上执行时模型系数的输出:

array([-4.64782052e-01,  1.47805207e+00, -3.28948689e-01, -5.37033173e+00,
        2.80000700e-01,  2.71523808e+00,  6.29170887e-01,  9.51627968e-01,
       ...
       -1.50574860e+00,  1.88477450e+00,  4.57285471e+00, -6.90459868e-01,
        5.49416409e+00])

理想情况下,我想保留 pandas 数据框信息并在调用 OneHotEncoder 和其他方法后检索派生列名。

【问题讨论】:

  • this 可能有帮助
  • 难道不能直接把输入传给管道的'transform'层,然后从那里得到'ridge'层的输入(X_train_transf)对应的列名吗?当使用@ sklearn 中的 987654327@ 类,.coef 数组存储拟合模型的系数并保留顺序,因此如果您知道列名,您可以将它们映射到“未标记”数组:param_coef_df = pd.DataFrame({'feature':X_train_transf.columns, 'coefficient': model.named_steps['ridge'].coef_})param_coef_df = param_coef_df.sort_values(by='coefficient')
  • @JacoSolari 您介意将该评论转换为显示工作示例的答案吗?
  • @JacoSolari 我已经有一段时间没有重新审视这个问题了,但是在我自己的工作中,我已经在我的代码中实现了相同的逻辑,将转换后的系数名称组合到 pandas 数据帧中。我相信在处理管道时必须将每个单独的转换称为命名步骤仍然存在限制。如果 ColumnTransformer.get_feature_names 方法支持管道,那就太好了,但目前还不支持。
  • @lurscher 我添加了一个答案,如果它适合您的需要,请告诉我。

标签: python pandas scikit-learn


【解决方案1】:

我实际上会从输入中创建列名。如果您的输入已经分为数字和分类,您可以使用pd.get_dummies 来获取每个分类特征的不同类别的数量。

然后,您可以根据带有一些人工数据的问题为列创建正确的名称,如本工作示例的最后一部分所示。

from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import Ridge
from sklearn.model_selection import KFold, cross_val_score, GridSearchCV

# create aritificial data
numeric_features_vals = pd.DataFrame({'x1': [1, 2, 3, 4], 'x2': [0.15, 0.25, 0.5, 0.45]})
numeric_features = ['x1', 'x2']
categorical_features_vals = pd.DataFrame({'cat1': [0, 1, 1, 2], 'cat2': [2, 1, 5, 0] })
categorical_features = ['cat1', 'cat2']

X_train = pd.concat([numeric_features_vals, categorical_features_vals], axis=1)
X_test = pd.DataFrame({'x1':[2,3], 'x2':[0.2, 0.3], 'cat1':[0, 1], 'cat2':[2, 1]})
y_train = pd.DataFrame({'labels': [10, 20, 30, 40]})

# impute and standardize numeric data 
numeric_transformer = Pipeline([
    ('impute', SimpleImputer(missing_values=np.nan, strategy="mean")),
    ('scale', StandardScaler())
])

# impute and encode dummy variables for categorical data
categorical_transformer = Pipeline([
    ('impute', SimpleImputer(missing_values=np.nan, strategy="most_frequent")),
    ('one_hot', OneHotEncoder(sparse=False, handle_unknown='ignore'))
])

preprocessor = ColumnTransformer(transformers=[
    ('num', numeric_transformer, numeric_features),
    ('cat', categorical_transformer, categorical_features)
])

clf = Pipeline([
    ('transform', preprocessor),
    ('ridge', Ridge())
])


kf = KFold(n_splits=2, shuffle=True, random_state=44)
cross_val_score(clf, X_train, y_train, cv=kf).mean()

param_grid = {
    'ridge__alpha': [.001, .1, 1.0, 5, 10, 100]
}

gs = GridSearchCV(clf, param_grid, cv = kf)
gs.fit(X_train, y_train)

model = gs.best_estimator_
predictions = model.fit(X_train, y_train).predict(X_test)
print('coefficients : ',  model.named_steps['ridge'].coef_, '\n')

# create column names for categorical hot encoded data
columns_names_to_map = list(np.copy(numeric_features))
columns_names_to_map.extend('cat1_' + str(col) for col in pd.get_dummies(X_train['cat1']).columns)
columns_names_to_map.extend('cat2_' + str(col) for col in pd.get_dummies(X_train['cat2']).columns)

print('columns after preprocessing :', columns_names_to_map,  '\n')
print('#'*80)
print( '\n', 'dataframe of rescaled features with custom colum names: \n\n', pd.DataFrame({col:vals for vals, col in zip (preprocessor.fit_transform(X_train).T, columns_names_to_map)}))
print('#'*80)
print( '\n', 'dataframe of ridge coefficients with custom colum names: \n\n', pd.DataFrame({col:vals for vals, col in zip (model.named_steps['ridge'].coef_.T, columns_names_to_map)}))

上面的代码(最后)打印出以下数据帧,它是从参数名称到参数值的映射:

【讨论】:

  • 但是您如何检索SimpleImputer.fit_transform 可能已删除的任何列?或SimpleImputer.fit 将删除的列
  • 据我所知SimpleImputer 只删除包含only 缺失值的列(至少在strategy='constant' 时)。无论如何,如果出于任何原因在 Ridge 块之前删除了列,我的代码将抛出错误,因为 Ridge 模型系数的长度和 columns_names_to_map 中的列名会不同。
  • 或者,您可以绕过Imputer,直接使用df.fillna(a_value_you_compute) 来替换缺失值(在Pandas DataFrames 中大部分时间是np.nan)。
猜你喜欢
  • 2020-09-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-06
  • 2020-12-20
  • 2017-03-05
  • 1970-01-01
  • 2021-08-14
  • 2019-07-26
相关资源
最近更新 更多