Python 使用带参数的分组映射UDF

Python 使用带参数的分组映射UDF,python,pyspark,pandas-groupby,Python,Pyspark,Pandas Groupby,我想使用data.groupby.apply()将函数应用于每个组的Pyspark Dataframe的每一行 我使用了UDF的分组映射。但是,我不知道如何向函数中添加另一个参数 我尝试将参数用作全局变量,但函数无法识别它(我的参数是pyspark数据帧) 我还尝试了这个问题中提出的解决方案(针对熊猫数据帧) @pandas\u udf(模式,PandasUDFType.GROUPED\u映射) def功能(键、数据、间隔): interval\u df=interval.filter(inte

我想使用data.groupby.apply()将函数应用于每个组的Pyspark Dataframe的每一行

我使用了UDF的分组映射。但是,我不知道如何向函数中添加另一个参数

我尝试将参数用作全局变量,但函数无法识别它(我的参数是pyspark数据帧)
我还尝试了这个问题中提出的解决方案(针对熊猫数据帧)

@pandas\u udf(模式,PandasUDFType.GROUPED\u映射)
def功能(键、数据、间隔):
interval\u df=interval.filter(interval[“var”]==key.toPandas()
对于间隔_df中的值:
#应用一些操作
返回数据.groupBy(“msn”).apply(计算差异,('arg1'))

@pandas\u udf(模式,PandasUDFType.GROUPED\u映射)
def功能(键、数据、间隔):
interval\u df=interval.filter(interval[“var”]==key.toPandas()
对于间隔_df中的值:
#应用一些操作
返回数据.groupBy(“msn”).apply(lambda x:calc_diff(x,'arg1'))
但我得到了一个错误:

ValueError:函数无效:函数类型为GROUPED_MAP的Panda_UDF必须采用一个参数(数据)或两个参数(键、数据)

有人能帮我解决上述问题吗


谢谢

我想你可以这样做

def myfun(data, key, interval):
    #Apply some operations
    return something

@pandas_udf(schema, PandasUDFType.GROUPED_MAP)
def myfun_udf(data):
    return myfun(data=data, key=mykey, interval=myinterval)


mykey=1
myinterval=2

Data.groupBy("msn").apply(myfun_udf)


您可以在函数中创建udf,以便在创建函数时知道函数参数。(或者您可以导入functools并使用部分函数求值来执行相同的操作。)以下是PySpark文档中的,经过修改以传入一些参数:

from pyspark.sql.functions import pandas_udf, PandasUDFType

df = spark.createDataFrame(
    [(1, 1.0), (1, 2.0), (2, 3.0), (2, 5.0), (2, 10.0)],
    ("id", "v"))


def my_function(df, by="id", column="v", value=1.0):
    schema = "{} long, {} double".format(by, column)

    @pandas_udf(schema, PandasUDFType.GROUPED_MAP)
    def subtract_value(pdf):
        # pdf is a pandas.DataFrame
        v = pdf[column]
        g = pdf[by]
        return pdf.assign(v = v - g * value)

    return df.groupby(by).apply(subtract_value)

my_function(df, by="id", column="v", value=2.0).show()

我喜欢@hwrd的想法,但相反,它将使其成为一种生成器模式,以便于集成,如@Feng的示例中所示:

def function_generator(key):
    @pandas_udf(schema,PandasUDFType.GROUPED_MAP)
    def function(interval):
        interval_df=interval.filter(interval["var"]==key).toPandas()
        for value in interval_df:
              #Apply some operations
    return function

calc_diff = function_generator('arg1')
output = Data.groupBy("msn").apply(calc_diff)


所有的答案似乎都很有用,但没有对正在发生的事情进行正式描述。所以,我从所有人那里,特别是从@sifta那里,取了一些零碎的东西,试图解释为。也许这会对将来的人有所帮助

假设我有一个PySpark DF,如下所示

# test = pd.DataFrame({
# 'c1': ['a', 'a', 'b', 'b', 'b'],
# 'c2': ['a1', 'a2', 'b1', 'b1', 'b2']})
# test = spark.createDataFrame(test)

+---+---+
| c1| c2|
+---+---+
|  a| a1|
|  a| a2|
|  b| b1|
|  b| b1|
|  b| b2|
+---+---+
我的目标是创建另一列
c3
,它可以是
组计数+一些固定值。好的,这肯定不是最好的例子,但是让我们试着用groupby来解决它。我们需要传递参数(固定值),这是一种不直接支持的参数

所以,根据答案,我们可以得出

schema = t.StructType([
  t.StructField('c1', t.StringType()),
  t.StructField('c2', t.StringType()),
  t.StructField('c3', t.IntegerType()),
])

def fn_wrapper(df, val):

  @f.pandas_udf(schema, f.PandasUDFType.GROUPED_MAP)
  def fn(pdf):
    pdf['c3'] = pdf.shape[0] + val
    return pdf

  return df.groupby('c1', 'c2').apply(fn)

fn_wrapper(test, 7).show()
但这到底意味着什么

我们有一个用于映射fn返回的DF的模式(
returnpdf

那么,让我们来了解这个技巧是如何工作的。我们已经定义了一个名为
fn_wrapper
的普通UDF,它接受Pyspark DF和要在核心pandas groupby中使用的参数。我们在
fn\u包装器(test,7.show()
中调用它。现在,当我们在
fn_包装器
中时,我们只是在其中有一个函数体,它将在此时被编译而不是执行

接下来,执行语句
返回df.groupby('c1','c2').apply(fn)
。请参见,我们将函数
fn
定义为一个UDF,它没有任何参数。然而,当
fn被称为apply
并且定义了
val
时,我们仍然在fn\u包装的
范围内。因此,我们可以很容易地在
pdf['c3']=pdf.shape[0]+val
中引用val,其中分组数据以数据帧的形式呈现


我想这有助于理解将函数包装为函数内部的函数,以及如何将UDF与参数结合起来。

在我必须处理在某个阶段不为我所知的方案的情况下,这种模式也帮助了我;我也可以把它们当作一个论点来通过。我试过上面的方法,但它不起作用!因为udf不使用定义的全局变量mykey和myinterval,所以每次更改这两个参数时,我都必须重新运行分组映射udf定义,以便更新它们!