在PySpark

时间:2016-04-26 19:02:34

标签: apache-spark pyspark

我有一个非常长(几十亿行)和相当宽(几百列)的RDD。我想在每列中创建一组唯一值(这些集合不需要并行化,因为它们每列只包含不超过500个唯一值。)

这是我到目前为止所做的:

data = sc.parallelize([["a", "one", "x"], ["b", "one", "y"], ["a", "two", "x"], ["c", "two", "x"]])
num_columns = len(data.first())
empty_sets = [set() for index in xrange(num_columns)]
d2 = data.aggregate((empty_sets), (lambda a, b: a.add(b)), (lambda x, y: x.union(y)))

我在这里做的是尝试启动一个空集列表,一个用于我的RDD中的每一列。对于聚合的第一部分,我想逐行遍历data,将列n中的值添加到我的列表集中的n集。如果该值已存在,则它不会执行任何操作。然后,它会在之后执行union集,因此只会在所有分区中返回不同的值。

当我尝试运行此代码时,出现以下错误:

AttributeError: 'list' object has no attribute 'add'

我认为问题在于我没有准确地说清楚我正在迭代集合列表(empty_sets)并且我正在迭代data中每行的列。我相信(lambda a, b: a.add(b)) aempty_setsbdata.first()(整行,而不是单个值)。这显然不起作用,也不是我预期的聚合。

如何遍历我的集合列表,并遍历我的数据帧的每一行,将每个值添加到其对应的集合对象中?

所需的输出如下:

[set(['a', 'b', 'c']), set(['one', 'two']), set(['x', 'y'])]

PS我已经看过这个例子here,这与我的用例非常相似(我在第一时间想到使用aggregate的想法) 。但是,我发现代码很难转换为PySpark,我很清楚casezip代码在做什么。

1 个答案:

答案 0 :(得分:2)

有两个问题。一,你的组合器函数假设每一行都是一组,但你在一组集合上运行。其中两个,add不返回任何内容(try a = set(); b = a.add('1'); print b),因此您的第一个组合器函数会返回None s的列表。要解决此问题,请使您的第一个组合函数非匿名并具有它们都循环遍历集合列表:

def set_plus_row(sets, row):
    for i in range(len(sets)):
        sets[i].add(row[i])
    return sets


unique_values_per_column = data.aggregate(
    empty_sets, 
    set_plus_row, # can't be lambda b/c add doesn't return anything
    lambda x, y: [a.union(b) for a, b in zip(x, y)]
)

我不确定Scala中的zip是什么,但在Python中,它需要两个列表并将每个对应的元素放在一起组成元组(try x = [1, 2, 3]; y = ['a', 'b', 'c']; print zip(x, y);),这样你就可以同时循环两个列表。