如何创建一个预处理管道,包括内置的scikit学习转换器、自定义转换器,其中一个用于功能工程



我正在使用此数据集:

https://www.kaggle.com/shahir/protein-data-set

摘要

我正在努力创建一个带有内置转换器和自定义转换器的预处理管道,其中包括一个将向数据添加额外属性并对添加的属性进一步执行转换的管道。

附加属性示例:

  • 有一个phValue属性缺少数据。我想尝试创建一个附加属性,在phLabel列中将phValue标记为(Acid、Neutral、Base(
  • 还有每个序列特征的字符串长度

这将需要对phValue的缺失值进行脉冲处理,然后创建额外的属性和进一步的转换器,这些转换器也将转换sequence_length属性。

我糟糕的变压器

这是我如何创建自定义转换器的一个例子,我可以使用它进行手动预处理,但是,在创建完整的预处理管道时,这不是正确的方法。

def data_to_frame(X):
if isinstance(X, pd.DataFrame):
return X
elif isinstance(X, sparse.csr_matrix):
return pd.DataFrame(X, indices, atributes)
elif isinstance(X, np.ndarray):
return pd.DataFrame(X, indices, atributes)
else:
raise Exception("Incorrect Data Structure Passed")
class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
def __init__(self, no_difference = True): # no *args or **kargs
self.no_difference = no_difference
def fit(self, X, y=None):
return self # nothing else to do
def transform(self, X):
atributes.extend(['sequence_length', 'difference', 'phLabel'])
sequence_length = X.sequence.str.len()
difference = X['residueCount'] - sequence_length
phLabel = X['phValue'].apply(ph_labels)
if self.no_difference:
atributes.append('no_difference')
no_difference = (difference == 0)
return np.c_[X, sequence_length, difference, phLabel, no_difference]
else:
return np.c_[X, sequence_length, difference, phLabel]

变压器中的熊猫操作

我想在变形金刚中执行的操作是熊猫特有的。我的解决方案是将输入的numpy数组转换为数据帧,并在转换函数中将其作为numpy数组返回。我使用全局变量作为属性和索引。我意识到这是一种乏善可陈的做法。如何在我的自定义变压器中使用熊猫操作?

我看到了这篇博客文章,但我无法用Column Transformer做到这一点:https://zablo.net/blog/post/pandas-dataframe-in-scikit-learn-feature-union/

更新:

我的管道的其他问题。指定要转换的列时,后续转换器如何工作?它是否将整套变压器传递给每个变压器,在指定的列上运行,并将修改后的整套变压器返回给其他变压器?此外,当我将参数传递给构造函数时,不为自定义转换器指定列似乎会引发错误,即使它们在我的情况下不起作用。我应该如何更改代码?

如果我在fit_transform之后注释掉OrdinalEncoder和OneHotEncoder,ColumnTransformer将输出一个numpy数组,其形状为:(rows,72(有19个属性,我在FeatureSelector transformer中删除了2个属性。因此,我希望收到一个没有OHE的(行,17(数组。

如果我保持原样,我会收到一个:ValueError: Input contains NaN.

attributes是我的数据集中每一列的全局数组。我删除了FeatureSelector中删除的值。

# numeric_feat_eng + categ_feat_eng contains all of my attributes
prepoc_pipeline = make_column_transformer(
(SimpleImputer(strategy='mean'), numeric_feat_eng),
(SimpleImputer(strategy='most_frequent'), categ_feat_eng),
(FixAtributeValues(), attributes),
(CombinedAttributesAdder(), attributes),
(FeatureSelector(attributes_to_drop), attributes_to_drop),
(LogTransformation(atr_log_trans), atr_log_trans),
(StandardScaler(), numeric_feat_eng),
(OrdinalEncoder(), id_cols),
(OneHotEncoder(handle_unknown='ignore'), categ_without_ids)
)
class FeatureSelector(BaseEstimator, TransformerMixin):
def __init__(self, attributes_drop = ['pdbxDetails', 'sequence']):
self.attributes_drop = attributes_drop
def fit(self, X, y=None):
return self # nothing else to do
def transform(self, X):
X = data_to_frame(X)
for x in self.attributes_drop:
attributes.remove(x)
X = X.drop(columns=self.attributes_drop)
return X

如果有人能指导我如何做到这一点,我将不胜感激!或者为我提供可以学习如何创建管道的资源。

这应该如预期的那样工作——很可能是您的实现出现了问题——可以尝试处理一个伪数据集。TransformerMixin并不真正关心输入是numpy还是pandas.DataFrame;意图";。

import pandas as pd
import numpy as np
from sklearn.base import TransformerMixin
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import FunctionTransformer
from sklearn.pipeline import make_pipeline

class CustomTransformer(TransformerMixin):
def __init__(self, some_stuff=None, column_names= []):
self.some_stuff = some_stuff
self.column_names = column_names
def fit(self, X, y=None):
return self
def transform(self, X):
# do stuff on X, and return dataframe
# of the same shape - this gets messy
# if the preceding item is a numpy array
# and not a dataframe
if isinstance(X, np.ndarray):
X = pd.DataFrame(X, columns=self.column_names)

X['str_len'] = X['my_str'].apply(lambda x: str(x)).str.len()
X['custom_func'] = X['val'].apply(lambda x: 1 if x > 0.5 else -1)
return X

df = pd.DataFrame({
'my_str': [111, 2, 3333],
'val': [0, 1, 1]
})
# mixing this works as expected
my_pipeline = make_pipeline(StandardScaler(), CustomTransformer(column_names=["my_str", "val"]))
my_pipeline.fit_transform(df)
# using this by itself works as well
my_pipeline = make_pipeline(CustomTransformer(column_names=["my_str", "val"]))
my_pipeline.fit_transform(df)

输出为:

In [  ]: my_pipeline = make_pipeline(StandardScaler(), CustomTransformer(column_names=["my_str", "val"])) 
...: my_pipeline.fit_transform(df)                                                                                                                                                                                                  
Out[  ]: 
my_str       val  str_len  custom_func
0 -0.671543 -1.414214       19           -1
1 -0.742084  0.707107       18            1
2  1.413627  0.707107       17            1
In [  ]: my_pipeline = make_pipeline(CustomTransformer(column_names=["my_str", "val"])) 
...: my_pipeline.fit_transform(df)                                                                                                                                                                                                  
Out[  ]: 
my_str  val  str_len  custom_func
0     111    0        3           -1
1       2    1        1            1
2    3333    1        4            1

或者,如果您想将事物直接映射到数据帧,则可以使用sklearn-pandas

from sklearn_pandas import DataFrameMapper
# using sklearn-pandas
str_transformer = FunctionTransformer(lambda x: x.apply(lambda y: y.str.len()))
cust_transformer = FunctionTransformer(lambda x: (x > 0.5) *2 -1)

mapper = DataFrameMapper([
(['my_str'], str_transformer),
(['val'], make_pipeline(StandardScaler(), cust_transformer))
], input_df=True, df_out=True)
mapper.fit_transform(df)

输出:

In [  ]: mapper.fit_transform(df)                                                                                                                                                                                                       
Out[47]: 
my_str  val
0       3   -1
1       2    1
2       1    1

使用sklearn panda可以更具体地将输入作为数据帧,将输出作为数据帧;还可以将每个列单独映射到每个感兴趣的管道,而不是将列名编码/硬编码为TransformerMixin对象的一部分。

相关内容

最新更新