使用pyspark实现计算Top k

关于Spark和HDFS的安装这里暂且不讲,只讲Spark的使用。

Top K就是要计算一个数组中前k个最大元素。这里我们把数据存储在一个文件中,文件中的没一行对应的是数据的id和数据的值。其中的每个id可能会多次出现。需要计算所有id中对应的出现的值之和最大的k个id。

文件格式:

id1,200
id2,700
id3,450
id1,300
...
  • 首先使用spark需要导入pyspark包。
from pyspark import *
  • 创建SparkContext。第一个参数是设置SparkContext的master节点的地址。这里我们只是一个简单的例子,因此选择本地运行模式。第二个参数指定该任务的名称,可不写。
sc = SparkContext('local', 'TopK')
  • 因为多个进程中都需要用到k,因此设置共享变量保存k的值。
broadcast_k = sc.broadcast(k_value)
  • 接下来就是直接设置map-reduce过程,对任务进行实际处理了。首先将使用待排序的数据创建RDD(弹性分布式数据集)。这里是数据在文件中,因此直接使用Spark提供的sc.textFile方法从文件直接创建RDD。如果要通过List创建可以使用sc.parallelize(arrList) 创建RDD,效果相同。
  • 接下来的map、reduceByKey、mapPartitions方法都是map-reduce操作,参数都是方法。这三个方法返回的都是新的RDD。
rdd = sc.textFile(data_path) \
            .map(mapToPairs) \
            .reduceByKey(add)\
            .mapPartitions(select_top_k)
  • map是对原数据进行映射,这里给map操作传入的方法就是将原数据映射为key-value对,id作为key,对应的值作为value。
def mapToPairs(line):
    res = line.split(',')
    return (res[0], float(res[1]))
  • reduce操作将数据根据key值进行合并,也就是计算每个id所有出现的值之和。因此作为参数传入的方法就是比较简单的求和。
def add(a,b):
     return a+b

在这里的reduceByKey中,这个作为参数的方法可以用lambda表达式替代,效果一样。

rdd.reduceByKey(lambda a,b: a+b)
  • 数据量可能非常大,因此使用mapPartitions操作将RDD映射为多个partition,再在多个partition中进行求top k的操作,只返回每个分区中的top k。
def select_top_k(it):
    k = broadcast_k.value
    sorted_res = sorted(it, key=lambda x: x[1], reverse=True)
    top_k_res = [0] * k
    i = 0
    for t in sorted_res:
        top_k_res[i] = t
        i += 1
        if i >= k:
            break
    return top_k_res
  • 此时我们的rdd中存储的只是每个分区中的top k,我们还需要从中取出所有总的数据中的top k。这里直接使用了takeOrdered方法,key使用了lambda表达式,由于Spark提供的takeOrdered方法是选取最小的k个,因此这里取key-value中value的相反数作为选择依据。
top_k_res = rdd.takeOrdered(k_value, key=lambda x: -x[1])

在进行上一步takeOrdered操作的同时rdd也被装换成了可以直接使用的list。因此之后我们直接输出或者将数据保存即可。

完整代码如下:

from pyspark import *

# 将输入映射为key-value键值对
def mapToPairs(line):
    res = line.split(',')
    return (res[0], float(res[1]))

# 根据key值求和
def add(a,b):
     return a+b

# 对每个partition取前top k
def select_top_k(it):
    k = broadcast_k.value
    sorted_res = sorted(it, key=lambda x: x[1], reverse=True)
    top_k_res = [0] * k
    i = 0
    for t in sorted_res:
        top_k_res[i] = t
        i += 1
        if i >= k:
            break
    return top_k_res

if __name__ == "__main__"
    # 需要排序的数组位置和其它参数
    data_path = 'data/arrays.txt'
    out_path = 'sortResult.txt'
    k_value = 10
    # 设置master url,初始化SC
    sc = SparkContext('local', 'TopK')
    # 创建共享变量,保存k的值
    broadcast_k = sc.broadcast(k_value)
    # 进行MapReduce操作
    rdd = sc.textFile(data_path)\
              .map(mapToPairs)  \
              .reduceByKey(add) \
              .mapPartitions(select_top_k)
    top_k_res = rdd.takeOrdered(k_value, key=lambda x: -x[1])
    # 输出结果
    with open(out_path, 'w') as out:
        for i in top_k_res:
            out.write(i[0] + '\n')

你可能感兴趣的:(使用pyspark实现计算Top k)