我的目标是合并id
列上的两个数据帧,并在另一个包含JSON的列上执行一个稍微复杂的合并,我们可以调用data
。
假设我有一个看起来像这样的DataFrame df1
:
id | data
---------------------------------
42 | {'a_list':['foo'],'count':1}
43 | {'a_list':['scrog'],'count':0}
我有兴趣与相似但不同的DataFrame df2
合并:
id | data
---------------------------------
42 | {'a_list':['bar'],'count':2}
44 | {'a_list':['baz'],'count':4}
我想要下面的DataFrame,在id
匹配的JSON数据中加入和合并属性,但在id
不匹配的地方保留行,并将data
列保持为-是:
id | data
---------------------------------------
42 | {'a_list':['foo','bar'],'count':3} <-- where 'bar' is added to 'foo', and count is summed
43 | {'a_list':['scrog'],'count':1}
44 | {'a_list':['baz'],'count':4}
可以看到id
是42的地方,我必须将一些逻辑应用于JSON的合并方式。
我的想法很奇怪,我想提供一个lambda / udf来合并data
列,但不确定在联接期间如何考虑。
或者,我可以将JSON中的属性分为几列,这可能是更好的方法?
df1
:
id | a_list | count
----------------------
42 | ['foo'] | 1
43 | ['scrog'] | 0
df2
:
id | a_list | count
---------------------
42 | ['bar'] | 2
44 | ['baz'] | 4
结果:
id | a_list | count
---------------------------
42 | ['foo', 'bar'] | 3
43 | ['scrog'] | 0
44 | ['baz'] | 4
如果我走这条路,那么我将不得不将列a_list
和count
再次合并到一个单独的列data
下的JSON中,但是我可以将其包裹为一个相对简单的map
函数。
实际上,我在列表中将有n
个DataFrame,例如df_list = [df1, df2, df3]
,形状相同。在n
个数据帧上执行这些相同操作的有效方法是什么?
不确定这样做的效率如何,或者不确定是否有其他类似的方法,但是结合接受的答案,这似乎可以解决问题:
for i in range(0, (len(validations) - 1)):
# set dfs
df1 = validations[i]['df']
df2 = validations[(i+1)]['df']
# joins here...
# update new_df
new_df = df2
答案 0 :(得分:2)
这是完成第二种方法的一种方法:
分解列表列,然后unionAll
分解两个DataFrame。下一组通过“ id”列并使用pyspark.sql.functions.collect_list()
和pyspark.sql.functions.sum()
:
import pyspark.sql.functions as f
new_df = df1.select("id", f.explode("a_list").alias("a_values"), "count")\
.unionAll(df2.select("id", f.explode("a_list").alias("a_values"), "count"))\
.groupBy("id")\
.agg(f.collect_list("a_values").alias("a_list"), f.sum("count").alias("count"))
new_df.show(truncate=False)
#+---+----------+-----+
#|id |a_list |count|
#+---+----------+-----+
#|43 |[scrog] |0 |
#|44 |[baz] |4 |
#|42 |[foo, bar]|3 |
#+---+----------+-----+
最后,您可以使用pyspark.sql.functions.struct()
和pyspark.sql.functions.to_json()
将此中间DataFrame转换为所需的结构:
new_df = new_df.select("id", f.to_json(f.struct("a_list", "count")).alias("data"))
new_df.show()
#+---+----------------------------------+
#|id |data |
#+---+----------------------------------+
#|43 |{"a_list":["scrog"],"count":0} |
#|44 |{"a_list":["baz"],"count":4} |
#|42 |{"a_list":["foo","bar"],"count":3}|
#+---+----------------------------------+
更新
如果您在df_list
中有一个数据帧列表,则可以执行以下操作:
from functools import reduce # for python3
df_list = [df1, df2]
new_df = reduce(lambda a, b: a.unionAll(b), df_list)\
.select("id", f.explode("a_list").alias("a_values"), "count")\
.groupBy("id")\
.agg(f.collect_list("a_values").alias("a_list"), f.sum("count").alias("count"))\
.select("id", f.to_json(f.struct("a_list", "count")).alias("data"))