如何在 sk-learn 管道中使用我自己的自定义函数?

Posted

技术标签:

【中文标题】如何在 sk-learn 管道中使用我自己的自定义函数?【英文标题】:How can I use my own custom function in an sk-learn pipeline? 【发布时间】:2021-08-13 01:30:50 【问题描述】:

我是 sk-learn 管道的新手,想使用我自己的离散分箱形式。我需要根据与原始列关联的另一列的累积总和对一列值进行分类。我有一个工作函数:

def dynamic_bin(df, column, weight, minimum):
    """
    

    Parameters
    ----------
    df : dataframe
    column : column to be binned
    weight : column that will dictate the bin
    minimum : minimum weight per bin

    Returns
    -------
    df : dataframe with new binned column

    """
    bins = [-np.inf]
    labels = [] 
    hold_over = []
    for i in sorted(df[column].unique()):
        g = df[df[column] == i].groupby(column).agg(weight:'sum').reset_index()
        
        if g[weight].values[0] < minimum:
            if hold_over is None:
                hold_over.append(g[weight].values[0])
                
            elif (sum(hold_over) + g[weight].values[0]) < minimum:
                hold_over.append(g[weight].values[0])
 
                
            elif (sum(hold_over) + g[weight].values[0]) >= minimum:
                hold_over.clear()
                bins.append(g[column].values[0])
                labels.append(g[column].values[0])
                
            
        elif g[weight].values[0] >= minimum:
            bins.append(g[column].values[0])
            labels.append(g[column].values[0])
    
    bins.pop()
    bins.append(np.inf)
    
    
    str_column = str(column)+str("_binned")
    # print(str_column)
    df[str_column] = pd.cut(df[column],
                            bins = bins,
                            labels = labels)
    

    return df

这就是我试图使它成为一个类的方式。

from sklearn.base import  BaseEstimator, TransformerMixin

class dynamic_bin(BaseEstimator, TransformerMixin):
    def __init__(self, weight, minimum):
        self.weight = weight
        self.minimum = minimum
    def fit(self, X, y=None):
        return self
    def tranform(self, X):
        """
    

        Parameters
        ----------
        df : dataframe
        column : column to be binned
        weight : column that will dictate the bin
        minimum : minimum weight per bin
    
        Returns
        -------
        df : dataframe with new binned column
    
        """
        bins = [-np.inf]
        labels = [] 
        hold_over = []
        for i in sorted(df[column].unique()):
            g = df[df[column] == i].groupby(column).agg(weight:'sum').reset_index()
            
            if g[weight].values[0] < minimum:
                if hold_over is None:
                    hold_over.append(g[weight].values[0])
                    
                elif (sum(hold_over) + g[weight].values[0]) < minimum:
                    hold_over.append(g[weight].values[0])
     
                    
                elif (sum(hold_over) + g[weight].values[0]) >= minimum:
                    hold_over.clear()
                    bins.append(g[column].values[0])
                    labels.append(g[column].values[0])
                    
                
            elif g[weight].values[0] >= minimum:
                bins.append(g[column].values[0])
                labels.append(g[column].values[0])
        
        bins.pop()
        bins.append(np.inf)
        
        
        str_column = str(column)+str("_binned")
        # print(str_column)
        df[str_column] = pd.cut(df[column],
                                bins = bins,
                                labels = labels)
        
    
        return df[str_column]

当我尝试通过以下方式实现它时,我得到了它下面的错误:

column_trans = ColumnTransformer(
    [
        ("binned_numeric", dynamic_bin(weight = 'Exposure', minimum = 1000),
            ["VehAge", "DrivAge"]),
        ("onehot_categorical", OneHotEncoder(),
            ["VehBrand", "VehPower", "VehGas", "Region", "Area"]),
        ("passthrough_numeric", "passthrough",
            ["BonusMalus"]),
        ("log_scaled_numeric", log_scale_transformer,
            ["Density"]),
    ],
    remainder="drop",
)
X = column_trans.fit_transform(df)

TypeError: All estimators should implement fit and transform, or can be 'drop' or 'passthrough' specifiers. 'dynamic_bin(minimum=1000, weight='Exposure')' (type <class 'dynamic_bin.dynamic_bin'>) doesn't.

我阅读了以下内容,但我并没有真正关注它。Put customized functions in Sklearn pipeline

有人发现我犯的错误吗?

【问题讨论】:

您链接的问题的答案。要在管道中使用函数,您需要实现.fit().transform()。该问题显示了如何从 sklearn 提供的基类继承,以便为管道制作一个简单的类包装器,以利用相关函数 我不确定我适合这种转变。 看起来您正在为数据安装 bin,然后返回 bin 或 binned 数据。有关内置示例,请参阅KBinsDiscretizer 已更新问题以获得更多上下文。 【参考方案1】:

错误本身是由于您的方法声明中的拼写错误。您在自定义转换器类中实现了一个名为 tranform 的函数(注意缺少的“s”)。这就是解释器抱怨您的自定义转换器没有实现transform 的原因。

虽然这将是一个简单的修复,但您还应该注意,您尚未调整自定义函数以在您定义的类中使用。例如:

变量df应该重命名为X weightminimum 现在是对象属性,需要引用为 self.weightself.minimum 变量column 未声明

您还需要解决这些问题。关于这一点,请注意ColumnTransformer 只会将列的子集传递给打算由该特定转换器转换的转换器。这意味着如果您只将列VehAgeDrivAge 传递给dynamic_bin,它就无法访问列Exposure

【讨论】:

谢谢。你的回答很有道理。关于无法访问“曝光”的最后一点是否意味着不可能? 有很多解决方法可以解决这个问题。一种解决方案是将“曝光”列也传递给您的自定义转换器,即在 ColumnTransformer 沿着“VehAge”和“DrivAge”的步骤中指定它。然后您可以根据“曝光”执行您的转换。如果您想从最终数据帧中删除“曝光”,请在返回之前将其放入您的 transform 方法中。

以上是关于如何在 sk-learn 管道中使用我自己的自定义函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何降级使用 angular2 编写的自定义管道以在 angular 1.5 中使用?

如何使用带有 Filebeat 模块的自定义摄取管道

如何使用 PySpark 中的自定义函数在同一 ML 管道中传递分桶器?

如何在 Scikit-learn 的管道中创建我们的自定义特征提取器函数并将其与 countvectorizer 一起使用

mlflow 如何使用自定义转换器保存 sklearn 管道?

XNA 中的自定义内容管道,如何手动设置漫反射纹理?