RDD的键值对操作
创建
可以从RDD中直接通过map函数进行创建
lines = sc.textFile("file:///usr/local/spark/README.md") pairs = lines.map( lambda x : (x.split(" ")[0], x) ) # 将第一行的第一个单词作为key,该行字符串作为value,构建pairRDD print( pairs.count() ) print( pairs.first() )
基本操作
- filter:对pair RDD的元素按照某种规则进行过滤,去掉不需要的pair RDD
reduceByKey:对pairRDD中的元素中所有相同key对应的元素进行统计,下面是单词个数统计的例子
lines = sc.textFile("file:///usr/local/spark/README.md") words = lines.flatMap( lambda x : x.split(" ") ) result = words.map( lambda x : (x,1) ).reduceByKey(lambda x,y:x+y) # 统计所有单词出现的次数 print( result.collect() )
combineByKey:这是pair RDD中一个非常核心的操作,groupByKey、reduceByKey都是由combineByKey实现的,它有三个参数
- combine function:可以将RDD[K,V]中的V转化为一个新的值C1
- 分区内的merge function:合并值函数,将一个C1类型值和一个V类型值合并为一个C2类型,输入参数为(C1,V),输出为(C2),这是在分区内进行的
分区之间的merge function:将两个C2整合为1个C3类型的值,输入(C2,C2),输出为C3
x = sc.parallelize([('B',1),('B',2),('A',3),('A',4),('A',5)]) createCombiner = (lambda el : (el, el**2)) mergeVal = (lambda aggregated, el : aggregated + (el, el**2)) mergeComb = (lambda agg1, agg2 : agg1+agg2) y = x.combineByKey( createCombiner, mergeVal, mergeComb ) print( x.collect() ) print( y.collect() )
设置分区个数:每个RDD都有固定数目的分区,在建立RDD时,如果不指定分区个数的话,系统会根据集群大小推断出一个有意义的默认值。
# 并行度调优 data = [("a",3), ("b",4), ("c",5), ("a", 6)] ret1 = sc.parallelize(data).reduceByKey( lambda x,y:x+y ) ret2 = sc.parallelize(data).reduceByKey( lambda x,y:x+y, 10 ) print( ret1.collect() ) print( ret2.collect() )
数据分组
- groupBy:对RDD对象中的元素进行分组,返回一个list,list中的每个元素都是一个key与满足该组分组条件的元素列表
groupByKey:对pair RDD中相同key对应的元素进行合并,形成一个元素列表作为value,key值保持不变
data = [("a",3), ("b",4), ("c",5), ("a", 6)] rdd = sc.parallelize( data ) ret1 = rdd.groupByKey().mapValues(len).collect() # 统计相同key的个数 ret2 = rdd.groupByKey().mapValues(list).collect() # 将相同key对应的value进行合并 print(ret1) print(ret2)
数据连接
join:对于2个pair RDD中都含有的key,设其value为v1,v2,返回一个pair RDD,其中的元素为
(key, (v1,v2))
x = sc.parallelize([("a", 1), ("b", 4)]) y = sc.parallelize([("a", 2), ("a", 3), ("b", 5)]) ret = x.join(y).collect() print(ret)
leftOuterJoin与rightOuterJoin:允许key值有缺失的连接,分别允许右边与左边的值有缺失,对应为None
x = sc.parallelize([("a", 1), ("c", 4)]) y = sc.parallelize([("a", 2), ("b", 5)]) ret = x.leftOuterJoin(y).collect() print(ret) x = sc.parallelize([("a", 1), ("c", 4)]) y = sc.parallelize([("a", 2), ("b", 5)]) ret = x.rightOuterJoin(y).collect() print(ret)
数据排序
sortByKey:按照key进行升序或者降序排列,可以指定比较key时采用的函数,即不是比较key1与key2,而是比较f(key1)与f(key2),但是f(key)的返回值类型需要与key相同。
x = sc.parallelize([(2, 2), (1, 3), (3, 5), (13, 4)]) ret = x.sortByKey( ascending=True, numPartitions=None, keyfunc=lambda x: -x ) # 原本设置的升序排列,这里将key变成相反数,相当于降序排列 print( ret.collect() )
pair RDD的行动操作
- countByKey:对每个key对应的元素个数进行计数
- collectAsMap:将结果以映射表的形式返回,便于查询
lookup:返回给定键对应的所有值,为list的形式
x = sc.parallelize([(2, 2), (1, 3), (3, 5), (13, 4), (2,233)]) print( x.countByKey() ) print( x.collectAsMap() ) # 对于相同key值,只保留一个value print( x.lookup(2) )
关于数据分区的进一步说明
- 在实际处理中,如果涉及到定时将采集到的数据放入本机上的数据集中,在不对本机数据集进行正确分区的情况下,在join的时候可能性能会很差,但是如果将本机数据集转化为许多个哈希分区,每个hash分区都有唯一的hash值,那么在加入新的数据时,效率会高很多。