Spark 中的 Transformation 操作

创建 RDD(parallelize & makeRDD)

  • 创建一个新的 RDD
  • makeRDD 底层调用了 parallelize 方法,这两个操作实际上是相同的
  • parallelize 方法有两个入参 parallelize(seq, numSlices),seq指定一个数据集合,numSlices指定分区个数(多少个task来处理)
scala> sc.parallelize(List(1, 2, 3, 4, 5))
res2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[2] at parallelize at <console>:25

scala> sc.makeRDD(List(1, 2, 3, 4, 5))
res4: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[4] at makeRDD at <console>:25

map & mapPartitions & mapPartitionsWithIndex

  • map是对 RDD 中每个元素进行处理,mapPartitions 是对 RDD 中每个分区进行处理
  • 如果把 RDD 的数据写入 MySQL,假设 RDD 中有 100 个元素,划分了 10 个分区,map 需要拿到100个 Connection,而mapPartitions 只需要拿到 10 个 Connection
  • mapPartitionsWithIndex 与 mapPartitions 类似,但是可以拿到分区编号
scala> val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[4] at parallelize at <console>:24

scala> rdd1.map(_ * 2).collect
res6: Array[Int] = Array(2, 4, 6, 8, 10)

scala> rdd1.mapPartitions(partition => partition.map(_ * 2)).collect
res8: Array[Int] = Array(2, 4, 6, 8, 10)

scala> rdd1.mapPartitionsWithIndex((index, partition) => {
     |   partition.map(x => s"分区编号是$index 元素是$x")
     | }).collect
res1: Array[String] = Array(分区编号是0 元素是1, 分区编号是0 元素是2, 分区编号是1 元素是3, 分区编号是1 元素是4, 分区编号是1 元素是5)

mapValues

  • 针对 KV 类型的 RDD 中的 V 做处理
scala> val rdd2 = sc.parallelize(List(("aa", 18), ("bb", 23), ("cc", 5)))
rdd2: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[2] at parallelize at <console>:24

scala> rdd2.mapValues(_ + 1).collect
res2: Array[(String, Int)] = Array((aa,19), (bb,24), (cc,6))

flatMap

  • flatMap = map + flatten,在 scala 和 Spark 中的操作是相同的
  • flatten 是 scala 中对于 Array 类型的一个操作(不是 Spark 中的算子),可以通过拼接原数组的所有行元素来将一个二维数组“变平”
# scala 中的 map + flatten
scala> val f = Array(Array(1, 2), Array(3, 4), Array(5, 6))
f: Array[Array[Int]] = Array(Array(1, 2), Array(3, 4), Array(5, 6))

scala> val m = f.map(_.map(_ * 2))
m: Array[Array[Int]] = Array(Array(2, 4), Array(6, 8), Array(10, 12))

scala> m.flatten
res0: Array[Int] = Array(2, 4, 6, 8, 10, 12)
# scala 中的 flatMap
scala> val f = Array(Array(1, 2), Array(3, 4), Array(5, 6))
f: Array[Array[Int]] = Array(Array(1, 2), Array(3, 4), Array(5, 6))

scala> f.flatMap(_.map(_ * 2))
res8: Array[Int] = Array(2, 4, 6, 8, 10, 12)
# Spark 中的 flatMap
scala> sc.parallelize(List(List(1, 2), List(3, 4))).flatMap(_.map(_ * 2)).collect
res7: Array[Int] = Array(2, 4, 6, 8)

scala> sc.parallelize(1 to 5).flatMap(1 to _).collect
res9: Array[Int] = Array(1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5)

glom

  • 把每个分区中的数据放入一个数组,返回一个大的数组包含这些数组
scala> sc.parallelize(1 to 30, 5).glom().collect
res11: Array[Array[Int]] = Array(Array(1, 2, 3, 4, 5, 6), Array(7, 8, 9, 10, 11, 12), Array(13, 14, 15, 16, 17, 18), Array(19, 20, 21, 22, 23, 24), Array(25, 26, 27, 28, 29,30))

sample

  • 根据给定的随机种子,从 RDD 中随机地按指定比例选取一部分记录
  • sample(withReplacement: Boolean, fraction: Double, seed: Long = Utils.random.nextLong)
    • 第一个参数标识是否放回,即一个元素是否可以被多次抽取
    • 第二个参数表示抽取的元素占 RDD 中所有元素的比例(不保证提供确切个数)
    • 第三个参数表示生成随机数的随机种子
scala> sc.parallelize(1 to 30).sample(false, 0.1, 6).collect
res23: Array[Int] = Array(25)

filter

  • 过滤 RDD 中的元素,只留下满足条件的元素
scala> sc.parallelize(1 to 30).filter(x => x > 20 && x % 2 == 0).collect
res25: Array[Int] = Array(22, 24, 26, 28, 30)

zipWithIndex

  • 将 RDD[T] 转换为 RDD[(T, Long)],其中Long为编号,编号规则为首先按照分区,再按照每个分区内部元素的排序编号
scala> val rdd1 = sc.parallelize(List(6, 10, 8, 9, 20, 15))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[1] at parallelize at <console>:24

scala> rdd1.zipWithIndex.collect
res1: Array[(Int, Long)] = Array((6,0), (10,1), (8,2), (9,3), (20,4), (15,5))

union & intersection & subtract

  • union 表示将两个 RDD 中的元素放在一起,不去重
  • intersection 表示求两个 RDD 中元素的交集
  • subtract 表示求两个 RDD 中元素的差集
scala> val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[49] at parallelize at <console>:24

scala> val rdd2 = sc.parallelize(List(3, 4, 5, 6, 7, 8, 8))
rdd2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[50] at parallelize at <console>:24

scala> rdd1.union(rdd2).collect
res26: Array[Int] = Array(1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 7, 8, 8)

scala> rdd1.intersection(rdd2).collect
res27: Array[Int] = Array(4, 6, 3, 5)

scala> rdd1.subtract(rdd2).collect
res28: Array[Int] = Array(2, 1)

distinct

  • 去重,可以传入分区个数
  • 元素分到哪个分区是按照 值 % 分区个数 来确定的
    • 4 % 4 = 0,8 % 4 = 0 所以他们分到了 0 号分区,剩下的以此类推
scala> val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[71] at parallelize at <console>:24

scala> rdd1.distinct(4).mapPartitionsWithIndex((index, partition) => {
     |   partition.map(x => println(s"分区是$index 元素是$x"))
     | }).collect
分区是1 元素是1
分区是1 元素是9
分区是1 元素是5
分区是0 元素是4
分区是0 元素是8
分区是2 元素是6
分区是2 元素是10
分区是2 元素是2
分区是3 元素是3
分区是3 元素是7

groupByKey & reduceByKey

  • def groupByKey(): RDD[(K, scala.Iterable[V])]
    • 没有入参,会将相同 Key 的 Value 放到一个集合中
scala> val rdd1 = sc.parallelize(List(("a", 1), ("b", 2), ("c", 3), ("a", 99)))
rdd1: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[85] at parallelize at <console>:24

scala> rdd1.groupByKey().collect
res37: Array[(String, Iterable[Int])] = Array((b,CompactBuffer(2)), (a,CompactBuffer(1, 99)), (c,CompactBuffer(3)))

scala> rdd1.groupByKey().mapValues(_.sum).collect
res38: Array[(String, Int)] = Array((b,2), (a,100), (c,3))
  • def reduceByKey(func: (V, V) => V): RDD[(K, V)]
    • 入参为处理相同 Key 的 Value 的函数
scala> rdd1.reduceByKey(_ + _).collect
res39: Array[(String, Int)] = Array((b,2), (a,100), (c,3))
  • 不使用 distinct,如何去重?
    • 将每一个元素映射为(x, null),然后使用reduceByKey取一个null,然后使用map取tuple中_1的元素即可
scala> val rdd1 = sc.parallelize(List(3, 4, 5, 15, 4, 10, 5, 6, 7, 1, 3, 4))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[90] at parallelize at <console>:24

scala> rdd1.map((_, null)).reduceByKey((x, y) => x).map(_._1).collect
res41: Array[Int] = Array(4, 6, 10, 15, 1, 3, 7, 5)

# distinct源码中的实现
 /**
  * Return a new RDD containing the distinct elements in this RDD.
  */
 def distinct(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
   map(x => (x, null)).reduceByKey((x, y) => x, numPartitions).map(_._1)
 }

groupBy

  • 自定义分组
scala> sc.parallelize(List("a", "a", "a", "b", "b", "c")).groupBy(x => x).collect
res42: Array[(String, Iterable[String])] = Array((b,CompactBuffer(b, b)), (a,CompactBuffer(a, a, a)), (c,CompactBuffer(c)))

sortBy & sortByKey

  • sortBy 是自定义排序,默认升序,如果想按照降序排列可以传入 false,或者在 sort 条件前加 -,底层调用了 sortByKey
scala> val rdd1 = sc.parallelize(List(("a", 18), ("b", 23), ("c", 5)))
rdd1: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[153] at parallelize at <console>:24

scala> rdd1.sortBy(_._2).collect
res53: Array[(String, Int)] = Array((c,5), (a,18), (b,23))

scala> rdd1.sortBy(_._2, false).collect
res54: Array[(String, Int)] = Array((b,23), (a,18), (c,5))

scala> rdd1.sortBy(-_._2).collect
res55: Array[(String, Int)] = Array((b,23), (a,18), (c,5))
  • sortByKey 是按照 key 进行排序
scala> rdd1.map(x => (x._2, x._1)).sortByKey().map(x => (x._1, x._2)).collect
res56: Array[(Int, String)] = Array((5,c), (18,a), (23,b))

join & leftOuterJoin & rightOuterJoin & fullOuterJoin

  • 内连接 join
    • def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]
scala> val rdd1 = sc.parallelize(List(("a", "上海"), ("b", "北京"), ("c", "深圳")))
rdd1: org.apache.spark.rdd.RDD[(String, String)] = ParallelCollectionRDD[176] at parallelize at <console>:24

scala> val rdd2 = sc.parallelize(List(("a", "20"), ("b", "22"), ("d", "32")))
rdd2: org.apache.spark.rdd.RDD[(String, String)] = ParallelCollectionRDD[177] at parallelize at <console>:24

scala> rdd1.join(rdd2).collect
res58: Array[(String, (String, String))] = Array((b,(北京,22)), (a,(上海,20)))
  • 左外连接(左连接) leftOuterJoin
    • def leftOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (V, Option[W]))]
scala> rdd1.leftOuterJoin(rdd2).collect
res59: Array[(String, (String, Option[String]))] = Array((b,(北京,Some(22))), (a,(上海,Some(20))), (c,(深圳,None)))
  • 右外连接(右连接) rightOuterJoin
    • def rightOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (Option[V], W))]
scala> rdd1.rightOuterJoin(rdd2).collect
res61: Array[(String, (Option[String], String))] = Array((d,(None,32)), (b,(Some(北京),22)), (a,(Some(上海),20)))
  • 全外连接 fullOuterJoin
    • def fullOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (Option[V], Option[W]))]
scala> rdd1.fullOuterJoin(rdd2).collect
res62: Array[(String, (Option[String], Option[String]))] = Array((d,(None,Some(32))), (b,(Some(北京),Some(22))), (a,(Some(上海),Some(20))), (c,(Some(深圳),None)))

cogroup & xxxJoin

  • cogroup是根据 key 进行关联,返回两边 RDD 的记录,没关联上的是空
    • def cogroup[W](other: RDD[(K, W)]): RDD[(K, (scala.Iterable[V], scala.Iterable[W]))]
  • cogroup 返回 RDD 的 Value 类型是 (scala.Iterable[V], scala.Iterable[W]),xxxJoin返回 RDD 的 Value 类型是 (Option[V], Option[W])
  • xxxJoin 底层调用的就是 cogroup
scala> rdd1.cogroup(rdd2).collect
res63: Array[(String, (Iterable[String], Iterable[String]))] = Array((d,(CompactBuffer(),CompactBuffer(32))), (b,(CompactBuffer(北京),CompactBuffer(22))), (a,(CompactBuffer(上海),CompactBuffer(20))), (c,(CompactBuffer(深圳),CompactBuffer())))
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值