spark aggregateByKey與aggregate

一、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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章