Spark RDD 函数大全(21-30)

  1. zip
    defzipU(implicitarg0:ClassTag[U]):RDD[(T,U)]
    zip 函数用于将两个 RDD 组合成 Key/Value 形式的 RDD,这里默认两个 RDD 的 partition 数量以及元素数量都相同,否则会抛出异常。
    scala>varrdd1=sc.makeRDD(1to5,2) rdd1:org.apache.spark.rdd.RDD[Int]=ParallelCollectionRDD[1]atmakeRDDat:21
    scala>varrdd2=sc.makeRDD(Seq("A","B","C","D","E"),2) rdd2: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[2] at makeRDD at:21
    scala>rdd1.zip(rdd2).collect res0:Array[(Int,String)]=Array((1,A),(2,B),(3,C),(4,D),(5,E))
    scala>rdd2.zip(rdd1).collect res1:Array[(String,Int)]=Array((A,1),(B,2),(C,3),(D,4),(E,5))
    scala>varrdd3=sc.makeRDD(Seq("A","B","C","D","E"),3) rdd3: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[5] at makeRDD at:21 scala>rdd1.zip(rdd3).collect java.lang.IllegalArgumentException: Can't zip RDDs with unequal numbers of partitions //如果两个 RDD 分区数不同,则抛出异常
  2. zipPartitions
    zipPartitions 函数将多个 RDD 按照 partition 组合成为新的 RDD,该函数需要组合
    的 RDD 具有相同的分区数,但对于每个分区内的元素数量没有要求。 该函数有好几种实现,可分为三类:
    参数是一个 RDD
    def zipPartitions[B, V](rdd2: RDD[B])(f: (Iterator[T], Iterator[B]) => Iterator[V])(implicit arg0: ClassTag[B],arg1:ClassTag[V]):RDD[V]
    def zipPartitions[B, V](rdd2: RDD[B], preservesPartitioning: Boolean)(f: (Iterator[T], Iterator[B]) => Iterator[V])(implicitarg0:ClassTag[B],arg1:ClassTag[V]):RDD[V]
    这两个区别就是参数 preservesPartitioning,是否保留父 RDD 的 partitioner 分区信息
    映射方法 f 参数为两个 RDD 的迭代器。
    scala>varrdd1=sc.makeRDD(1to5,2) rdd1:org.apache.spark.rdd.RDD[Int]=ParallelCollectionRDD[22]atmakeRDDat:21
    scala>varrdd2=sc.makeRDD(Seq("A","B","C","D","E"),2) rdd2:org.apache.spark.rdd.RDD[String]=ParallelCollectionRDD[23]atmakeRDDat:21
    //rdd1 两个分区中元素分布: scala>rdd1.mapPartitionsWithIndex{ | (x,iter)=>{ | varresult=ListString | while(iter.hasNext){ | result::=("part_"+x+"|"+iter.next()) | } | result.iterator | | } | }.collect res17:Array[String]=Array(part_0|2,part_0|1,part_1|5,part_1|4,part_1|3)
    //rdd2 两个分区中元素分布 scala>rdd2.mapPartitionsWithIndex{ | (x,iter)=>{
    | varresult=ListString | while(iter.hasNext){ | result::=("part_"+x+"|"+iter.next()) | } | result.iterator | | } | }.collect res18:Array[String]=Array(part_0|B,part_0|A,part_1|E,part_1|D,part_1|C)
    //rdd1 和 rdd2 做 zipPartition scala>rdd1.zipPartitions(rdd2){ | (rdd1Iter,rdd2Iter)=>{ | varresult=ListString | while(rdd1Iter.hasNext&&rdd2Iter.hasNext){ | result::=(rdd1Iter.next()+""+rdd2Iter.next()) | } | result.iterator | } | }.collect res19:Array[String]=Array(2_B,1_A,5_E,4_D,3_C)
    参数是两个 RDD
    def zipPartitions[B, C, V](rdd2: RDD[B], rdd3: RDD[C])(f: (Iterator[T], Iterator[B], Iterator[C]) => Iterator[V])(implicitarg0:ClassTag[B],arg1:ClassTag[C],arg2:ClassTag[V]):RDD[V]
    def zipPartitions[B, C, V](rdd2: RDD[B], rdd3: RDD[C], preservesPartitioning: Boolean)(f: (Iterator[T], Iterator[B], Iterator[C]) => Iterator[V])(implicit arg0: ClassTag[B], arg1: ClassTag[C], arg2:ClassTag[V]):RDD[V]
    用法同上面,只不过该函数参数为两个 RDD,映射方法 f 输入参数为两个 RDD 的迭代器。
    scala>varrdd1=sc.makeRDD(1to5,2) rdd1:org.apache.spark.rdd.RDD[Int]=ParallelCollectionRDD[27]atmakeRDDat:21
    scala>varrdd2=sc.makeRDD(Seq("A","B","C","D","E"),2) rdd2:org.apache.spark.rdd.RDD[String]=ParallelCollectionRDD[28]atmakeRDDat:21
    scala>varrdd3=sc.makeRDD(Seq("a","b","c","d","e"),2) rdd3:org.apache.spark.rdd.RDD[String]=ParallelCollectionRDD[29]atmakeRDDat:21
    //rdd3 中个分区元素分布 scala>rdd3.mapPartitionsWithIndex{ | (x,iter)=>{ | varresult=ListString | while(iter.hasNext){ | result::=("part
    "+x+"|"+iter.next()) | } | result.iterator | | } | }.collect res21:Array[String]=Array(part_0|b,part_0|a,part_1|e,part_1|d,part_1|c)
    //三个 RDD 做 zipPartitions scala>varrdd4=rdd1.zipPartitions(rdd2,rdd3){ | (rdd1Iter,rdd2Iter,rdd3Iter)=>{ | varresult=ListString | while(rdd1Iter.hasNext&&rdd2Iter.hasNext&&rdd3Iter.hasNext){ | result::=(rdd1Iter.next()+""+rdd2Iter.next()+""+rdd3Iter.next()) | } | result.iterator | } | } rdd4:org.apache.spark.rdd.RDD[String]=ZippedPartitionsRDD3[33]atzipPartitionsat:27
    scala>rdd4.collect res23:Array[String]=Array(2_B_b,1_A_a,5_E_e,4_D_d,3_C_c)
    参数是三个 RDD
    def zipPartitions[B, C, D, V](rdd2: RDD[B], rdd3: RDD[C], rdd4: RDD[D])(f: (Iterator[T], Iterator[B],
    Iterator[C], Iterator[D]) => Iterator[V])(implicit arg0: ClassTag[B], arg1: ClassTag[C], arg2: ClassTag[D],arg3:ClassTag[V]):RDD[V]
    def zipPartitions[B, C, D, V](rdd2: RDD[B], rdd3: RDD[C], rdd4: RDD[D], preservesPartitioning: Boolean)(f: (Iterator[T], Iterator[B], Iterator[C], Iterator[D]) => Iterator[V])(implicit arg0: ClassTag[B],arg1:ClassTag[C],arg2:ClassTag[D],arg3:ClassTag[V]):RDD[V]
    用法同上面,只不过这里又多了个一个 RDD 而已。
  3. zipWithIndex
    defzipWithIndex():RDD[(T,Long)] 该函数将 RDD 中的元素和这个元素在 RDD 中的 ID(索引号)组合成键/值对。 scala>varrdd2=sc.makeRDD(Seq("A","B","R","D","F"),2) rdd2: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[34] at makeRDD at:21 scala>rdd2.zipWithIndex().collect res27:Array[(String,Long)]=Array((A,0),(B,1),(R,2),(D,3),(F,4))
  4. zipWithUniqueId
    defzipWithUniqueId():RDD[(T,Long)] 该函数将 RDD 中元素和一个唯一 ID 组合成键/值对,该唯一 ID 生成算法如下: 每个分区中第一个元素的唯一 ID 值为:该分区索引号, 每个分区中第 N 个元素的唯一 ID 值为: (前一个元素的唯一 ID 值)+(该 RDD 总的 分区数) 看下面的例子: scala>varrdd1=sc.makeRDD(Seq("A","B","C","D","E","F"),2) rdd1: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[44] at makeRDD at:21 //rdd1 有两个分区, scala>rdd1.zipWithUniqueId().collect res32:Array[(String,Long)]=Array((A,0),(B,2),(C,4),(D,1),(E,3),(F,5))
    //总分区数为 2 //第一个分区第一个元素 ID 为 0,第二个分区第一个元素 ID 为 1 //第一个分区第二个元素 ID 为 0+2=2,第一个分区第三个元素 ID 为 2+2=4 //第二个分区第二个元素 ID 为 1+2=3,第二个分区第三个元素 ID 为 3+2=5
    键值转换
  5. partitionBy
    defpartitionBy(partitioner:Partitioner):RDD[(K,V)] 该函数根据 partitioner 函数生成新的 ShuffleRDD,将原 RDD 重新分区。 scala>varrdd1=sc.makeRDD(Array((1,"A"),(2,"B"),(3,"C"),(4,"D")),2) rdd1:org.apache.spark.rdd.RDD[(Int,String)]=ParallelCollectionRDD[23]atmakeRDDat:21 scala>rdd1.partitions.size res20:Int=2
    //查看 rdd1 中每个分区的元素 scala>rdd1.mapPartitionsWithIndex{ | (partIdx,iter)=>{ | varpart_map=scala.collection.mutable.MapString,List[(Int,String)] | while(iter.hasNext){ | varpart_name="part_"+partIdx; | varelem=iter.next() | if(part_map.contains(part_name)){ | varelems=part_map(part_name) | elems::=elem | part_map(part_name)=elems | }else{ | part_map(part_name)=List[(Int,String)]{elem} | } | } | part_map.iterator | | } | }.collect res22:Array[(String,List[(Int,String)])]=Array((part_0,List((2,B),(1,A))),(part_1,List((4,D),(3,C))))
    //(2,B),(1,A)在 part_0 中,(4,D),(3,C)在 part_1 中
    //使用 partitionBy 重分区 scala>varrdd2=rdd1.partitionBy(neworg.apache.spark.HashPartitioner(2)) rdd2:org.apache.spark.rdd.RDD[(Int,String)]=ShuffledRDD[25]atpartitionByat:23
    scala>rdd2.partitions.size res23:Int=2
    //查看 rdd2 中每个分区的元素 scala>rdd2.mapPartitionsWithIndex{ | (partIdx,iter)=>{ | varpart_map=scala.collection.mutable.MapString,List[(Int,String)] | while(iter.hasNext){ | varpart_name="part_"+partIdx; | varelem=iter.next() | if(part_map.contains(part_name)){ | varelems=part_map(part_name) | elems::=elem | part_map(part_name)=elems | }else{ | part_map(part_name)=List[(Int,String)]{elem} | } | } | part_map.iterator | } | }.collect res24:Array[(String,List[(Int,String)])]=Array((part_0,List((4,D),(2,B))),(part_1,List((3,C),(1,A)))) //(4,D),(2,B)在 part_0 中,(3,C),(1,A)在 part_1 中
  6. mapValues
    mapValues 顾名思义就是输入函数应用于 RDD 中 Kev-Value 的 Value,原 RDD 中的 Key 保持 不变,与新的 Value 一起组成新的 RDD 中的元素。因此,该函数只适用于元素为 KV 对的 RDD。 举例: scala>vala=sc.parallelize(List("dog","tiger","lion","cat","panther","eagle"),2) scala>valb=a.map(x=>(x.length,x)) scala>b.mapValues("x"+_+"x").collect
    res5: Array[(Int, String)] = Array((3,xdogx), (5,xtigerx), (4,xlionx),(3,xcatx), (7,xpantherx), (5,xeaglex))
  7. flatMapValues
    flatMapValues 类似于 mapValues,不同的在于 flatMapValues 应用于元素为 KV 对的 RDD 中 Value。每个一元素的 Value 被输入函数映射为一系列的值,然后这些值再与原 RDD 中的 Key 组成一系列新的 KV 对。 举例 vala=sc.parallelize(List((1,2),(3,4),(5,6))) valb=a.flatMapValues(x=>1.to(x)) b.collect.foreach(println)
  8. 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,分区内相同的 key 做一次 mergeValue:合并值函数,将一个C类型和一个V类型值合并成一个C类型, 输入参数为(C,V), 输出为 C,分区内相同的 key 循环做 mergeCombiners:分区合并组合器函数,用于将两个 C 类型值合并成一个 C 类型,输入参数 为(C,C),输出为 C,分区之间循环做 numPartitions:结果 RDD 分区数,默认保持原有的分区数 partitioner:分区函数,默认为 HashPartitioner mapSideCombine:是否需要在 Map 端进行 combine 操作,类似于 MapReduce 中的 combine, 默认为 true
    看下面例子:
    scala>varrdd1=sc.makeRDD(Array(("A",1),("A",2),("B",1),("B",2),("C",1))) rdd1:org.apache.spark.rdd.RDD[(String,Int)]=ParallelCollectionRDD[64]atmakeRDDat:21 scala>rdd1.combineByKey( | (v:Int)=>v+"", | (c:String,v:Int)=>c+"@"+v, | (c1:String,c2:String)=>c1+""+c2 | ).collect res60:Array[(String,String)]=Array((A,2_1),(B,1_2_),(C,1_)) 其中三个映射函数分别为: createCombiner:(V)=>C (v:Int)=>v+ “_” //在每一个 V 值后面加上字符_,返回 C 类型(String) mergeValue:(C,V)=>C (c:String,v:Int)=>c+ “@” +v//合并 C 类型和 V 类型,中间加字符@,返回 C(String) mergeCombiners:(C,C)=>C (c1:String,c2:String)=>c1+ “” +c2//合并 C 类型和 C 类型,中间加$,返回 C(String) 其他参数为默认值。 最终,将 RDD[String,Int]转换为 RDD[String,String]。
    再看例子:
    rdd1.combineByKey( (v:Int)=>List(v), (c:List[Int],v:Int)=>v::c, (c1:List[Int],c2:List[Int])=>c1:::c2 ).collect res65:Array[(String,List[Int])]=Array((A,List(2,1)),(B,List(2,1)),(C,List(1))) 最终将 RDD[String,Int]转换为 RDD[String,List[Int]]。
  9. foldByKey
    deffoldByKey(zeroValue:V)(func:(V,V)=>V):RDD[(K,V)]
    deffoldByKey(zeroValue:V,numPartitions:Int)(func:(V,V)=>V):RDD[(K,V)]
    deffoldByKey(zeroValue:V,partitioner:Partitioner)(func:(V,V)=>V):RDD[(K,V)]
    该函数用于 RDD[K,V]根据 K 将 V 做折叠、合并处理,其中的参数 zeroValue 表示先根据映射 函数将 zeroValue 应用于 V,进行初始化 V,再将映射函数应用于初始化后的 V.
    例子:
    scala>varrdd1=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",00),("A",20), //即:("A",0),("A",0),再将映射函//数应用于初始化后的 V,最后得到:(A,00),即:(A,0) //其他 K 也一样,最终都得到了 V=0
    scala>rdd1.foldByKey(1)(_
    _).collect res78:Array[(String,Int)]=Array((A,0),(B,2),(C,1)) //映射函数为乘法时,需要将 zeroValue 设为 1,才能得到我们想要的结果。
    在使用 foldByKey 算子时候,要特别注意映射函数及 zeroValue 的取值。
  10. reduceByKeyLocally
    defreduceByKeyLocally(func:(V,V)=>V):Map[K,V]
    该函数将 RDD[K,V]中每个 K 对应的 V 值根据映射函数来运算,运算结果映射到一个 Map[K,V] 中,而不是 RDD[K,V]。
    scala>varrdd1=sc.makeRDD(Array(("A",0),("A",2),("B",1),("B",2),("C",1))) rdd1:org.apache.spark.rdd.RDD[(String,Int)]=ParallelCollectionRDD[91]atmakeRDDat:21
    scala>rdd1.reduceByKeyLocally((x,y)=>x+y) res90:scala.collection.Map[String,Int]=Map(B->3,A->2,C->1)

推荐阅读更多精彩内容