PySpark在每个数据帧行上执行普通Python函数
我有Spark DataFrameDF1,有数百万行。每行最多有100列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
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
fromDF.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] |
+----+----+----+----+----+----+---------------------+