spark@聚合操作

990 阅读1分钟

groupByKey

// 原始数据
val data = sc.parallelize(List(
  ("zhangsan", 123),
  ("zhangsan", 321),
  ("lisi", 96),
  ("lisi", 75),
  ("lisi", 23),
  ("wangwu", 99),
  ("wangwu", 91),
  ("wangwu", 92),
  ("wangwu", 94)
))

列转行

val groupByKey = data.groupByKey
// 结果
// (zhangsan,CompactBuffer(123, 321))
// (wangwu,CompactBuffer(99, 91, 92, 94))
// (lisi,CompactBuffer(96, 75, 23))

行转列

groupByKey.flatMap(e => {
  e._2.map((e._1, _))
}).foreach(println)

// 使用flatMapValues
// 这个转换算子 源码可看
groupByKey.flatMapValues(_.iterator).foreach(println)
  • mapValues 对每values进行map操作 并与key组成kv
  • flatMapValues 对values进行flatMap操作 并与key组成kv

max & min & sum & count

  • max spark没有提供封装好的算子,但是提供了reduceByKey min同理
// max
val nums = sc.parallelize(1 to 10)
nums
  .map((null, _))
  .reduceByKey((ov: Int, nv: Int) => {
    if (ov >= nv) ov else nv
  }).values.foreach(println)
  • sum也是通过reduceByKey
nums.map((null,_)).reduceByKey(_+_).foreach(println)
  • count也是
nums.map(_ =>(null,1)).reduceByKey(_+_).values.foreach(println)

avg

简单粗暴的做法

咱们已经会算sumcount了,做除法即可

// 如何算avg?
val sum = nums.map((null,_)).reduceByKey(_+_)
val count = nums.map(_ =>(null,1)).reduceByKey(_+_)
// 得让这俩相遇, 让其进行join
sum.join(count).mapValues(e => e._1/e._2).values.foreach(println)

看看成本

这种做法显然是不可取的。

combineByKey

combineByKey传入的三个函数的语义:

  • 第一个value进来要怎么处理
  • 第二个value进来 这时候和老得value 怎么一块处理(combine)
  • 两个combine结果如何 进行 combine
nums.map((null, _)).combineByKey(
  (v: Int) => (v, 1),
  (ov: (Int, Int), nv: Int) => (ov._1 + nv, ov._2 + 1),
  (c1: (Int, Int), c2: (Int, Int)) => (c1._1 + c2._1, c1._2 + c2._2)
)
  .mapValues(e => e._1 / e._2)
  .values
  .foreach(println)
  • 这样通过combineByKey 一次性将数据源 算出sum和conunt。并且进行一次shuffle操作就能得出avg