【问题标题】:Find maximum row per group in Spark DataFrame在 Spark DataFrame 中查找每组的最大行数
【发布时间】:2016-05-15 02:51:46
【问题描述】:

我正在尝试使用 Spark 数据帧而不是 RDD,因为它们似乎比 RDD 更高级,并且倾向于生成更具可读性的代码。

在一个 14 节点的 Google Dataproc 集群中,我有大约 600 万个名称,它们被两个不同的系统转换为 id:sasb。每个Row 包含nameid_said_sb。我的目标是生成从id_said_sb 的映射,这样对于每个id_sa,对应的id_sbid_sa 附加的所有名称中最常见的ID。

让我们试着用一个例子来澄清一下。如果我有以下行:

[Row(name='n1', id_sa='a1', id_sb='b1'),
 Row(name='n2', id_sa='a1', id_sb='b2'),
 Row(name='n3', id_sa='a1', id_sb='b2'),
 Row(name='n4', id_sa='a2', id_sb='b2')]

我的目标是生成从a1b2 的映射。实际上,与a1 关联的名称是n1n2n3,它们分别映射到b1b2b2,因此b2 是名称中最频繁的映射关联到a1。同样,a2 将映射到b2。可以假设总会有赢家:无需打破平局。

我希望我可以在我的数据框上使用groupBy(df.id_sa),但我不知道下一步该做什么。我希望聚合最终可以产生以下行:

[Row(id_sa=a1, max_id_sb=b2),
 Row(id_sa=a2, max_id_sb=b2)]

但也许我尝试使用错误的工具,我应该重新使用 RDD。

【问题讨论】:

    标签: apache-spark pyspark apache-spark-sql


    【解决方案1】:

    使用join(如果出现平局,将导致多于一行):

    import pyspark.sql.functions as F
    from pyspark.sql.functions import count, col 
    
    cnts = df.groupBy("id_sa", "id_sb").agg(count("*").alias("cnt")).alias("cnts")
    maxs = cnts.groupBy("id_sa").agg(F.max("cnt").alias("mx")).alias("maxs")
    
    cnts.join(maxs, 
      (col("cnt") == col("mx")) & (col("cnts.id_sa") == col("maxs.id_sa"))
    ).select(col("cnts.id_sa"), col("cnts.id_sb"))
    

    使用窗口函数(会掉线):

    from pyspark.sql.functions import row_number
    from pyspark.sql.window import Window
    
    w = Window().partitionBy("id_sa").orderBy(col("cnt").desc())
    
    (cnts
      .withColumn("rn", row_number().over(w))
      .where(col("rn") == 1)
      .select("id_sa", "id_sb"))
    

    使用struct 排序:

    from pyspark.sql.functions import struct
    
    (cnts
      .groupBy("id_sa")
      .agg(F.max(struct(col("cnt"), col("id_sb"))).alias("max"))
      .select(col("id_sa"), col("max.id_sb")))
    

    另见How to select the first row of each group?

    【讨论】:

    • 您介意再澄清一下.agg(F.max(struct(col("cnt"), col("id_sb"))).alias("max")) 的工作原理吗?它是否使用row_number().over(struct(col("cnt"), col("id_sb"))) 之类的东西解决?
    【解决方案2】:

    我认为您可能正在寻找的是窗口函数: http://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=window#pyspark.sql.Window

    https://databricks.com/blog/2015/07/15/introducing-window-functions-in-spark-sql.html

    这是 Scala 中的一个示例(我现在没有带 Hive 的 Spark Shell,所以我无法测试代码,但我认为它应该可以工作):

    case class MyRow(name: String, id_sa: String, id_sb: String)
    
    val myDF = sc.parallelize(Array(
        MyRow("n1", "a1", "b1"),
        MyRow("n2", "a1", "b2"),
        MyRow("n3", "a1", "b2"),
        MyRow("n1", "a2", "b2")
    )).toDF("name", "id_sa", "id_sb")
    
    import org.apache.spark.sql.expressions.Window
    
    val windowSpec = Window.partitionBy(myDF("id_sa")).orderBy(myDF("id_sb").desc)
    
    myDF.withColumn("max_id_b", first(myDF("id_sb")).over(windowSpec).as("max_id_sb")).filter("id_sb = max_id_sb")
    

    可能有更有效的方法可以使用 Window 函数实现相同的结果,但我希望这会为您指明正确的方向。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-16
      相关资源
      最近更新 更多