性能:按以前分组列的子集分组

时间:2016-05-06 18:10:30

标签: apache-spark dataframe apache-spark-sql

我有一个带有两个分类列的DataFrame,类似于以下示例:

+----+-------+-------+
| ID | Cat A | Cat B |
+----+-------+-------+
|  1 |   A   |   B   |
|  2 |   B   |   C   |
|  5 |   A   |   B   |
|  7 |   B   |   C   |
|  8 |   A   |   C   |
+----+-------+-------+

我需要进行一些处理,这需要两个步骤:第一步需要按两个分类列对数据进行分组。在该示例中,它将生成以下DataFrame:

+-------+-------+-----+
| Cat A | Cat B | Cnt |
+-------+-------+-----+
|   A   |   B   |  2  |
|   B   |   C   |  2  |
|   A   |   C   |  1  |
+-------+-------+-----+

然后,下一步是仅按CatA进行分组,以计算新的聚合,例如:

+-----+-----+
| Cat | Cnt |
+-----+-----+
|  A  |  3  |
|  B  |  2  |
+-----+-----+

现在来问题

  1. 在我的解决方案中,我通过

    创建了中间数据框
    val df2 = df.groupBy("catA", "catB").agg(...)
    

    然后我汇总这个df2以获得最后一个:

    val df3 = df2.groupBy("catA").agg(...)
    

    我认为它比再次聚合第一个DF更有效。这是一个很好的假设吗?或者没有区别?

  2. 是否有任何建议可以更有效地获得相同的结果?

1 个答案:

答案 0 :(得分:3)

一般来说,它看起来是一种好方法,应该比两次聚合数据更有效。由于shuffle文件是隐式缓存的,因此至少部分工作应该只执行一次。因此,当您在df2上调用操作并随后在df3上调用操作时,您应该会看到已跳过与df2对应的阶段。第一个shuffle强制执行的部分结构也可以减少第二个agg期间聚合缓冲区的内存需求。

不幸的是,DataFrame聚合与RDD聚合不同,无法使用自定义分区程序。这意味着您无法使用基于值catA的单个shuffle计算两个数据帧。这意味着第二个聚合将需要单独的交换散列分区。我怀疑是否有理由改用RDDs