一、aggregateByKey
学过hadoop的话其实就很好理解aggregateByKey了。
def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U,combOp: (U, U) => U): RDD[(K, U)] = self.withScope
首先,这个函数是处于数据类型为key-value形式的时候才有的。
然后该函数是柯里化函数,有两个括号的参数:
1、第一个括号是:zeroValue,表示初始值,第二个括号的第一个参数(分区内reduce)会用到,而第二个参数(分区间reduce)不会用到初始值
2、第二个括号:
(1)第一个参数:相当于hadoop mapreduce中mapper中的combiner。不同分区(相当于mapreduce的不同map)中的每个分区的数据都各自按key分组,然后key相同的数据列表使用该第一个参数进行执行scala的fold操作。
(2)第二个参数:相当于hadoop mapreduce中的reducer。不同的分区经过第一个参数的之后,每个分区的数据合并为一个新的列表。。再使用第二个参数做reduceByKey操作。。
下面是我自己根据aggregateByKey原理封装的代码与原版aggregateByKey的对比:
def main(args: Array[String]): Unit = {
val sc = new SparkContext(new SparkConf().setMaster("local[*]").setAppName("WC"))
val sourceDataRdd = sc.parallelize(List("a" -> 1, "a" -> 2, "b" -> 3, "z" -> 4, "f" -> 6))
//初始值(aggregateByKey的第一个括号的参数)
val zeroValue = 10
//aggregateByKey第二个括号的第一个参数
val firstFunc = (left: Int, right: Int) => left + right
//aggregateByKey第二个括号的第二个参数
val secondFunc = (left: Int, right: Int) => left + right
//使用aggregateByKey
val result1 = sourceDataRdd
.aggregateByKey(zeroValue)(firstFunc, secondFunc)
.collect()
.mkString(",")
//根据原理自己封装的aggregateByKey
val result2 = sourceDataRdd
.mapPartitions(
_.toList.groupBy(_._1) //根据key分组
.mapValues(_.map(_._2)) // values由List[(String,Int)] 转为 List[Int]
.mapValues(_.fold(zeroValue)(firstFunc)) //使用初始值进行fold
.toIterator
)
.reduceByKey(secondFunc)
.collect()
.mkString(",")
println(result1)
println(result2)
sc.stop()
}
执行输出结果如下:(分区数和我不一样的话输出顺序也不一样。)
(f,16),(a,23),(z,14),(b,13)
(f,16),(a,23),(z,14),(b,13)
二、aggregate
参数与aggregateByKey一样。。
1、第一个括号是初始值。
2、第二个括号
(1)第一个参数:对分区内的所有数据(一个list)进行fold操作(初始值就是第一个括号的参数),最终一个分区的结果只会有一个值。
(2)第二个参数:N个分区经过第一个参数之后得到N个值(一个新的list),然后根据这N个值再次进行fold操作(初始值再次被用上),初始值在分区内与分区间都会用上,这是与aggregateByKey的一个不同点,aggregateByKey在分区间时不会使用初始值。
执行输出结果如下:
def main(args: Array[String]): Unit = {
val sc = new SparkContext(new SparkConf().setMaster("local[*]").setAppName("WC"))
val sourceDataRdd = sc.parallelize(List(1, 2, 3, 5, 67, 123))
//初始值(aggregateByKey的第一个括号的参数)
val zeroValue = 10
//aggregateByKey第二个括号的第一个参数
val firstFunc = (left: Int, right: Int) => left + right
//aggregateByKey第二个括号的第二个参数
val secondFunc = (left: Int, right: Int) => left + right
//使用aggregateByKey
val result1 = sourceDataRdd
.aggregate(zeroValue)(firstFunc, secondFunc)
//根据原理自己封装的aggregateByKey
val result2 = sourceDataRdd
.mapPartitions(iterator => List(iterator.fold(zeroValue)(firstFunc)).toIterator)
.collect() //转为scala的集合再使用fold而不是使用rdd本身的fold(因为误会)
.fold(zeroValue)(secondFunc)
println(result1)
println(result2)
sc.stop()
}
输出结果:、
271
271