我有一个简单的sklearn类,我想用作sklearn管道的一部分。这个类只需要一个pandas数据帧X_DF
和一个分类列名,并调用pd.get_dummies
来返回数据帧,其中列变成了一个伪变量矩阵。。。
import pandas as pd
from sklearn.base import TransformerMixin, BaseEstimator
class dummy_var_encoder(TransformerMixin, BaseEstimator):
'''Convert selected categorical column to (set of) dummy variables
'''
def __init__(self, column_to_dummy='default_col_name'):
self.column = column_to_dummy
print self.column
def fit(self, X_DF, y=None):
return self
def transform(self, X_DF):
''' Update X_DF to have set of dummy-variables instead of orig column'''
# convert self-attribute to local var for ease of stepping through function
column = self.column
# add columns for new dummy vars, and drop original categorical column
dummy_matrix = pd.get_dummies(X_DF[column], prefix=column)
new_DF = pd.concat([X_DF[column], dummy_matrix], axis=1)
return new_DF
现在使用这个转换器来适应/转换,我得到了预期的输出。对于以下一些玩具数据:
from sklearn import datasets
# Load toy data
iris = datasets.load_iris()
X = pd.DataFrame(iris.data, columns = iris.feature_names)
y = pd.Series(iris.target, name='y')
# Create Arbitrary categorical features
X['category_1'] = pd.cut(X['sepal length (cm)'],
bins=3,
labels=['small', 'medium', 'large'])
X['category_2'] = pd.cut(X['sepal width (cm)'],
bins=3,
labels=['small', 'medium', 'large'])
我的伪编码器产生正确的输出:
encoder = dummy_var_encoder(column_to_dummy = 'category_1')
encoder.fit(X)
encoder.transform(X).iloc[15:21,:]
category_1
category_1 category_1_small category_1_medium category_1_large
15 medium 0 1 0
16 small 1 0 0
17 small 1 0 0
18 medium 0 1 0
19 small 1 0 0
20 small 1 0 0
然而,当我从下面定义的sklearn管道调用相同的转换器时:
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import KFold, GridSearchCV
# Define Pipeline
clf = LogisticRegression(penalty='l1')
pipeline_steps = [('dummy_vars', dummy_var_encoder()),
('clf', clf)
]
pipeline = Pipeline(pipeline_steps)
# Define hyperparams try for dummy-encoder and classifier
# Fit 4 models - try dummying category_1 vs category_2, and using l1 vs l2 penalty in log-reg
param_grid = {'dummy_vars__column_to_dummy': ['category_1', 'category_2'],
'clf__penalty': ['l1', 'l2']
}
# Define full model search process
cv_model_search = GridSearchCV(pipeline,
param_grid,
scoring='accuracy',
cv = KFold(),
refit=True,
verbose = 3)
一切都很好,直到我安装好管道,在这一点上,我从伪编码器得到了一个错误:
cv_model_search.fit(X,y=y)
在[101]中:cv_model_search.fit(X,y=y)为4个中的每一个拟合3个折叠候选,共12个符合
无无无无[CV]dummy_vars_column_to_dummy=类别_1,clf_penalty=l1。。。。。。。。。
追踪(最近一次通话):
文件",第1行,在cv_model_search.fit(X,y=y)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/sklearn/model_selection/_sarch.py",638线,合适cv.分割(X,y,组))
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/sklearn/externals/joblib/pparallel.py",第779行,在调用时self.dispatch_one_batch(迭代器):
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/sklearn/externals/joblib/pparallel.py",第625行,在dispatch_one_batch中自我_调度(任务)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/sklearn/externals/joblib/pparallel.py",第588行,在_dispatch中job=自我_backend.apply_async(批处理,回调=cb)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/sklearn/externals/joblib/\parallel_backends.py",第111行,在apply_async中result=即时结果(func)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/sklearn/externals/joblib/\parallel_backends.py",第332行,在initself.results=batch()中
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/sklearn/externals/joblib/pparallel.py",第131行,在调用中返回[func(*args,**kwargs)for func,args,kwargs in self.items]
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/skmearl/model_selection/_validation.py",第437行,在_fit_and_score中估计器.fit(X_train,y_train,**fit_params)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/skmear/pipeline.py",线257,配合Xt,fit_params=自身_拟合(X,y,**fit_params)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/skmear/pipeline.py",第222行,in_fit**fit_params_steps[名称])
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/sklearn/externals/joblib/memory.py",第362行,在调用中返回self.func(*args,**kwargs)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/skmear/pipeline.py",第589行,在_fit_transform_one中res=transformer.fit_transform(X,y,**fit_params)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/skmear/base.py",第521行,在fit_transform中return self.fit(X,y,**fit_params).transform(X)
文件",第21行,在转换中dummy_matrix=pd.get_dummies(X_DF[列],前缀=列)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/pandas/core/frame.py",第1964行,getitem返回self_getitem_column(key)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/pandas/core/frame.py",第1971行,在_getitem_column中回归自我_get_item_cache(密钥)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/pandas/core/generic.py",第1645行,在_get_item_cache中values=self_data.get(item)
文件"/home/max/anaconda3/envs/remine/lib/python2.7/site packages/pandas/core/internals.py",第3599行,在getraise ValueError("不能用空键标记索引")
ValueError:无法使用空键标记索引
跟踪会告诉您到底出了什么问题。学习诊断跟踪确实非常宝贵,尤其是当您从可能不完全了解的库中继承时。
现在,我自己已经在sklearn中进行了一些继承,我可以毫无疑问地告诉您,如果输入到fit
或fit_transform
方法的数据类型不是NumPy数组,那么GridSearchCV
会给您带来一些麻烦。正如Vivek在他的评论中提到的,传递给您的拟合方法的X不再是DataFrame。但让我们先来看看痕迹。
ValueError:不能用空键标记索引
虽然Vivek对NumPy数组的看法是正确的,但这里还有另一个问题。您得到的实际错误是拟合方法中column
的值为None。如果您查看上面的encoder
对象,您会看到__repr__
方法输出以下内容:
dummy_var_encoder(column_to_dummy=None)
使用Pipeline
时,此参数将被初始化并传递给GridSearchCV
。这种行为在整个交叉验证和搜索方法中都可以看到,输入参数中具有不同名称的属性会导致类似的问题。解决这个问题会让你走上正确的道路。
修改__init__
方法将解决这个特定问题:
def __init__(self, column='default_col_name'):
self.column = column
print(self.column)
然而,一旦你做到了这一点,Vivek提到的问题就会引起人们的注意,你将不得不处理这个问题。这是我以前遇到过的事情,尽管不是专门针对DataFrames的。我在使用sklearnGridSearchCV
中提出了一个关于自定义类的解决方案,该类的fit方法需要3个参数。基本上,我创建了一个实现__getitem__
方法的包装器,使数据的外观和行为能够通过GridSearchCV
、Pipeline
和其他交叉验证方法中使用的验证方法。
编辑
我做了这些更改,看起来您的问题来自验证方法check_array
。虽然用dtype=pd.DataFrame
调用此方法可以工作,但线性模型用dtype=np.float64
调用此方法会抛出错误。要绕过这一点,而不是将原始数据与虚设数据连接起来,您只需返回虚设列并使用它们进行拟合即可。无论如何都应该这样做,因为您不希望在试图拟合的模型中同时包含伪列和原始数据。你也可以考虑drop_first
选项,但我偏离主题了。因此,像这样更改fit
方法可以使整个过程按预期工作。
def transform(self, X_DF):
''' Update X_DF to have set of dummy-variables instead of orig column'''
# convert self-attribute to local var for ease of stepping through function
column = self.column
# add columns for new dummy vars, and drop original categorical column
dummy_matrix = pd.get_dummies(X_DF[column], prefix=column)
return dummy_matrix