Spark - 在一列上分组并找到其他列的平均值

时间:2017-02-19 15:22:31

标签: scala apache-spark aggregate grouping

我有一些包含4列(c1,c2,c3和c4)的数据,并通过一些scala代码将其放入RDD中。

我想通过c1分组/ bin并找到每个c1组中c2的平均值和c3的平均值以及c4。

我正在研究RDD:reduceByKey,但我还没有设法理解它是如何被使用的。有一个更好的方法吗?如何从Scala API执行此操作?

1 个答案:

答案 0 :(得分:4)

你说你有一个DataFrame,所以你可能不应该使用RDD API(通常效率较低,在这种情况下也可能不那么直观) - 这里&#39 ;使用DataFrame API的解决方案:

import org.apache.spark.sql.functions._

val result = df.groupBy("c1").agg(mean("c2"), mean("c3"), mean("c4"))

result将是具有以下架构的DataFrame(假设c1是一个开头的字符串):

root
 |-- c1: string (nullable = true)
 |-- avg(c2): double (nullable = true)
 |-- avg(c3): double (nullable = true)
 |-- avg(c4): double (nullable = true)

修改

如果列的列表是动态的,您可以轻松地将这样的列表映射到相应的"表示"并使用该列表聚合DF:

val colsToCompute = List("c2", "c3", "c4") // can be loaded dynamically
val means: Seq[Column] = colsToCompute.map(mean)
val result = df.groupBy("c1").agg(means.head, means.tail: _*)

为了完整性 - 这是使用 RDD API的解决方案,但是:

  • 它简洁得多
  • 更难以“生化”#34;对于动态数量的列
  • 可能表现更差

可能会稍微缩短实现,但不会简单得多:

val rdd: RDD[(String, Int, Int, Int)] = ...

val result: RDD[(String, (Double, Double, Double))] = rdd
  .keyBy(_._1)
  .mapValues { case (k, v1, v2, v3) => (1, v1, v2, v3) } // add base for counter
  .reduceByKey { case ((a1, a2, a3, a4), (b1, b2, b3, b4)) => (a1+b1, a2+b2, a3+b3, a4+b4) } // sum counter and values
  .mapValues { case (count, v1, v2, v3) => (v1.toDouble/count, v2.toDouble/count, v3.toDouble/count) } // calculate means