我正在使用的数据集:
https://www.kaggle.com/shahir/protein-data-set
总结
我正在努力创建一个预处理管道,包含内置变换器和自定义变换器,其中一个变换器将添加额外的属性到数据中,并进一步对添加的属性进行变换。
额外属性的例子:
- 有一个phValue属性,其中有缺失数据。我希望尝试创建一个额外的属性,在phLabel列中将phValue标记为(酸性、中性、碱性)。
- 还有每个序列特征的字符串长度。
这将需要对phValue的缺失值进行填补,然后创建额外属性,并进一步变换序列长度属性。
我的糟糕变换器
这是一个我如何创建自定义变换器的例子,我可以用它进行手动预处理,但这不是创建完整预处理管道的正确方法。
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]
变换器中的Pandas操作
我想在变换器中执行的操作是特定于pandas的。我的解决方案是将输入的numpy数组转换为数据框,并在transform函数中将其作为numpy数组返回。我使用全局变量来存储属性和索引。我意识到这是一个缺乏深度的做法。我如何在自定义变换器中使用pandas操作?
我看到了这篇博客文章,但无法使用Column Transformer实现:https://zablo.net/blog/post/pandas-dataframe-in-scikit-learn-feature-union/
更新:
我的管道中的其他问题。当指定要变换的列时,后续的变换器如何工作?它是否将整个集合传递给每个变换器,操作指定的列,并将修改后的完整集合返回给其他变换器?此外,对于自定义变换器不指定列似乎会引发错误,尽管在我的情况下它们是非功能性的,因为我将参数传递给了构造函数。我应该如何修改我的代码?
如果我在fit_transform之后注释掉OrdinalEncoder和OneHotEncoder,ColumnTransformer输出一个形状为(rows, 72)的numpy数组。我的数据中有19个属性,我在FeatureSelector变换器中删除了2个属性。所以我期望在没有OHE的情况下收到一个形状为(rows, 17)的数组。
如果我保持原样,我会收到一个:ValueError: Input contains NaN.
attributes
是我数据集中每一列的全局数组。我在FeatureSelector中删除了我丢弃的值。
# numeric_feat_eng + categ_feat_eng 包含我所有的属性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 pdimport numpy as npfrom sklearn.base import TransformerMixinfrom sklearn.preprocessing import StandardScalerfrom sklearn.preprocessing import FunctionTransformerfrom sklearn.pipeline import make_pipelineclass 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): # 对X进行操作,并返回相同形状的数据框 # 如果前面的项目是numpy数组而不是数据框,这会变得很复杂 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 Xdf = pd.DataFrame({ 'my_str': [111, 2, 3333], 'val': [0, 1, 1]})# 混合使用这按预期工作my_pipeline = make_pipeline(StandardScaler(), CustomTransformer(column_names=["my_str", "val"]))my_pipeline.fit_transform(df)# 单独使用这也工作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_func0 -0.671543 -1.414214 19 -11 -0.742084 0.707107 18 12 1.413627 0.707107 17 1In [ ]: my_pipeline = make_pipeline(CustomTransformer(column_names=["my_str", "val"])) ...: my_pipeline.fit_transform(df) Out[ ]: my_str val str_len custom_func0 111 0 3 -11 2 1 1 12 3333 1 4 1
或者,如果你想直接将事物映射到数据框,你可以使用 sklearn-pandas
from sklearn_pandas import DataFrameMapper# 使用sklearn-pandasstr_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 val0 3 -11 2 12 1 1
使用sklearn pandas可以更具体地指定输入和输出为数据框,并允许你将每个列单独映射到每个感兴趣的管道,而不是将列名编码/硬编码为 TransformerMixin
对象的一部分。