spark编程模型(十三)之RDD键值转换操作(Transformation Operation)——combineByKey、foldByKey

combineByKey()

  • def combineByKey[C](createCombiner: (V) => C, mergeValue: (C, V) => C, mergeCombiners: (C, C) => C): RDD[(K, C)]

  • def combineByKey[C](createCombiner: (V) => C, mergeValue: (C, V) => C, mergeCombiners: (C, C) => C, numPartitions: Int): RDD[(K, C)]

  • def combineByKey[C](createCombiner: (V) => C, mergeValue: (C, V) => C, mergeCombiners: (C, C) => C, partitioner: Partitioner, mapSideCombine: Boolean = true, serializer: Serializer = null): RDD[(K, C)]

  • 该函数用于将RDD[K,V]转换成RDD[K,C],这里的V类型和C类型可以相同也可以不同

参数 参数说明
createCombiner 组合器函数,用于将V类型转换成C类型,输入参数为RDD[K,V]中的V,输出为C
mergeValue 合并值函数,将一个C类型和一个V类型值合并成一个C类型,输入参数为(C,V),输出为C
mergeCombiners 合并组合器函数,用于将两个C类型值合并成一个C类型,输入参数为(C,C),输出为C
numPartitions 结果RDD分区数,默认保持原有的分区数
partitioner 分区函数,默认为HashPartitioner
mapSideCombine 是否需要在Map端进行combine操作,类似于MapReduce中的combine,默认为true
  • createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就
    和之前的某个元素的键相同。如果这是一个新的元素, combineByKey() 会使用一个叫作 createCombiner() 的函数来创建
    那个键对应的累加器的初始值

  • mergeValue: 如果这是一个在处理当前分区之前已经遇到的键, 它会使用 mergeValue() 方法将该键的累加器对应的当前值与这个新的值进行合并

  • mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更
    多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并

      scala> var rdd1 = sc.makeRDD(Array(("A",1),("A",2),("B",1),("B",2),("C",1)))
      rdd1: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[54] at makeRDD at <console>:26
    
      scala> rdd1.partitions.size
      res40: Int = 1
          
      scala> rdd1.combineByKey(
               | (v) => (v + "_"),
               | (c:String, v:Int) => (c + "@" + v),
               | (c1: String, c2 : String) => (c1 + "$" + c2)).collect
      res41: Array[(String, String)] = Array((B,1_@2), (A,1_@2), (C,1_))
    
      scala> var rdd1 = sc.makeRDD(Array(("A",1),("A",2),("C", 3),("B",1),("B",2),("C",1)), 2)
      rdd1: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[60] at makeRDD at <console>:26
    
      scala> rdd1.partitions.size
      res46: Int = 2
    
      scala> rdd1.glom().collect
      res47: Array[Array[(String, Int)]] = Array(Array((A,1), (A,2), (C,3)), Array((B,1), (B,2), (C,1)))
    
      scala> rdd1.combineByKey(
          | (v) => (v + "_"),
          | (c:String, v:Int) => (c + "@" + v),
          | (c1: String, c2 : String) => (c1 + "$" + c2)).collect
      res48: Array[(String, String)] = Array((B,1_@2), (A,1_@2), (C,3_$1_))
    

foldByKey

  • def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]

  • def foldByKey(zeroValue: V, numPartitions: Int)(func: (V, V) => V): RDD[(K, V)]

  • def foldByKey(zeroValue: V, partitioner: Partitioner)(func: (V, V) => V): RDD[(K, V)]

  • 该函数用于RDD[K,V]根据K将V做折叠、合并处理,其中的参数zeroValue表示先根据映射函数将zeroValue应用于V,进行初始化V,再将映射函数应用于初始化后的V

  • 在使用foldByKey算子时候,要特别注意映射函数及zeroValue的取值

      scala> var rdd1 = sc.makeRDD(Array(("A",0),("A",2),("B",1),("B",2),("C",1)))
      
      scala> rdd1.foldByKey(0)(_+_).collect
      res75: Array[(String, Int)] = Array((A,2), (B,3), (C,1)) 
      //将rdd1中每个key对应的V进行累加,注意zeroValue=0,需要先初始化V,映射函数为+操
      //作,比如("A",0), ("A",2),先将zeroValue应用于每个V,得到:("A",0+0), ("A",2+0),即:
      //("A",0), ("A",2),再将映射函数应用于初始化后的V,最后得到(A,0+2),即(A,2)
      
      scala> rdd1.foldByKey(2)(_+_).collect
      res76: Array[(String, Int)] = Array((A,6), (B,7), (C,3))
      //先将zeroValue=2应用于每个V,得到:("A",0+2), ("A",2+2),即:("A",2), ("A",4),再将映射函
      //数应用于初始化后的V,最后得到:(A,2+4),即:(A,6)
      
      scala> rdd1.foldByKey(0)(_*_).collect
      res77: Array[(String, Int)] = Array((A,0), (B,0), (C,0))
      //先将zeroValue=0应用于每个V,注意,这次映射函数为乘法,得到:("A",0*0), ("A",2*0),
      //即:("A",0), ("A",0),再将映射函//数应用于初始化后的V,最后得到:(A,0*0),即:(A,0)
      //其他K也一样,最终都得到了V=0
       
      scala> rdd1.foldByKey(1)(_*_).collect
      res78: Array[(String, Int)] = Array((A,0), (B,2), (C,1))
      //映射函数为乘法时,需要将zeroValue设为1,才能得到我们想要的结果。
    
posted @ 2018-08-11 01:25  oldsix666  阅读(107)  评论(0编辑  收藏  举报