键值对RDD也叫做Pair RDD
1. 创建Pair RDD
把一个普通 RDD转换为pair RDD的时候使用map()函数来实现
#使用第一个单词作为一个键,创建一个pair RDD
pairs=lines.map(lambda x:(x.split(" ")[0],x))
2.Pair RDD转化操作
2.1针对单个pairRDD的转换操作
reduceByKey(func)
合并具有相同键的值
rdd.reduceByKey((x, y) => x + y)
groupByKey()
对具有相同键的值进行分组
rdd.groupByKey()
生成的数据示例如下
{
(1,[2]),
(5,[1,5,8]),
(7,[6,7,8])
}
combineByKey(createCombiner,mergeValue,mergeCombiners,partitioner)
使用不同的返回类型合并具有相同键的值
mapValues(func)
对pair RDD 中的每个值应用一个函数而不改变键
rdd.mapValues(x => x+1)
flatMapValues(func)
对pair RDD 中的每个值应用一个返回迭代器的函数,然后对返回的每个元素都生成一个对应原键的键值对记录。通常用于符号化
val a = sc.parallelize(List(("fruit", "apple,banana,pear"), ("animal", "pig,cat,dog,tiger")))
a.flatMapValues(_.split(",")).collect()
Array[(String, String)] = Array((fruit,apple), (fruit,banana), (fruit,pear),
(animal,pig), (animal,cat), (animal,dog), (animal,tiger))
keys()
返回一个仅包含键的RDD
values()
返回一个仅包含值的RDD
sortByKey()
返回一个根据键排序的RDD
函数接受一个ascending参数,表示是否按升序排序,默认是true
也可以自定义排序顺序
#比如虽然rdd是整数,但是利用字符串排序
rdd.sortByKey(ascending=True, numPartitions=None, keyfunc = lambda x: str(x))
2.2针对两个pairRDD转换操作
操作的两个键
(rdd = {(1, 2), (3, 4), (3, 6)}other = {(3, 9)})
subtractByKey
删掉RDD 中键与other RDD 中的键相同的元素
rdd.subtractByKey(other)
结果 {(1, 2)}
join
对两个RDD 进行内联结
普通的join操作符表示内连接,只有在两个pair RDD都存在键时才输出。当一个输入对应的键有多个值时,生成的pair RDD会包括来自两个输入RDD的每一组相对应的记录
rdd.join(other)
结果
{(3, (4, 9)), (3,(6, 9))}
rightOuterJoin
对两个RDD 进行连接操作,确保第二个RDD 的键必须存在(右外连接)
leftOuterJoin
对两个RDD 进行连接操作,确保第一个RDD 的键必须存在(左外连接)
var rdd1 = sc.makeRDD(Array(("A","1"),("B","2"),("C","3")),2)
var rdd2 = sc.makeRDD(Array(("A","a"),("C","c"),("D","d")),2)
rdd1.rightOuterJoin(rdd2).collect
结果
Array[(String, (Option[String], String))] = Array((D,(None,d)), (A,(Some(1),a)), (C,(Some(3),c)))
cogroup
将两个RDD 中拥有相同键的数据分组到一起
rdd.cogroup(other)
结果
{(1,([2],[])), (3,([4, 6],[9]))}
2.1.聚合操作
reduceByKey()
reduceByKey()与reduce()类似,reduceByKey() 会为数据集中的每个键进行并行的归约操作,每个归约操作会将键相同的值合并起来
foldByKey()
与fold() 一样,foldByKey() 操作所使用的合并函数对零值与另一个元素进行合并,结果仍为该元素。
rdd.mapValues(lambda x: (x, 1)).reduceByKey(lambda x, y: (x[0] + y[0], x[1] + y[1]))
#利用reduceByKey实现单词计数
sc = SparkContext("local","test")
rdd=sc.textFile("file:///G:/Reference/source/spark/text.txt")
words=rdd.flatMap(lambda x:x.split(" "))
print(words.map(lambda x:(x,1)).take(5))
result=words.map(lambda x:(x,1)).reduceByKey(lambda x,y:x+y)
#也可以利用words.countByValue()直接进行单词计数
combineByKey()
#完整的函数定义
def combineByKey(self, createCombiner, mergeValue, mergeCombiners,
numPartitions=None, partitionFunc=portable_hash):
combineByKey() 会遍历分区中的所有元素,每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。遇到新元素时,combineByKey() 会使用一个叫作createCombiner() 的函数来创建那个键对应的累加器的初始值,如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue() 方法将该键的累加器对应的当前值与这个新的值进行合并,如果有多个分区,需要使用mergeCombiners()将各个分区结果合并
#利用combineByKey()求平均值
#第一个匿名函数用于生产累加器,第二个匿名函数则处理已有累加器key的value,第三个则用于处理跨分区
sumCount = nums.combineByKey((lambda x: (x,1)),
(lambda x, y: (x[0] + y, x[1] + 1)),
(lambda x, y: (x[0] + y[0], x[1] + y[1])))
sumCount.map(lambda key, xy: (key, xy[0]/xy[1])).collectAsMap()
spark中很多函数都是都是在combineByKey()基础上实现的
也可以为RDD指定分区数进行调优
data = [("a", 3), ("b", 4), ("a", 1)]
sc.parallelize(data).reduceByKey(lambda x, y: x + y, 10)
也可以使用repartition()或者coalesce()在除分组操作和聚合操作之外也能改变分区,coalesce()是repartition()优化版
2.2数据分组
groupByKey()
使用RDD 中的键来对数据进行
分组。对于一个由类型K 的键和类型V 的值组成的RDD,所得到的结果RDD 类型会是[K, Iterable[V]]。
groupBy()
用于未成对数据上,可以根据除键以外的条件分组,它可以接收一个函数,对源RDD中的每个元素使用该函数将返回结果作为键再进行分组
cogroup()
对多个共享同一个键的RDD 进行分组,如果键值不是同一种类型则生成如下的的RDD
[(K, (Iterable[V], Iterable[W]))]
3.Pair RDD行动操作
countByKey()
对每个键对应的元素分别计数
collectAsMap()
将结果以映射表的形式返回,以便查询
lookup(key)
返回给定键对应的所有值