PySpark在每个数据帧行上执行普通Python函数

PySpark在每个数据帧行上执行普通Python函数,python,dataframe,apache-spark,pyspark,apache-spark-sql,Python,Dataframe,Apache Spark,Pyspark,Apache Spark Sql,我有Spark DataFrameDF1,有数百万行。每行最多有100列 col1 | col2 | col3 | ... | colN -------------------------------- v11 | v12 | v13 | ... | v1N v21 | v22 | v23 | ... | v2N ... | ... | ... | ... | ... 另外,我还有另一个DataFrameDF2,其中有数百行包含name和body列。Name包含函数名,bod

我有Spark DataFrameDF1,有数百万行。每行最多有100列

col1 | col2 | col3 | ... | colN
--------------------------------
v11  | v12  | v13  | ... | v1N
v21  | v22  | v23  | ... | v2N
...  | ...  | ...  | ... | ...
另外,我还有另一个DataFrameDF2,其中有数百行包含name和body列。Name包含函数名,body包含纯Python代码,返回true或false的布尔函数。这些函数在其逻辑中可以引用DF1中单行中的任何列

func_name | func_body
-----------------------------------------------
func1     |   col2 < col45
func2     |   col11.contains("London") and col32*col15 < col21
funcN     |   .... 

PySpark是否可行?如果可以,请举例说明如何实现?UDF函数使用
Map
from
DF.columns
作为输入参数是正确的方法还是可以用更简单的方式实现?Spark对一个时间点可以注册多少UDF函数(数字)有任何限制吗?

您可以使用SQL表达式来实现这一点,SQL表达式可以使用进行计算。但是,由于SQL表达式不能作为列值进行计算(请参见此),您将无法连接这两个数据帧,因此您必须将函数收集到一个列表中(因为您只有数百行,它可以放在内存中)

以下是一个工作示例,您可以根据自己的需求进行调整:

data1 = [(1, "val1", 4, 5, "A", 10), (0, "val2", 7, 8, "B", 20),
         (9, "val3", 8, 1, "C", 30), (10, "val4", 2, 9, "D", 30),
         (20, "val5", 6, 5, "E", 50), (3, "val6", 100, 2, "X", 45)]

df1 = spark.createDataFrame(data1, ["col1", "col2", "col3", "col4", "col5", "col6"])

data2 = [("func1", "col1 + col3 = 5 and col2 like '%al1'"),
         ("func2", "col6 = 30 or col1 * col4 > 20"),
         ("func3", "col5 in ('A', 'B', 'C') and col6 - col1 < 30"),
         ("func4", "col2 like 'val%' and col1 > 0")]

df2 = spark.createDataFrame(data2, ["func_name", "func_body"])

# get functions into a list
functions = df2.collect()

# case/when expression to evaluate the functions
satisfied_expr = [when(expr(f.func_body), lit(f.func_name)) for f in functions]

# add new column tags
df1.withColumn("tags", array(*satisfied_expr)) \
    .withColumn("tags", expr("filter(tags, x -> x is not null)")) \
    .show(truncate=False)

谢谢你的回答。不幸的是,我无法将提到的Python函数重写为SQL表达式。我需要像Python一样评估Python函数code@alexanoid好的,我明白了。不幸的是,如果您想按原样使用它们,就必须为它们中的每一个注册UDF。您可以将
df1
的所有列作为列表传递给所有UDF,并在每个UDF中获得您想要的列并应用您的逻辑…但是使用此方法,您不会让spark优化计算,因此可能会遇到一些性能问题。谢谢!老式的UDF是一种临时解决方案,它正在将遗留逻辑移植到Spark API。因此,时间性能问题不是一个大问题,因为在某个时候,所有这些函数都将被用ApacheAPI编写的新逻辑所取代。一个附加问题-是否可以从另一个UDF函数调用一个UDF函数?据我所知,您不能从另一个UDF调用UDF…您到底想做什么?嗨@alexanoid,是的,就是这样!当您调用它时,它仍然被解释为Python函数。如果您注册创建的UDF,那么您可以将它们的名称保留在DF2中,然后使用与上面代码中相同的逻辑。
data1 = [(1, "val1", 4, 5, "A", 10), (0, "val2", 7, 8, "B", 20),
         (9, "val3", 8, 1, "C", 30), (10, "val4", 2, 9, "D", 30),
         (20, "val5", 6, 5, "E", 50), (3, "val6", 100, 2, "X", 45)]

df1 = spark.createDataFrame(data1, ["col1", "col2", "col3", "col4", "col5", "col6"])

data2 = [("func1", "col1 + col3 = 5 and col2 like '%al1'"),
         ("func2", "col6 = 30 or col1 * col4 > 20"),
         ("func3", "col5 in ('A', 'B', 'C') and col6 - col1 < 30"),
         ("func4", "col2 like 'val%' and col1 > 0")]

df2 = spark.createDataFrame(data2, ["func_name", "func_body"])

# get functions into a list
functions = df2.collect()

# case/when expression to evaluate the functions
satisfied_expr = [when(expr(f.func_body), lit(f.func_name)) for f in functions]

# add new column tags
df1.withColumn("tags", array(*satisfied_expr)) \
    .withColumn("tags", expr("filter(tags, x -> x is not null)")) \
    .show(truncate=False)
+----+----+----+----+----+----+---------------------+
|col1|col2|col3|col4|col5|col6|tags                 |
+----+----+----+----+----+----+---------------------+
|1   |val1|4   |5   |A   |10  |[func1, func3, func4]|
|0   |val2|7   |8   |B   |20  |[func3]              |
|9   |val3|8   |1   |C   |30  |[func2, func3, func4]|
|10  |val4|2   |9   |D   |30  |[func2, func4]       |
|20  |val5|6   |5   |E   |50  |[func2, func4]       |
|3   |val6|100 |2   |X   |45  |[func4]              |
+----+----+----+----+----+----+---------------------+