【问题标题】:ReturnType of Pandas UDF varies per input. How to deal? - PysparkPandas UDF 的 ReturnType 因输入而异。如何解决? - 派斯帕克
【发布时间】:2022-01-19 07:42:53
【问题描述】:

我写了一个 pandas UDF,它返回一列的值,而另一列有它的最大值

@F.pandas_udf("string")
def belonging_to_max_udf(value_of: pd.Series, where_this_is_max: pd.Series) -> str :
  mx = where_this_is_max.max()
  if not pd.isnull(mx):
    return_value = value_of[where_this_is_max == mx]
    return return_value.iloc[0]
  return None

但是,“value_of”列可以是任何类型。在这个特定示例中,它是一个字符串列。但是,我也想将此函数用于其他列类型,而不必为所有可能的返回类型编写单独的函数,即我不想执行以下操作:

@F.pandas_udf("string")
def belonging_to_max__STRING_udf(value_of: pd.Series, where_this_is_max: pd.Series) -> str :
  mx = where_this_is_max.max()
  if not pd.isnull(mx):
    return_value = value_of[where_this_is_max == mx]
    return return_value.iloc[0]
  return None


@F.pandas_udf("double")
def belonging_to_max_DOUBLE_udf(value_of: pd.Series, where_this_is_max: pd.Series) -> float:
  mx = where_this_is_max.max()
  if not pd.isnull(mx):
    return_value = value_of[where_this_is_max == mx]
    return return_value.iloc[0]
  return None

... etc for other types

有什么好办法解决这个问题吗?

我尝试过重载:

@F.pandas_udf("string")
@F.pandas_udf("double")
def belonging_to_max_udf(value_of: pd.Series, where_this_is_max: pd.Series) -> Any:
  mx = where_this_is_max.max()
  if not pd.isnull(mx):
    return_value = value_of[where_this_is_max == mx]
    return return_value.iloc[0]
  return None

但是没有用。

【问题讨论】:

  • 不知道我是否理解。您用stringdouble 定义的是UDF 的返回类型,它与您的输入类型(即value_of)无关。请参阅pyspark.sql.functions.pandas_udf 了解更多信息。是否要根据输入列动态设置 UDF 的返回值?
  • 您好弗拉德西夫,感谢您的回复。我理解这个字符串,double 涉及 UDF 的返回类型。但是,UDF 返回 value_of 列的值(更具体地说,该列的值对应于 where_this_is_max 取其最大值的索引)。 value_of 列可以是 string-column(其中 UDF 的返回类型为 string)或 double 列(其中 UDF 的返回类型为 double),或任何其他类型(boolean、int 等)

标签: python apache-spark pyspark user-defined-functions


【解决方案1】:

您可以按照声明的方式根据列数据类型动态定义pandas UDF。请注意,包装器会为不同类型创建 UDF,但它对用户是透明的。

import pandas as pd
from typing import Any
from pyspark.sql.functions import pandas_udf
from pyspark.sql import DataFrame

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

# You expose only this function to users
def max_udf_wrapper(df: DataFrame, col: str):
    data_type= df.schema[col].dataType
    #dynamically construct UDF here based on col_type
    def _find_max(v: pd.Series) -> Any:
        return v.max()
    find_max_udf = pandas_udf(_find_max, data_type)
    return find_max_udf(df[col])


df.select(max_udf_wrapper(df, 'id'), max_udf_wrapper(df, 'v')).printSchema()
#root
# |-- find_max(id): string (nullable = true)
# |-- find_max(v): double (nullable = true)

df.select(max_udf_wrapper(df, 'id'), max_udf_wrapper(df, 'v')).show()

#+------------+-----------+
#|find_max(id)|find_max(v)|
#+------------+-----------+
#|         200|       10.0|
#+------------+-----------+

【讨论】:

  • 嗨 Nithish。谢谢你和我一起思考。但是,在这种情况下,我仍然需要定义两个 udf,max_udf_double 和 max_udf_string。我只想拥有一个 UDF
  • 我明白,所以您关心的不是代码重复,您是否可以接受涉及从UDF 转换结果的解决方案?
  • 我希望函数的用户不必考虑输入类型(因此,他也不必考虑是否必须使用 max_udf_double 或 max_udf_string,只需使用 max_udf)。我不确定如何在这张图片中进行转换(尽管用户不必考虑应该使用哪个功能,但他/她应该知道应该将其转换为哪种格式)。但是,如果您知道这样做的好方法,那将不胜感激!我正在考虑编写一个动态创建熊猫 UDF 的包装器,但不知道该怎么做哈哈。
  • @Rens 我已经编辑了答案,这样您就可以向用户公开所有数据类型的单个函数。
猜你喜欢
  • 2021-07-25
  • 2018-07-30
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2021-07-07
  • 2018-09-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多