黑猴子的家:Spark 键值对 RDD 之 转化操作

1、转化操作列表

针对两个pair RDD 的转化操作(rdd = {(1,2),(3,4),(3,6)} 为例)

函数名目的示例结果
reduceByKey(func)合并具有相同键的值rdd.reduceByKey((x,y)=> x+ y){(1,2),(3,10)}
groupByKey()对具有相同键的值进行分组rdd.groupByKey(){(1,[2]),(3,[4,6])}
combineByKey(
createCombiner,
mergeValue,
mergeCombiners,
partitioner)
使用不同的返回类型合并具有相同键的值
mapValues(func)对pair RDD中的每个值应用一个函数而不改变键rdd.mapValues(x=> x+1){(1,3),(3,5),(3,7)}
flatMapValues(func)对pair RDD 中的每个值应用一个返回迭代器的函数,然后对返回的每个元素都生成一个对应原键的键值对记录。通常用于符号化rdd.flatMapValues(x=>(x to 5)){ (1,2),(1,3),(1,4),(1,5),(3,4),(3,5),}
keys()返回一个仅包含键的RDDrdd.keys(){1,3,3}
values()返回一个仅包含值的RDDrdd.values(){2,4,6}
sortByKey返回一个根据键排序的RDDrdd.sortByKey(){(1,2),(3,4),(3,6),}

针对两个pair RDD 的转化操作(rdd = {(1,2),(3,4),(3,6)} other = {(3,9)})

函数名目的示例结果
subtractByKey删除RDD中键与other RDD 中的键相同的元素rdd.subtractByKey(other){(1,2)}
join对两个RDD进行内连接rdd.join(other){(3,(4,9)),(3,(6,9))}
rightOuterJoin对两个RDD进行连接操作,确保第一个RDD的键必须存在(右外连接)rdd.rightOuterJoin(other){(3,(Some(4),9)),(3,(Some(6),9))}
leftOuterJoin对两个RDD进行连接操作,确保第二个RDD的键必须存在(左外连接)rdd.leftOuterJoin(other){(1,(2,None)), (3,(4,Some(9))), (3,(6,Some(9)))}
cogroup将两个RDD中拥有相同键的数据分组到一起rdd.cogroup(other){(1,([2],[])), (3,([4,6],[9]))}
2、聚合操作

当数据集以键值对形式组织的时候,聚合具有相同键的元素进行一些统计是很常见的操作。之前讲解过基础RDD上的fold()、combine()、reduce()等行动操作,pair RDD上则有相应的针对键的转化操作。Spark 有一组类似的操作,可以组合具有相同键的值。这些操作返回RDD,因此它们是转化操作而不是行动操作。

reduceByKey() 与 reduce() 相当类似;它们都接收一个函数,并使用该函数对值进行合并。 reduceByKey() 会为数据集中的每个键进行并行的归约操作,每个归约操作会将键相同的值合并起来。因为数据集中可能有大量的键,所以 reduceByKey() 没有被实现为,向用户程序返回一个值的行动操作。实际上,它会返回一个由各键和对应键归约出来的结果值组成的新的 RDD。

foldByKey() 则与 fold() 相当类似;它们都使用一个RDD 和合并函数中的 数据类型相同的 零值作为初始值。与 fold() 一样,foldByKey() 操作所使用的合并函数对零值与另一 个元素进行合并,结果仍为该元素。

(1)求均值 方法一
scala> val inputRDD = sc.makeRDD(List( \
("panda",0),("pink",3),("pirate",3),("panda",1),("pink",4)))
inputRDD: org.apache.spark.rdd.RDD[(String, Int)] 
                             = ParallelCollectionRDD[6] at makeRDD at <console>:24

scala> val resultRDD = inputRDD.mapValues(x => (x, 1)).reduceByKey( \
(x, y) => (x._1 + y._1, x._2 + y._2)).map{ \
case (key, value) => (key, value._1 / value._2.toFloat) }
resultRDD: org.apache.spark.rdd.RDD[(String, Float)] 
                             = MapPartitionsRDD[9] at map at <console>:26

scala> resultRDD.collect
res3: Array[(String, Float)] = Array((panda,0.5), (pink,3.5), (pirate,3.0))
9193428-fac21bb4350fac25.png

combineByKey() 是最为常用的基于键进行聚合的函数。大多数基于键聚合的函数都是用它实现的。和 aggregate() 一样,combineByKey() 可以让用户返回与输入数据的类型不同的 返回值

要理解 combineByKey(),要先理解它在处理数据时是如何处理每个元素的。由于 combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就 和之前的某个元素的键相同

如果这是一个新的元素,combineByKey() 会使用一个叫作 createCombiner() 的函数来创建 那个键对应的累加器的初始值。需要注意的是,这一过程会在每个分区中第一次出现各个 键时发生,而不是在整个 RDD 中第一次出现一个键时发生

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

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

(2)求均值 方法二
scala> val inputRDD = sc.makeRDD(List( \
("panda",0),("pink",3),("pirate",3),("panda",1),("pink",4)))
inputRDD: org.apache.spark.rdd.RDD[(String, Int)] 
                             = ParallelCollectionRDD[6] at makeRDD at <console>:24

scala> val resultRDD = inputRDD.combineByKey(
(v) => (v, 1),
(acc: (Int, Int), v) => (acc._1 + v, acc._2 + 1),
(acc1: (Int, Int), acc2: (Int, Int)) => (acc1._1 + acc2._1, acc1._2 + acc2._2)
).map{ case (key, value) => (key, value._1 / value._2.toFloat) }
resultRDD: org.apache.spark.rdd.RDD[(String, Float)] 
                             = MapPartitionsRDD[11] at map at <console>:26

scala> resultRDD.collect
res5: Array[(String, Float)] = Array((panda,0.5), (pink,3.5), (pirate,3.0))

scala> resultRDD.collectAsMap.map(println _)
(pirate,3.0)
(pink,3.5)
(panda,0.5)
res6: Iterable[Unit] = ArrayBuffer((), (), ())
3、数据分组

如果数据已经以预期的方式提取了键,groupByKey() 就会使用 RDD 中的键来对数据进行 分组。对于一个由类型 K 的键和类型 V 的值组成的 RDD,所得到的结果 RDD 类型会是 [K, Iterable[V]]。

groupBy() 可以用于未成对的数据上,也可以根据除键相同以外的条件进行分组。它可以 接收一个函数,对源 RDD 中的每个元素使用该函数,将返回结果作为键再进行分组。

多个RDD分组,可以使用cogroup函数,cogroup() 的函数对多个共享同 一个键的 RDD 进行分组。对两个键的类型均为 K 而值的类型分别为 V 和 W 的 RDD 进行 cogroup() 时,得到的结果 RDD 类型为 [(K, (Iterable[V], Iterable[W]))]。如果其中的 一个 RDD 对于另一个 RDD 中存在的某个键没有对应的记录,那么对应的迭代器则为空。 cogroup() 提供了为多个 RDD 进行数据分组的方法。

4、连接

连接主要用于多个Pair RDD的操作,连接方式多种多样:右外连接、左外连接、交叉连接以及内连接。

普通的 join 操作符表示内连接,生成的pair RDD会包括来自两个输入RDD的每一组相对应的记录。

leftOuterJoin()产生的pair RDD中,来源于RDD的每一个键都有对应的记录。每个键相应的值是由一个源 RDD 中的值与一个包含第二个 RDD 的值的 Option(在 Java 中为 Optional)对象组成的二元组。

rightOuterJoin() 几乎与 leftOuterJoin() 完全一样,只不过预期结果中的键必须出现在 第二个 RDD 中,而二元组中的可缺失的部分则来自于源 RDD 而非第二个 RDD。

scala> val storeA = sc.makeRDD(List(("AA",12),("AA",13),("cc",14),("dd",15)))
storeA: org.apache.spark.rdd.RDD[(String, Int)] 
                             = ParallelCollectionRDD[12] at makeRDD at <console>:24

scala> val storeB = sc.makeRDD(List(("cc",17),("AA",16)))
storeB: org.apache.spark.rdd.RDD[(String, Int)] 
                             = ParallelCollectionRDD[13] at makeRDD at <console>:24

scala> storeA.join(storeB).collect
res7: Array[(String, (Int, Int))] = Array((AA,(12,16)), (AA,(13,16)), (cc,(14,17)))

scala> storeA.leftOuterJoin(storeB).collect
res9: Array[(String, (Int, Option[Int]))] 
        = Array((dd,(15,None)), (AA,(12,Some(16))), (AA,(13,Some(16))), (cc,(14,Some(17))))

scala> storeA.rightOuterJoin(storeB).collect
res10: Array[(String, (Option[Int], Int))] 
        = Array((AA,(Some(12),16)), (AA,(Some(13),16)), (cc,(Some(14),17)))
5、数据排序

sortByKey() 函数接收一个叫作 ascending 的参数,表示我们是否想要让结果按升序排序(默认值为 true)。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值