Spark SQL二次过滤和分组

时间:2015-12-18 21:49:51

标签: apache-spark apache-pig apache-spark-sql

问题:我有一个数据集A {filed1,field2,field3 ...},我想首先按照field1对A进行分组,然后在每个结果组,我想做一堆子查询,例如,计算具有field2 == true的行数,或计算具有field3和{{的不同field4 == "some_value"的数量1}}等等。

我能想到的一些替代方案:我可以编写一个自定义的用户定义聚合函数,它接受一个计算过滤条件的函数,但这样我就必须为每个函数创建一个实例。查询条件。我也看过field5 == false函数可以实现一些操作,但我无法弄清楚如何使用它来实现filter-distinct-count语义。

在Pig中,我可以这样做:

countDistinct

有没有办法在Spark SQL中执行此操作?

1 个答案:

答案 0 :(得分:1)

排除不同的计数,这可以通过简单的总和来解决:

import org.apache.spark.sql.functions.sum

val df = sc.parallelize(Seq(
  (1L, true, "x", "foo", true), (1L, true, "y", "bar", false), 
  (1L, true, "z", "foo", true), (2L, false, "y", "bar", false), 
  (2L, true, "x", "foo", false)
)).toDF("field1", "field2", "field3", "field4", "field5")

val left = df.groupBy($"field1").agg(
  sum($"field2".cast("int")).alias("fa"),
  sum(($"field4" === "foo" && ! $"field5").cast("int")).alias("fb")
)
left.show

// +------+---+---+
// |field1| fa| fb|
// +------+---+---+
// |     1|  3|  0|
// |     2|  1|  1|
// +------+---+---+

不幸的是,这更棘手。 Spark SQL doesn't physically group data中的GROUP BY子句。更不用说找到不同的元素是非常昂贵的。您可以做的最好的事情就是分别计算不同的计数并简单地加入结果:

val right = df.where($"field4" === "foo" && ! $"field5")
  .select($"field1".alias("field1_"), $"field3")
  .distinct
  .groupBy($"field1_")
  .agg(count("*").alias("fc"))

val joined = left
  .join(right, $"field1" === $"field1_", "leftouter")
  .na.fill(0)

使用UDAF计算每个条件的不同值绝对是一种选择,但有效的实现将相当棘手。从内部表示转换相当昂贵,并且使用集合存储实现快速UDAF也不便宜。如果您可以接受近似解决方案,可以在那里使用bloom过滤器。