一.键值对RDD-pairRDD
键值对RDD是Spark操作中最常用的RDD,它是很多程序的构成要素,因为他们提供了并行操作各个键或跨界点重新进行数据分组的操作接口。
>>> lines=sc.textFile("file:///usr/local/spark/mycode/pairrdd/test.txt")
>>> pairRDD =lines.flatMap(lambda line: line.split("")).map(lambda word:(word,1))
>>> pairRDD.foreach(print)
结果:
(a,1)
(a,1)
(b,1)
(c,1)
二.常用键值对转换操作
reduceByKey(func):使用func函数合并具有相同键的值
>>> pairRDD.reduceByKey(lambda a,b : a+b).foreach(print)
(a,2)
(b,1)
(c,1)
groupByKey()的功能是,对具有相同键的值进行分组
对以上数据处理的结果是 (a,(1,1))(b,1)(c,1)
>>> pairRDD.groupByKey()
PythonRDD[11] at RDD at PythonRDD.scala:48
>>> pairRDD.groupByKey().foreach(print)
('a', <pyspark.resultiterable.ResultIterable object at 0x7f1869f81f60>)
('b', <pyspark.resultiterable.ResultIterable object at 0x7f1869f81f60>)
('c', <pyspark.resultiterable.ResultIterable object at 0x7f1869f81f60>)
keys()
keys()只会把键值对RDD中的key返回形成一个新的RDD。比如,对四个键值对(“a”,1)、(“a”,1)、("b”,1)和(“c”,1)构成的RDD,采用keys()后得到的结果是一个RDD[Int],内容是{“a”,”a”,"b”,”c”}。
对pairRDD进行keys操作,代码:
>>> pairRDD.keys()
PythonRDD[20] at RDD at PythonRDD.scala:48
>>> pairRDD.keys().foreach(print)
a
a
b
c
values():
只会把键值对RDD中的value返回形成一个新的RDD,代码:
scala> pairRDD.values()
PythonRDD[22] at RDD at PythonRDD.scala:48
scala> pairRDD.values().foreach(print)
1
1
1
1
sortByKey():
功能是返回一个根据键排序的RDD。
代码如下:
>>> pairRDD.sortByKey()
PythonRDD[30] at RDD at PythonRDD.scala:48
>>> pairRDD.sortByKey().foreach(print)
(a,1)
(a,1)
(b,1)
(c,1)
mapValues(func):
对键值对RDD中的每个value都应用一个函数,但是,key不会发生变化。代码:
>>> pairRDD.mapValues(lambda x : x+1)
PythonRDD[38] at RDD at PythonRDD.scala:48
>>> pairRDD.mapValues( lambda x : x+1).foreach(print)
(a,2)
(a,2)
(b,2)
(c,2)
join:
(连接)操作是键值对常用的操作。“连接”(join)这个概念来自于关系数据库领域,因此,join的类型也和关系数据库中的join一样,包括内连接(join)、左外连接(leftOuterJoin)、右外连接(rightOuterJoin)等。最常用的情形是内连接,所以,join就表示内连接。
对于内连接,对于给定的两个输入数据集(K,V1)和(K,V2),只有在两个数据集中都存在的key才会被输出,最终得到一个(K,(V1,V2))类型的数据集。
比如,pairRDD1是一个键值对集合{(“a”,1)、(“a",2)、(“b”,3)和(“c”,3)},pairRDD2是一个键值对集合{(“a”,”haha”)},那么,pairRDD1.join(pairRDD2)的结果就是一个新的RDD,这个新的RDD是键值对集合{(“a”,1,”haha”),(“a”,2,”haha”)}。
附录:在别处看的操作集合
基本RDD转化操作在此同样适用。但因为键值对RDD中包含的是一个个二元组,所以需要传递的函数会由原来的操作单个元素改为操作二元组。
下表总结了针对单个键值对RDD的转化操作,以 { (1,2) , (3,4) , (3,6) } 为例,f表示传入的函数
函数名 | 目的 | 示例 | 结果 |
reduceByKey(f) | 合并具有相同key的值 | rdd.reduceByKey( ( x,y) => x+y ) | { (1,2) , (3,10) } |
groupByKey() | 对具有相同key的值分组 | rdd.groupByKey() | { (1,2) , (3, [4,6] ) } |
mapValues(f) | 对键值对中的每个值(value)应用一个函数,但不改变键(key) | rdd.mapValues(x => x+1) | { (1,3) , (3,5) , (3,7) } |
combineBy Key( createCombiner, mergeValue, mergeCombiners, partitioner) | 使用不同的返回类型合并具有相同键的值 | 下面有详细讲解 | - |
flatMapValues(f) | 对键值对RDD中每个值应用返回一个迭代器的函数,然后对每个元素生成一个对应的键值对。常用语符号化 | rdd.flatMapValues(x => ( x to 5 )) | { (1, 2) , (1, 3) , (1, 4) , (1, 5) , (3, 4) , (3, 5) } |
keys() | 获取所有key | rdd.keys() | {1,3,3} |
values() | 获取所有value | rdd.values() | {2,4,6} |
sortByKey() | 根据key排序 | rdd.sortByKey() | { (1,2) , (3,4) , (3,6) } |
广播变量:
广播变量(broadcast variables)允许程序开发人员在每个机器上缓存一个只读的变量,而不是为机器上的每个任务都生成一个副本。可以通过调用SparkContext.broadcast(v)来从一个普通变量v中创建一个广播变量。这个广播变量就是对普通变量v的一个包装器,通过调用value方法就可以获得这个广播变量的值,具体代码如下:
>>> broadcastVar = sc.broadcast([1, 2, 3])
>>> broadcastVar.value
[1,2,3]
累加器:
可以通过调用SparkContext.accumulator()来创建。运行在集群中的任务,就可以使用add方法来把数值累加到累加器上,但是,这些任务只能做累加操作,不能读取累加器的值,只有任务控制节点(Driver Program)可以使用value方法来读取累加器的值。
>>> accum = sc.accumulator(0)
>>> sc.parallelize([1, 2, 3, 4]).foreach(lambda x : accum.add(x))
>>> accum.value
10
下表总结了针对两个键值对RDD的转化操作,以rdd1 = { (1,2) , (3,4) , (3,6) } rdd2 = { (3,9) } 为例,
函数名 | 目的 | 示例 | 结果 |
subtractByKey | 删掉rdd1中与rdd2的key相同的元素 | rdd1.subtractByKey(rdd2) | { (1,2) } |
join | 内连接 | rdd1.join(rdd2) | {(3, (4, 9)), (3, (6, 9))} |
leftOuterJoin | 左外链接 | rdd1.leftOuterJoin (rdd2) | {(3,( Some( 4), 9)), (3,( Some( 6), 9))} |
rightOuterJoin | 右外链接 | rdd1.rightOuterJoin(rdd2) | {(1,( 2, None)), (3, (4, Some( 9))), (3, (6, Some( 9)))} |
cogroup | 将两个RDD钟相同key的数据分组到一起 | rdd1.cogroup(rdd2) | {(1,([ 2],[])), (3, ([4, 6],[ 9]))} |