我有一个非常长(几十亿行)和相当宽(几百列)的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))
a
是empty_sets
而b
是data.first()
(整行,而不是单个值)。这显然不起作用,也不是我预期的聚合。
如何遍历我的集合列表,并遍历我的数据帧的每一行,将每个值添加到其对应的集合对象中?
所需的输出如下:
[set(['a', 'b', 'c']), set(['one', 'two']), set(['x', 'y'])]
PS我已经看过这个例子here,这与我的用例非常相似(我在第一时间想到使用aggregate
的想法) 。但是,我发现代码很难转换为PySpark,我很清楚case
和zip
代码在做什么。
答案 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);
),这样你就可以同时循环两个列表。