val conf = new SparkConf()
.setMaster("local")
.setAppName("CbkDemo")
val sc = new SparkContext(conf)
sc.setLogLevel("error")
val rdd: RDD[(String, Double)] = sc.parallelize(
Array(("George", 88.0), ("George", 95.0), ("George", 88.0),("KangKang", 93.0),
("KangKang", 95.0), ("KangKang", 98.0),("limu", 98.0)))
// val rdd2: RDD[(String, Double)] = rdd.coalesce(3)
//求和
/**
* createCombiner: V => C ,这个函数把当前的值作为参数,此时我们可以对其做些附加操作(类型转换)并把它返回 (这一步类似于初始化操作)
* mergeValue: (C, V) => C,该函数把元素V合并到之前的元素C(createCombiner)上 (这个操作在每个分区内进行)
* mergeCombiners: (C, C) => C,该函数把2个元素C合并 (这个操作在不同分区间进行)
*/
val res = rdd.combineByKey(
x => {println(s"$x******");x},
(x: Double, y: Double) => {println(s"$x%%%%%%$y");x+y},
(x: Double, y: Double) => {println(s"$x@@@@@@$y");x+y}
)
res.foreach(println)
输出结果:
88.0******
88.0%%%%%%95.0
183.0%%%%%%88.0
93.0******
93.0%%%%%%95.0
188.0%%%%%%98.0
98.0******
(George,271.0)
(KangKang,286.0)
(limu,98.0)
图示:
那么怎么走第三部呢?
mergeCombiners: (C, C) => C,该函数把2个元素C合并 (这个操作在不同分区间进行)
val conf = new SparkConf()
.setMaster("local")
.setAppName("CbkDemo")
val sc = new SparkContext(conf)
sc.setLogLevel("error")
val rdd: RDD[(String, Double)] = sc.parallelize(
Array(("George", 88.0), ("George", 95.0), ("George", 88.0),
("George", 88.0), ("George", 95.0), ("George", 88.0),
("George", 88.0), ("George", 95.0), ("George", 88.0),
("George", 88.0), ("George", 95.0), ("George", 88.0),("KangKang", 93.0),
("KangKang", 95.0), ("KangKang", 98.0),("limu", 98.0)),3)
// val rdd2: RDD[(String, Double)] = rdd.coalesce(3)
//求和
/**
* createCombiner: V => C ,这个函数把当前的值作为参数,此时我们可以对其做些附加操作(类型转换)并把它返回 (这一步类似于初始化操作)
* mergeValue: (C, V) => C,该函数把元素V合并到之前的元素C(createCombiner)上 (这个操作在每个分区内进行)
* mergeCombiners: (C, C) => C,该函数把2个元素C合并 (这个操作在不同分区间进行)
*/
val res = rdd.combineByKey(
x => {println(s"$x******");x},
(x: Double, y: Double) => {println(s"$x%%%%%%$y");x+y},
(x: Double, y: Double) => {println(s"$x@@@@@@$y");x+y}
)
res.foreach(println)
结果展示:
88.0******
88.0%%%%%%95.0
183.0%%%%%%88.0
271.0%%%%%%88.0
359.0%%%%%%95.0
88.0******
88.0%%%%%%88.0
176.0%%%%%%95.0
271.0%%%%%%88.0
359.0%%%%%%88.0
95.0******
95.0%%%%%%88.0
93.0******
93.0%%%%%%95.0
188.0%%%%%%98.0
98.0******
454.0@@@@@@447.0
901.0@@@@@@183.0
(George,1084.0)
(limu,98.0)
(KangKang,286.0)
图示:
方法的第一个操作在相同分区相同key的时候只操作一次,然后一直进行第二个操作,如果不同分区中有相同的key值则进行第三步操作,否则不执行第三步操作。【因为第二步操作已经把结果算出来了】
友情提示:之所以我们的输出第二步操作时没有输出最终结果,原因在于,为了返回值。我们把输出语句放在了前面,也就是说输出语句后,还有一步加的操作。
val conf = new SparkConf()
.setMaster("local")
.setAppName("CbkDemo")
val sc = new SparkContext(conf)
sc.setLogLevel("error")
val rdd: RDD[(String, Double)] = sc.parallelize(
Array(("George", 88.0), ("George", 95.0), ("George", 88.0),
("George", 88.0), ("George", 95.0), ("George", 88.0),
("George", 88.0), ("George", 95.0), ("George", 88.0),
("George", 88.0), ("George", 95.0), ("George", 88.0),("KangKang", 93.0),
("KangKang", 95.0), ("KangKang", 98.0),("limu", 98.0)),3)
//求平均数
val res: RDD[(String, (Int, Double))] = rdd.combineByKey(
score => (1, score),
(total: (Int, Double), newScore) => (total._1 + 1, total._2 + newScore),
(total: (Int, Double), sum: (Int, Double)) => (total._1 + sum._1, total._2 + sum._2)
)
val fin: RDD[(String, Double)] = res.map{case (name,(num,score)) => (name,score/num)}
fin.foreach(println)
输出结果:
(George,90.33333333333333)
(limu,98.0)
(KangKang,95.33333333333333)
combineByKey
是针对不同partition进行操作的。它的第一个参数用于数据初始化(后面着重讲),第二个是针对一个partition的combine操作函数,第三个是在所有partition都combine完毕后,针对所有临时结果进行combine操作的函数。
友情补充:
关于数据初始化
之前有人说,初始化是对每个数据进行操作,这其实是错误的。应该是针对每个partition中,每个key下的第一个数据进行操作。这句话怎么理解呢?看代码:
val rdd1 = sc.parallelize(List(1,2,2,3,3,3,3,4,4,4,4,4), 2)
val rdd2 = rdd1.map((_, 1))
val rdd3 = rdd2.combineByKey(-_, (x:Int, y:Int) => x + y, (x:Int, y:Int) => x + y)
val rdd4 = rdd2.combineByKey(+_, (x:Int, y:Int) => x + y, (x:Int, y:Int) => x + y)
rdd2.collect
rdd3.collect
rdd4.collect
Array((1,1), (2,1), (2,1), (3,1), (3,1), (3,1), (3,1), (4,1), (4,1), (4,1), (4,1), (4,1))
Array((4,3), (2,0), (1,-1), (3,0))
Array((4,5), (2,2), (1,1), (3,4))
在上述代码中,(1,1), (2,1), (2,1), (3,1), (3,1), (3,1) 被划分到第一个partition,(3,1), (4,1), (4,1), (4,1), (4,1), (4,1) 被划分到第二个。于是有如下操作:
(1, 1):由于只有1个,所以在值取负的情况下,自然输出(1, -1)
(2, 1):由于有2个,第一个取负,第二个不变,因此combine后为(2, 0)
(3, 1):partition1中有3个,参照上述规则,combine后为(3, 1),partition2中有1个,因此combine后为(3, -1)。在第二次combine时,不会有初始化操作,因此直接相加,结果为(3, 0)
(4, 1):过程同上,结果为(4, 3)