Pyspark连接并操作列中列表中的值

时间:2016-03-19 23:43:22

标签: python join apache-spark dataframe pyspark

我有两个数据帧,即

df1 = sc.parallelize([
  ['u1', 'type1', ['a', 'b']],
  ['u2', 'type1', ['a', 'c', 'd']],
  ['u1', 'type2', ['d']]
]).toDF(('person', 'type', 'keywords'))

df2 = sc.parallelize([
  ['a', 2],
  ['b', 1],
  ['c', 0],
  ['d', 1],
  ['e', 3],
]).toDF(('keyword', 'score'))

我需要为每个person和每type计算其score的平均keywords。因此,person'type1'上type'u1'的平均值为1.5,因为它有关键字'a'和'b',贡献为2 + 1/2 = 1.5

我尝试了一种包含联接的方法:

df = df1.join(df2) \
  .select('person', 'type', 'keywords', 'keyword', 'score') \
  .groupBy('person', 'type') \
  .agg(avg('score'))

但问题是,它是计算每个可能关键字的平均值,而不仅仅是那些表示用户和类型的关键字,因此我到处获得1.4,这是所有关键字的所有分数之和除以它们的数量。 我需要在每个用户的列表keywords中仅对这些关键字的分数进行总结并输入。

1 个答案:

答案 0 :(得分:2)

您必须首先explode keywords

from pyspark.sql.functions import explode, avg, col

(df1.select("person", "type", explode("keywords").alias("keyword"))
    .join(df2, "keyword")
    .groupBy("person", "type")
    .agg(avg("score")))

虽然可以做这样的事情

from pyspark.sql.functions import expr

(df1.join(df2, expr("array_contains(keywords, keyword)"))
    .groupBy("person", "type")
    .agg(avg("score")))

为了达到相同的效果,您希望在实践中避免使用它来避免扩展为笛卡尔积。