[Spark RDD转换操作运算子]

Spark RDD转换操作

RDD,即弹性分布式数据集,全称为Resilient Distributed Dataset,是一个容错的,并行的数据结构,可以让用户显式地 将数据存储到磁盘和内存中,并能控制数据的分区。同时,RDD还提供了一组非常丰富的操作来操作这些数据,如:map,flatMap,filter等转换操作,以及SaveAsTextFile,conutByKey等行动操作。
本博客中将详细讲解RDD的转换操作的各种运算方法。

RDD转换操作运算子

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)

flatMap

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)=> Iterator类型。

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))

aggregateByKey

aggregateByKey(zeroValue, numPartitions)(seqOp: (U, V) => U,combOp: (U, U) => U)和reduceByKey的不同在于:reduceByKey输入/输出都是(K,V),而aggregateByKey输出是(K,U),可以不同于输入(K,U)。
        aggregateByKey的三个参数如下:
        zeroValue:U,初始值,比如初始值为0或空列表。
        numPartitions:指定并行任务的分区数。
        seqOp:(U,V)=>U,seq操作符,描述如何将T合并入U,比如如何将item合并到列表中。
        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的三个参数如下:
    createCombiner:在遍历(K,V)是,如果combineByKey的第一次遇到值为K的key(类型K),那么将对这个(K,V)调用createCombiner函数,将V转换为c(类型C,聚合对象的类型)。
       mergeValue:在遍历(k,v)是,如果comineByKey不是第一次遇到值为k的key(类型为K),那么将对这个(k,v)调用mergeValue函数,它的作用是将v累加到聚合对象(类型C)中,mergeValue的类型是(C,V)=>C,参数中的C遍历到此处的聚合对象,然后对v进行聚合得到新的聚合对象值。
       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)))

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))

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)))

cogroup

cogroup(other: RDD[(K, W)], partitioner: Partitioner)是将RDD中的输入数据集(K,V)和另外一个数据集(K,W)进行cogroup,得到一个格式为(K,Seq[V],Seq[W])的数据集

val rdd17 = rddMap.cogroup(rddMap1)
printResult("cogroup", rdd17)
结果:cogroup >> List((0,(CompactBuffer(3, 6, 9),CompactBuffer(4, 8))), (2,(CompactBuffer(2, 5, 8),CompactBuffer(2, 6))), (1,(CompactBuffer(1, 4, 7),CompactBuffer(1, 5, 9))), (3,(CompactBuffer(),CompactBuffer(3, 7))))

cartesian

cartesian(other: RDD[U])是对两个数据集T和U进行笛卡尔积操作,得到(T,U)格式的数据集。

val rdd18 = rddMap.cartesian(rddMap1)
printResult("cartesian", rdd18)
结果:cartesian >> List(((1,1),(1,1)), ((1,1),(2,2)), ((1,1),(3,3)), ((1,1),(0,4)), ((2,2),
(1,1)), ((2,2),(2,2)), ((2,2),(3,3)), ((2,2),(0,4)), ((0,3),(1,1)), ((0,3),(2,2)), ((0,3),
(3,3)), ((0,3),(0,4)), ((1,4),(1,1)), ((1,4),(2,2)), ((1,4),(3,3)), ((1,4),(0,4)), ((1,1),
(1,5)), ((1,1),(2,6)), ((1,1),(3,7)), ((1,1),(0,8)), ((1,1),(1,9)), ((2,2),(1,5)), ((2,2),
(2,6)), ((2,2),(3,7)), ((2,2),(0,8)), ((2,2),(1,9)), ((0,3),(1,5)), ((0,3),(2,6)), ((0,3),
(3,7)), ((0,3),(0,8)), ((0,3),(1,9)), ((1,4),(1,5)), ((1,4),(2,6)), ((1,4),(3,7)), ((1,4),
(0,8)), ((1,4),(1,9)), ((2,5),(1,1)), ((2,5),(2,2)), ((2,5),(3,3)), ((2,5),(0,4)), ((0,6),
(1,1)), ((0,6),(2,2)), ((0,6),(3,3)), ((0,6),(0,4)), ((1,7),(1,1)), ((1,7),(2,2)), ((1,7),
(3,3)), ((1,7),(0,4)), ((2,8),(1,1)), ((2,8),(2,2)), ((2,8),(3,3)), ((2,8),(0,4)), ((0,9),
(1,1)), ((0,9),(2,2)), ((0,9),(3,3)), ((0,9),(0,4)), ((2,5),(1,5)), ((2,5),(2,6)), ((2,5),
(3,7)), ((2,5),(0,8)), ((2,5),(1,9)), ((0,6),(1,5)), ((0,6),(2,6)), ((0,6),(3,7)), ((0,6),
(0,8)), ((0,6),(1,9)), ((1,7),(1,5)), ((1,7),(2,6)), ((1,7),(3,7)), ((1,7),(0,8)), ((1,7),
(1,9)), ((2,8),(1,5)), ((2,8),(2,6)), ((2,8),(3,7)), ((2,8),(0,8)), ((2,8),(1,9)), ((0,9),
(1,5)), ((0,9),(2,6)), ((0,9),(3,7)), ((0,9),(0,8)), ((0,9),(1,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)

randomSplit

randomSplit(weights: Array[Double],seed: Long = Utils.random.nextLong)是对RDD按照权重进行数据分割,第一个参数为分割权重数组,第二参数为随机种子,该方法通常用于样本的划分。

val rdd20: Array[RDD[Int]] = rdd1.randomSplit(Array(0.3, 0.7), 1)
rdd20.foreach(rdd => {
     printResult("randomSplit", rdd)
})
结果:randomSplit >> List(1, 3, 4, 5, 6, 7, 9), List(2, 8)

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

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

val rdd23 = rdd1.coalesce(2)
printResult("coalesce", rdd23)
结果:coalesce >> List(1, 3, 4, 6, 7, 9, 2, 5, 8)

 repartition

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

val rdd24 = rdd1.repartition(2)
printResult("repartition", rdd24)
结果:repartition >> List(1, 3, 4, 6, 7, 9, 2, 5, 8)

treeAggregate

treeAggregate[U](zeroValue: U)(seqOp: (U, T) => U,combOp: (U, U) => U,depth: Int = 2)是采用多层树模式进行RDD的聚合操作,类似于aggregateByKey。

        treeAggregate的是三个参数说明如下:
        zeroValue:U,初始值,可以是0或空列表等。
        seqOp:(U,T)=>U,seq操作符,描述如何将T合并入U,比如将item合并到列表中。
        combOp: (U, U),comb操作符,描述如何合并两个U,比如将两个列表进行合并。
     

val rdd25: (Int, Int) = rddMap.treeAggregate(0, 0)(
           seqOp = (u, t) => {
                (u._1 + t._1, u._2 + t._2)
            },
           combOp = (u1, u2) => {
                (u1._1 + u2._1, u1._2 + u2._2)
            })
println("treeAggregate>> " + rdd25)
结果:treeAggregate>> (9,45)

你可能感兴趣的:(RDD,Spark,scala,大数据,Spark进阶专栏)