Apache spark Pyspark:在UDF中传递多个列

Apache spark Pyspark:在UDF中传递多个列,apache-spark,pyspark,spark-dataframe,Apache Spark,Pyspark,Spark Dataframe,我正在编写一个用户定义的函数,它将获取数据帧中除第一列之外的所有列,并执行求和(或任何其他操作)。现在,数据帧有时可以有3列或4列或更多。这将有所不同 我知道我可以硬编码4个列名作为UDF中的通行证,但在这种情况下,它会有所不同,所以我想知道如何做到这一点 这里有两个例子,第一个我们要添加两列,第二个我们要添加三列 如果要传递给UDF的所有列都具有相同的数据类型,则可以使用数组作为输入参数,例如: >>> from pyspark.sql.types import Intege

我正在编写一个用户定义的函数,它将获取数据帧中除第一列之外的所有列,并执行求和(或任何其他操作)。现在,数据帧有时可以有3列或4列或更多。这将有所不同

我知道我可以硬编码4个列名作为UDF中的通行证,但在这种情况下,它会有所不同,所以我想知道如何做到这一点

这里有两个例子,第一个我们要添加两列,第二个我们要添加三列


如果要传递给UDF的所有列都具有相同的数据类型,则可以使用数组作为输入参数,例如:

>>> from pyspark.sql.types import IntegerType
>>> from pyspark.sql.functions import udf, array
>>> sum_cols = udf(lambda arr: sum(arr), IntegerType())
>>> spark.createDataFrame([(101, 1, 16)], ['ID', 'A', 'B']) \
...     .withColumn('Result', sum_cols(array('A', 'B'))).show()
+---+---+---+------+
| ID|  A|  B|Result|
+---+---+---+------+
|101|  1| 16|    17|
+---+---+---+------+

>>> spark.createDataFrame([(101, 1, 16, 8)], ['ID', 'A', 'B', 'C'])\
...     .withColumn('Result', sum_cols(array('A', 'B', 'C'))).show()
+---+---+---+---+------+
| ID|  A|  B|  C|Result|
+---+---+---+---+------+
|101|  1| 16|  8|    25|
+---+---+---+---+------+

使用结构而不是数组

from pyspark.sql.types import IntegerType
from pyspark.sql.functions import udf, struct
sum_cols = udf(lambda x: x[0]+x[1], IntegerType())
a=spark.createDataFrame([(101, 1, 16)], ['ID', 'A', 'B'])
a.show()
a.withColumn('Result', sum_cols(struct('A', 'B'))).show()

另一种没有数组和结构的简单方法

from pyspark.sql.types import IntegerType
from pyspark.sql.functions import udf, struct

def sum(x, y):
    return x + y

sum_cols = udf(sum, IntegerType())

a=spark.createDataFrame([(101, 1, 16)], ['ID', 'A', 'B'])
a.show()
a.withColumn('Result', sum_cols('A', 'B')).show()

这就是我尝试的方式,似乎也很有效:

colsToSum = df.columns[1:]
df_sum = df.withColumn("rowSum", sum([df[col] for col in colsToSum]))

如果您不想键入所有列名,而只想将所有列转储到UDF中,则需要在结构中封装列表理解

from pyspark.sql.functions import struct, udf
sum_udf = udf(lambda x: sum(x[1:]))
df_sum = df.withColumn("result", sum_udf(struct([df[col] for col in df.columns])))

也许这是一个迟来的答案,但我不喜欢在没有必要的情况下使用UDF,因此:

from pyspark.sql.functions import col
from functools import reduce
data = [["a",1,2,5],["b",2,3,7],["c",3,4,8]]
df = spark.createDataFrame(data,["id","v1","v2",'v3'])

calculate = reduce(lambda a, x: a+x, map(col, ["v1","v2",'v3']))

df.withColumn("Result", calculate)
#
#id v1  v2  v3  Result
#a  1   2   5   8
#b  2   3   7   12
#c  3   4   8   15
在这里,您可以使用
列中实现的任何操作。另外,如果您想编写具有特定逻辑的自定义
udf
,您可以使用它,因为
Column
提供了树执行操作。无需收集到数组并在其上求和

如果与数组操作相比,从性能的角度来看将是不好的,让我们来看看物理计划,在我的情况下和数组的情况下,在我的情况下和<代码>数组 CASE,

我的情况是:

== Physical Plan ==
*(1) Project [id#355, v1#356L, v2#357L, v3#358L, ((v1#356L + v2#357L) + v3#358L) AS Result#363L]
+- *(1) Scan ExistingRDD[id#355,v1#356L,v2#357L,v3#358L]
数组大小写:

== Physical Plan ==
*(2) Project [id#339, v1#340L, v2#341L, v3#342L, pythonUDF0#354 AS Result#348]
+- BatchEvalPython [<lambda>(array(v1#340L, v2#341L, v3#342L))], [pythonUDF0#354]
   +- *(1) Scan ExistingRDD[id#339,v1#340L,v2#341L,v3#342L]
==物理计划==
*(2) 项目[id#339,v1#340L,v2#341L,v3#342L,pythonUDF0 354作为结果#348]
+-BatchEvalPython[(数组(v1#340L,v2#341L,v3#342L)),[pythonUDF0 354]
+-*(1)扫描现有RDD[id#339,v1#340L,v2#341L,v3#342L]

如果可能的话-我们需要避免使用UDF作为催化剂,因为我们不知道如何优化这些

也可以在Scala中使用:
myUdf(数组($“col1”,“$“col2”))
如何为不同类型的列实现它?@constructor如果不同类型的和数也相同,您可以使用
数组
(即整数和双精度->两者都将转换为双精度)你能解释一下为什么人们会使用
struct
而不是
array
?我猜这是为了处理不同类型的列?我想你不需要导入struct。这只会因为你没有调用UDF而起作用。否则,你需要将这个列表封装在一个struct中。