为每个键计算唯一值的有效方法

时间:2015-11-09 02:58:26

标签: python apache-spark pyspark

我有一个成员列表,它们有许多属性,其中两个是名称和ID。我希望得到一个RDD中的元组列表。元组将包含ID作为第一个元素,以及与ID关联的unique个名称的计数作为第二个元素。

e.g。比如:ID, <# of unique names associated with ID>

这里是我为完成此任务而编写的代码:

IDnametuple = members.map(lambda a: (a.ID, a.name))   # extract only ID and name
idnamelist = IDnametuple.groupByKey()                 # group the IDs together 
idnameunique_count = (idnamelist
     # set(tup[1]) should extract unique elements, 
     # and len should tell the number of them
    .map(lambda tup: (tup[0], len(set(tup[1]))))) 

它非常慢,并且比为每个成员计算唯一属性的类似操作慢得多。

有更快的方法吗?我尝试使用尽可能多的内置插件,这是加速操作的正确方法,来自我所听到的内容。

2 个答案:

答案 0 :(得分:3)

没有任何细节,我们只能猜测,但显而易见的选择是groupByKey。如果每个id与大量名称相关联,则由于广泛的改组,它可能相当昂贵。最简单的改进是aggregateByKeycombineByKey

create_combiner = set

def merge_value(acc, x):
    acc.add(x)
    return acc

def merge_combiners(acc1, acc2):
    acc1.update(acc2)
    return acc1

id_name_unique_count = (id_name_tuple  # Keep consistent naming convention
  .combineByKey(create_combiner, merge_value, merge_combiners)
  .mapValues(len))

如果预期的唯一值数量很大,您可能更愿意替换近似的精确方法。一种可能的方法是使用Bloom过滤器来跟踪唯一值而不是set

有关groupByKeyaggregateByKeyreduceByKeycombineByKey)的其他信息,请参阅:

答案 1 :(得分:1)

这基本上是https://spark.apache.org/docs/latest/programming-guide.html#working-with-key-value-pairs的单词计数示例,但计算不同的键值对:

from operator import add
IDnametuple = sc.parallelize([(0, "a"),(0, "a"),(0, "b"),(1, "a"),(1, "b"),(1, "c"),(2, "z")])
idnameunique_count = (IDnametuple.distinct()
                                  .map(lambda idName : (idName[0], 1))
                                  .reduceByKey(add))

因此idnameunique_count.collect()会返回[(0, 2), (1, 3), (2, 1)],其中(0, "a")只计算一次。正如@ zero323所提到的,这里的关键是将groupByKey替换为reduceByKey,以避免创建名称的中间列表。您只需要名称计数,这是一个小得多的对象,可能是一个巨大的列表。此外,您的版本使用set()在闭包代码中按顺序消除重复项,而distinct则作为分布式并行RDD转换执行。