在PySpark中求和

时间:2019-03-13 10:47:17

标签: python dataframe pyspark

我有一个像这样的pyspark数据框

data = [(("ID1", 10, 30)), (("ID2", 20, 60))]
df1 = spark.createDataFrame(data, ["ID", "colA", "colB"])
df1.show()

df1: 
+---+-----------+
| ID| colA| colB|
+---+-----------+
|ID1|   10|   30|
|ID2|   20|   60| 
+---+-----------+

我有另一个这样的数据框

data = [(("colA", 2)), (("colB", 5))]
df2 = spark.createDataFrame(data, ["Column", "Value"])
df2.show()

df2:
+-------+------+
| Column| Value|
+-------+------+
|   colA|     2|
|   colB|     5| 
+-------+------+

我想将df1中的每一列除以df2中的相应值。因此df3看起来像

df3: 
+---+-------------------------+
| ID|        colA|        colB|
+---+------------+------------+
|ID1|    10/2 = 5|    30/5 = 6|
|ID2|   20/2 = 10|   60/5 = 12| 
+---+------------+------------+

最终,我想添加colA和colB以获得每个ID的最终df4

df4: 
+---+---------------+
| ID|       finalSum|
+---+---------------+
|ID1|     5 + 6 = 11|
|ID2|   10 + 12 = 22| 
+---+---------------+

1 个答案:

答案 0 :(得分:2)

这个想法是将两个DataFrames连接在一起,然后应用division操作。由于df2包含列名和相应的值,因此我们需要先pivot(),然后再与主表df1联接。 (数据透视是一项昂贵的操作,但只要DataFrame很小,就可以了。)

# Loading the requisite packages
from pyspark.sql.functions import col
from functools import reduce
from operator import add

# Creating the DataFrames
df1 = sqlContext.createDataFrame([('ID1', 10, 30), ('ID2', 20, 60)],('ID','ColA','ColB'))
df2 = sqlContext.createDataFrame([('ColA', 2), ('ColB', 5)],('Column','Value'))

代码相当通用,因此我们不需要自己指定列名。我们找到了需要操作的列名。除了ID,我们需要全部。

# This contains the list of columns where we apply mathematical operations
columns_to_be_operated = df1.columns
columns_to_be_operated.remove('ID')
print(columns_to_be_operated)
    ['ColA', 'ColB']

枢纽df2,我们将加入df1

# Pivoting the df2 to get the rows in column form
df2 = df2.groupBy().pivot('Column').sum('Value')
df2.show()
+----+----+ 
|ColA|ColB| 
+----+----+ 
|   2|   5| 
+----+----+

我们可以更改列名,这样我们就不会为每个列都重复一个名称。为此,我们在所有名称上都添加了后缀_x

# Dynamically changing the name of the columns in df2
df2 = df2.select([col(c).alias(c+'_x') for c in df2.columns])
df2.show()
+------+------+ 
|ColA_x|ColB_x| 
+------+------+ 
|     2|     5| 
+------+------+

接下来,我们使用笛卡尔联接来联接表。 (请注意,如果df2过大,可能会遇到内存问题。)

df = df1.crossJoin(df2)
df.show()
+---+----+----+------+------+ 
| ID|ColA|ColB|ColA_x|ColB_x| 
+---+----+----+------+------+ 
|ID1|  10|  30|     2|     5| 
|ID2|  20|  60|     2|     5| 
+---+----+----+------+------+

最后通过首先将列除以相应的值来添加列。 reduce()将两个自变量的函数add()累积应用于序列的项。

df = df.withColumn(
    'finalSum', 
    reduce(add, [col(c)/col(c+'_x') for c in columns_to_be_operated])
).select('ID','finalSum')

df.show()
+---+--------+ 
| ID|finalSum| 
+---+--------+ 
|ID1|    11.0| 
|ID2|    22.0| 
+---+--------+

注意:OP必须谨慎对待0的除法。可以更改上述正上方的代码段,以将这种情况考虑在内。