键值对RDD 通常用来进行聚合计算。我们一般要先通过一些初始ETL(抽取、转化、装载)操作来将数据转化为键值对形式。键值对RDD 提供了一些新的操作接口(比如

统计每个产品的评论,将数据中键相同的分为一组,将两个不同的RDD 进行分组合并等)。

1. 创建Pair RDD

    在Python 中使用第一个单词作为键创建出一个pair RDD

pairs = lines.map(lambda x: (x.split(" ")[0], x))

python一个键对应多个值怎么存 python键值对操作_数据

python一个键对应多个值怎么存 python键值对操作_python一个键对应多个值怎么存_02

      pari RDD 支持 RDD所支持的函数。例如,我们可以拿前一节中的pair RDD, 筛选掉长度超过20个字符的行。

result = pairs.filter(lambda keyValue: len(keyValue[1]) < 20)

      如果只想访问RDD的值部分,可以使用mapValues(func)函数



2. 聚合操作

     (1)在Python 中使用reduceByKey() 和mapValues() 计算每个键对应的平均值

rdd.mapValues(lambda x: (x, 1)).reduceByKey(lambda x, y: (x[0] + y[0], x[1] + y[1]))


     (2) 使用reduceByKey() 对所有的单词进行计数。

rdd = sc.textFile("s3://...")
   words = rdd.flatMap(lambda x: x.split(" "))
   result = words.map(lambda x: (x, 1)).reduceByKey(lambda x, y: x + y)

      事实上,我们可以对第一个RDD 使用countByValue() 函数,以更快地实现单词计数:input.flatMap(x => x.split(" ")).countByValue()。

     (3) 在Python 中使用combineByKey() 求每个键对应的平均值  

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()

     (4)  并行度调优

在执行聚合或分组操作时,可以要求Spark 使用给定的分区数。Spark

在Python 中自定义reduceByKey() 的并行度

data = [("a", 3), ("b", 4), ("a", 1)]
      sc.parallelize(data).reduceByKey(lambda x, y: x + y) # 默认并行度
      sc.parallelize(data).reduceByKey(lambda x, y: x + y, 10) # 自定义并行度

3  数据分组

groupByKey() 就会使用RDD 中的键来对数据进行分组。对于一个由类型K 的键和类型V 的值组成的RDD,所得到的结果RDD 类型会是[K, Iterable[V]]。


RDD 的数据进行分组,还可以使用一个叫作cogroup() 的函数对多个共享同一个键的RDD 进行分组。对两个键的类型均为K 而值的类型分别为V 和W 的RDD 进行cogroup() 时,得到的结果RDD 类型为[(K, (Iterable[V], Iterable[W]))]。


4  数据分区


4.1  分区操作


Spark 程序可以通过控制, RDD 分区方式来减少通信开销。


rdd.partitionBy(100).persist()

  

4.2 从分区中获益的操作

数据分区中获益的有cogroup()、groupWith()、join()、leftOuterJoin()、rightOuterJoin()、groupByKey()、reduceByKey()、combineByKey() 以及lookup()。对于join()这类二元操作,预先进行数据分区会让其中至少一个RDD不发生数据混洗操作。如果两个RDD 使用同样的分区方式,并且它们还缓存在同样的机器上(比如一个RDD 是通过mapValues() 从另一个RDD 中创建出来的,这两个RDD 就会拥有相同的键和分区方式),或者其中一个RDD 还没有被计算出来,那么跨节点的数据混洗就不会发生了。


4.3 自动分区的操作


RDD 设好分区方式的操作:cogroup()、groupWith()、join()、lef tOuterJoin()、rightOuterJoin()、groupByKey()、reduceByKey()、combineByKey()、partitionBy()、sort()、mapValues()(如果父RDD 有分区方式的话)、flatMapValues()(如果父RDD 有分区方式的话),以及filter()(如果父RDD 有分区方式的话)。其他所有的操作生成的结果都不会存在特定的分区方式。

4.4 自定义分区

Python 中,不需要扩展Partitioner 类,而是把一个特定的哈希函数作为一个额外的参数传给RDD.partitionBy() 函数,如例4-27 所示。


import urlparse
def hash_domain(url):
return hash(urlparse.urlparse(url).netloc)
rdd.partitionBy(20, hash_domain) # 创建20个分区