【问题标题】:How to add my own function as a custom stage in a ML pyspark Pipeline? [duplicate]如何在 ML pyspark 管道中添加我自己的函数作为自定义阶段? [复制]
【发布时间】:2018-12-27 04:28:26
【问题描述】:

来自 Florian 的示例代码

-----------+-----------+-----------+
|ball_column|keep_the   |hall_column|
+-----------+-----------+-----------+
|          0|          7|         14|
|          1|          8|         15|
|          2|          9|         16|
|          3|         10|         17|
|          4|         11|         18|
|          5|         12|         19|
|          6|         13|         20|
+-----------+-----------+-----------+

代码的第一部分在禁止列表中删除列名称

#first part of the code

banned_list = ["ball","fall","hall"]
condition = lambda col: any(word in col for word in banned_list)
new_df = df.drop(*filter(condition, df.columns))

所以上面的代码应该去掉ball_columnhall_column

代码的第二部分存储列表中的特定列。对于这个例子,我们将存储唯一剩下的一个,keep_column

bagging = 
    Bucketizer(
        splits=[-float("inf"), 10, 100, float("inf")],
        inputCol='keep_the',
        outputCol='keep_the')

现在使用管道对列进行装袋如下

model = Pipeline(stages=bagging).fit(df)

bucketedData = model.transform(df)

如何将代码的第一块(banned listconditionnew_df)作为阶段添加到 ml 管道?

【问题讨论】:

    标签: python apache-spark pyspark apache-spark-sql


    【解决方案1】:

    我相信这可以满足您的需求。您可以创建自定义Transformer,并将其添加到Pipeline 中的阶段。请注意,我略微更改了您的函数,因为我们无法访问您提到的所有变量,但概念保持不变。

    希望这会有所帮助!

    import pyspark.sql.functions as F
    from pyspark.ml import Pipeline, Transformer
    from pyspark.ml.feature import Bucketizer
    from pyspark.sql import DataFrame
    from typing import Iterable
    import pandas as pd
    
    # CUSTOM TRANSFORMER ----------------------------------------------------------------
    class ColumnDropper(Transformer):
        """
        A custom Transformer which drops all columns that have at least one of the
        words from the banned_list in the name.
        """
    
        def __init__(self, banned_list: Iterable[str]):
            super(ColumnDropper, self).__init__()
            self.banned_list = banned_list
    
        def _transform(self, df: DataFrame) -> DataFrame:
            df = df.drop(*[x for x in df.columns if any(y in x for y in self.banned_list)])
            return df
    
    
    # SAMPLE DATA -----------------------------------------------------------------------
    df = pd.DataFrame({'ball_column': [0,1,2,3,4,5,6],
                       'keep_the': [6,5,4,3,2,1,0],
                       'hall_column': [2,2,2,2,2,2,2] })
    df = spark.createDataFrame(df)
    
    
    # EXAMPLE 1: USE THE TRANSFORMER WITHOUT PIPELINE -----------------------------------
    column_dropper = ColumnDropper(banned_list = ["ball","fall","hall"])
    df_example = column_dropper.transform(df)
    
    
    # EXAMPLE 2: USE THE TRANSFORMER WITH PIPELINE --------------------------------------
    column_dropper = ColumnDropper(banned_list = ["ball","fall","hall"])
    bagging = Bucketizer(
            splits=[-float("inf"), 3, float("inf")],
            inputCol= 'keep_the',
            outputCol="keep_the_bucket")
    model = Pipeline(stages=[column_dropper,bagging]).fit(df)
    bucketedData = model.transform(df)
    bucketedData.show()
    

    输出:

    +--------+---------------+
    |keep_the|keep_the_bucket|
    +--------+---------------+
    |       6|            1.0|
    |       5|            1.0|
    |       4|            1.0|
    |       3|            1.0|
    |       2|            0.0|
    |       1|            0.0|
    |       0|            0.0|
    +--------+---------------+
    

    另外,请注意,如果您需要安装自定义方法(例如自定义StringIndexer),您还应该创建自定义Estimator

    class CustomTransformer(Transformer):
    
        def _transform(self, df) -> DataFrame:
    
    
    class CustomEstimator(Estimator):
    
        def _fit(self, df) -> CustomTransformer:
    

    【讨论】:

    • +1。但有趣的是,您的回答在这里与此相矛盾:stackoverflow.com/questions/51402369/…
    • @eliasah 谢谢,我没有真正看 Bucketizer,只是第一次尝试创建自定义 Pipeline。你能帮我理解矛盾是什么吗,我没看到?
    • 问题只是每个人都在为 OP 提供建议的方向。但我喜欢这种方法,它更干净。
    • 我知道@Matthew。不要误会!我只是建议你在描述你的问题时更加努力。你的问题看起来有点像 XY 问题,我强烈建议你阅读 stackoverflow.com/questions/48427185/… 以帮助你写出更好的问题;)
    • 嗨@Mathew,1) 所以我们的类继承自Transformer,见here。 2) 表明这个参数应该是一个带有字符串的可迭代(例如列表)。 3)初始化Transformer的__init__()函数。 4) 变换输入df的变换方法。 5) 该方法需要一个DataFrame,否则它怎么知道要转换哪个DataFrame? 6) 它表示输出将属于DataFrame 类(类型提示)。希望这能澄清一点;)
    猜你喜欢
    • 2018-07-16
    • 1970-01-01
    • 2021-08-13
    • 2019-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-12
    相关资源
    最近更新 更多