[scala-spark]10. RDD转换操作

RDD提供了一组非常丰富的操作来操作数据,如:map,flatMap,filter等转换操作,以及SaveAsTextFile,conutByKey等行动操作。这里仅仅综述了转换操作。

  • map

map是对RDD中的每一个元素都执行一个指定的函数来产生一个新的RDD,RDD之间的元素是一对一的关系。

val rdd1: RDD[Int] = sc.parallelize(1 to 9, 3)
val rdd2: RDD[Int] = rdd1.map(_ * 2)
printResult("map", rdd2)
// 结果:map >> List(2, 4, 6, 8, 10, 12, 14, 16, 18)
  • flapMap

flatMap类似于map,但是每一个输入元素,会被映射为0到多个出输出元素(即func函数的返回值是一个Seq,而不是单一元素)的新的RDD,RDD之间的元素是一对多关系。


val rdd3: RDD[Int] = rdd2.filter(x => x > 10).flatMap(x => x to 21)
printResult("flatMap", rdd3)
// 结果:flatMap >> List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 
14, 15, 16, 17, 18, 19, 20, 21, 
16, 17, 18, 19, 20, 21, 
18, 19, 20, 21)
  • filter

filter是对RDD元素进行过滤,返回一个新的数据集,有经过func函数后返回值为true的元素组成。

val rdd4 = rdd2.filter(x => x > 11)
printResult("filter", rdd4)
// 结果:filter >> List(12, 14, 16, 18)
  • mapPartitions

mapPartitions是map的一个变种。map的输入函数应用于RDD中的每一个元素,而mapPartitions的输入函数应用于每一个分区的数据,也就是把每一个分区中的内容作为整体来处理。

函数定义:

 def mapPartitions[U: ClassTag](f: Iterator[T] => Iterator[U],preservesPartitioning: Boolean = false): RDD[U]

val rdd5: RDD[(Int, Int)] = rdd1.repartition(2).mapPartitions((iter: Iterator[Int]) => {
val lst: ListBuffer[(Int, Int)] = new ListBuffer[(Int, Int)]()
var prev: Int = 0
var current: Int = 0
 while (iter.hasNext) {
     current = iter.next
     lst += ((prev, current))
     prev = current
     }
     lst.iterator
  })
printResult("mapPartitions", rdd5)
结果:mapPartitions >> List((0,1), (1,3), (3,5), (5,7), (7,9), (0,2), (2,4), (4,6), (6,8))
  • mapPartitionsWithIndex

mapPartitionsWithIndex于mapPartitions的功能类似,只是多传入split index而已,所有func函数必须是(Int,Iterator<T>)=> Iterator<U>类型。

val rdd6 = rdd1.repartition(2).mapPartitionsWithIndex((idx, iter) => {
val lst = new ListBuffer[String]()
var sum = 0
while (iter.hasNext) {
    sum += iter.next
   }
   lst += (idx + ":" + sum)
   lst.iterator
})
printResult("mapPartitionsWithIndex", rdd6)
// 结果:mapPartitionsWithIndex >> List(0:25, 1:20)
  • sample

sample(withReplacemet,fraction,seed)是根据给定的随机种子seed,随机抽样出数量为fraction的数据。其中,withReplacement:是否放回抽样;fraction:比抽样比例,0.1表示抽样10%,seed:随机种子,相同的seed得到的随机序列是一样的。所以,如果没有设置seed,同一段代码执行两遍得到的随机序列是一样的。
 


val sampleRDD = sc.parallelize(0 to 1000, 2)
val rdd7 = sampleRDD.sample(false, 0.02, 2)
printResult("sample", rdd7)
// 结果:sample >> List(10, 42, 110, 121, 145, 158, 166, 234, 237, 253, 266, 343, 354, 393, 404, 457, 460, 662, 728, 738, 806, 808, 868, 887, 889, 934, 952)
  • union

union(otherDataset)是数据的合并,返回一个新的数据集,由原数据集和otherDataset合并而成的一个数据集RDD。

val rdd8 = rdd1.union(rdd2)
printResult("union", rdd8)
// 结果:union >> List(1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 4, 6, 8, 10, 12, 14, 16, 18)
  • intersection

intersection(otherDataset)是数据交集,返回一个新的数据集,它是两个数据集的交集数据。

val rdd9 = rdd8.intersection(rdd1)
printResult("intersection", rdd9)
// 结果:intersection >> List(4, 8, 1, 9, 5, 6, 2, 3, 7)
  • distinct

distince(numPartitions)是对数据集进行去重,返回一个新的数据集,它是对两个数据集去掉重复数据后得到的一个数据集。其中,numPartitions参数是设置任务并行数量。


val rdd10 = rdd8.union(rdd9).distinct(2)
printResult("distinct", rdd10)
// 结果:distinct >> List(4, 16, 14, 6, 8, 12, 18, 10, 2, 1, 3, 7, 9, 5)
  • groupByKey

groupByKey(partitioner)是对数据进行分组操作,在一个由(K,V)对组成的数据集上调用,返回一个(K,Seq[V])对的数据集。注意,默认情况下,使用8个并行任务进行分组,可以传入partitioner参数设置并行任务的分区数.

val rddMap: RDD[(Int, Int)] = rdd1.map(item => (item % 3, item))
val rdd11 = rddMap.groupByKey();
printResult("groupByKey", rdd11)
// 结果:groupByKey >> List((0,CompactBuffer(3, 6, 9)), (2,CompactBuffer(2, 5, 8)), 
(1,CompactBuffer(1, 4, 7)))
  • reduceByKey

reduceByKey(func, numPartitions)是对数据进行分组聚合操作,在一个(K,V)对的数据集上使用,返回一个(K,V)对的数据集。Key是相同的值,都被使用指定的reduce函数聚合到一起。和groupByKey类似,可以通过参数numPartitions设置并行任务的分区数。

val rdd12 = rddMap.reduceByKey((x, y) => x + y)
printResult("reduceByKey", rdd12)
// 结果:reduceByKey >> List((0,18), (2,15), (1,12))
  • sortByKey

sortByKey(ascending, numPartitions)是对RDD中的数据集进行排序操作,对(K,V)类型的数据按照K进行排序,其中K需要实现Ordered方法。

第一个参数是ascending,该参数决定排序后RDD中的元素是升序还是降序,默认是true,按升序排序。
第二个参数是numPartitions,即排序分区的并行任务个数。

val rdd15 = rddMap.sortByKey(false)
printResult("sortByKey", rdd15)
// 结果:sortByKey >> List((2,2), (2,5), (2,8), (1,1), (1,4), (1,7), (0,3), (0,6), (0,9))
  • aggregateByKey

aggregateByKey(zeroValue, numPartitions)(seqOp: (U, V) => U,combOp: (U, U) => U)和reduceByKey的不同在于:reduceByKey输入/输出都是(K,V),而aggregateByKey输出是(K,U),可以不同于输入(K,U)。
ggregateByKey的三个参数如下:        

  1. zeroValue:U,初始值,比如初始值为0或空列表。        
  2. numPartitions:指定并行任务的分区数。        
  3. seqOp:(U,V)=>U,seq操作符,描述如何将T合并入U,比如如何将item合并到列表中。        
  4. combOp: (U, U) => U,comb操作符,描述如何合并两个U,比如合并两个列表。        

所以,可以将aggregateByKey函数抽象成更高级的,更灵活的reduce和group的组合。


val rdd13 = rddMap.aggregateByKey(0)(
    seqOp = (x, y) => {
        math.max(x, y)
    },
    combOp = (x, y) => {
        x + y
    })
printResult("aggregateByKey", rdd13)
// 结果:aggregateByKey >> List((0,12), (2,10), (1,11))
  • combineByKey

combineByKey(createCombiner: V => C,mergeValue: (C, V) => C,mergeCombiners: (C, C) => C,numPartitions: Int)是对RDD中的数据按照Key进行聚合操作。聚合操作的逻辑是通过自定义函数提供给combineByKey。 把(K,V)类型的RDD转换为(K,C)类型的RDD,C和Vk可以不一样。
combineyey的三个参数如下:  

  1.  createCombiner:在遍历(k,v)时,如果combineByKey的第一次遇到值为K的key(类型K),那么将对这个(K,V)调用createCombiner函数,将V转换为c(类型C,聚合对象的类型)。      
  2. mergeValue:在遍历(k,v)时,如果comineByKey不是第一次遇到值为k的key(类型为K),那么将对这个(k,v)调用mergeValue函数,它的作用是将v累加到聚合对象(类型C)中,mergeValue的类型是(C,V)=>C,参数中的C遍历到此处的聚合对象,然后对v进行聚合得到新的聚合对象值。      
  3. mergeCombiners:combineByKey实在分布式环境中执行的,RDD的每个分区单独进行combineBykey操作,最后需要对各个分区的结果进行最后的聚合。它的函数类型是(C,C)=>C,每个参数是分区聚合得到的聚合对象。 
val rdd14 = rdd1.map(item => (item % 4, item))
            .mapValues(v => v.toDouble)
            .combineByKey((v: Double) => (v, 1),
                (c: (Double, Int), v: Double) => (c._1 + v, c._2 + 1),
                (c1: (Double, Int), c2: (Double, Int)) => (c1._1 + c2._1, c1._2 + c2._2))
printResult("combineByKey", rdd14)
// 结果:combineByKey >> List((0,(12.0,2)), (2,(8.0,2)), (1,(15.0,3)), (3,(10.0,2)))
  • jion

join(other, partitioner)是连接操作,将数据的数据集(K,V)和另外一个数据集(K,W)进行join,得到(K,(V,W))。该操作是对于相同K的V和W集合进行笛卡尔积操作,也即V和W的所有组合。连接操作除了join外,还有左连接,右连接,全连接操作函数:leftOuterJoin,rightOuterJoin和fullOuterJoin,它们的用法基本上是一样的。

val rddMap1 = rdd1.map(item => (item % 4, item))
val rdd16 = rddMap.join(rddMap1)
printResult("join", rdd16)
// 结果:join >> List((0,(3,4)), (0,(3,8)), (0,(6,4)), (0,(6,8)), (0,(9,4)), (0,(9,8)), 
(2,(2,2)), (2,(2,6)), (2,(5,2)), (2,(5,6)), (2,(8,2)), (2,(8,6)), (1,(1,1)), (1,(1,5)), 
(1,(1,9)), (1,(4,1)), (1,(4,5)), (1,(4,9)), (1,(7,1)), (1,(7,5)), (1,(7,9)))
  • pipe

pipe(command: Seq[String],env: Map[String, String])是以shell命令处理RDD数据。


val rdd19: RDD[String] = rdd1.pipe("head -n 1")
printResult("pipe", rdd19)
// 结果:pipe >> List(1,4,7)
  • subtract

subtract(other: RDD[T], numPartitions: Int)是RDD对other数据集进行减法操作,将输入的rdd中的元素减去other中的元素,得到他们差值的一个新的RDD。


val rdd21 = rdd1.subtract(sc.parallelize(1 to 5))
printResult("subtract", rdd21)
// 结果:subtract >> List(6, 9, 7, 8)
  • zip

zip[U: ClassTag](other: RDD[U]): RDD[(T, U)]是对两个RDD进行拉链操作,得到一个新的RDD[T,U]。拉链操作还包括:zipWitIndex和zipPartitinos。

val rdd22 = rdd1.repartition(3).zip(sc.parallelize(Array("A", "B", "C", "D", "E", "F", "G", "H", "I"), 3))
printResult("zip", rdd22)
// 结果: zip >> List((3,A), (6,B), (8,C), (1,D), (4,E), (9,F), (2,G), (5,H), (7,I))

  • coalesce 和 repartition

coalesce(numPartitions: Int)是对RDD进行重分区,默认不进行shuffle,且该RDD的分区个数等于numPartitions个数。

repartition(numPartitions: Int)是将RDD进行重新分区,分区过程中会进行shuffle,调整分区数量为numPartitions。

发布了652 篇原创文章 · 获赞 1573 · 访问量 298万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503

分享到微信朋友圈

×

扫一扫,手机浏览