有没有一种正确的方法来加速两个数据集之间的笛卡尔积?我使用了350k元素的数据集,我想得到它的组合(n取2)。
我使用经典策略在Spark中找到2组合:
words_comb = dataset.cartesian(dataset).filter(lambda x: x[0] < x[1])
我正在使用Databricks框架,并且需要超过45分钟才能解决(45分钟时火花驱动程序停在Databricks中......)。我们都同意这个特定问题的瓶颈是数据集的笛卡儿积这一事实,其时间复杂度为O(n ^ 2)。
有没有办法改善这个?有没有更好的方法解决这个问题?
(谢客)
答案 0 :(得分:2)
我需要构建一个图G =(N,F),其中F(n)是具有edit_distance(n,s)= 1的单词s的子集S作为图像的函数。 hae从所有单词组合开始,然后我过滤了所有不满足edit_distance = 1约束的单词对。
你的方法效率低得离谱。 n 平均长度的字 s 或多或少 O(n 2 s 2 ) ( n 2 edit_distance
来电)。与此同时,您的数据很小(根据the comment为4.1MB)并且分布式处理及其开销并不是很有用。你应该重新考虑你的方法。
我的建议是使用有效的查找结构(例如Trie或BWT),这可以促进不匹配的高效搜索。构建索引,使用整个数据集,如果需要,使用线程来并行化搜索。
答案 1 :(得分:0)
It's possible to get rid of Cartesian product, without using a special data structure.
I will demonstrate the method by an example with pyspark
Suppose you have a data set of (user, query).
input:
users_queries_rdd = sc.parallelize([
('u1', 'q1'), ('u1', 'q2'), ('u1', 'q3'), ('u1', 'q4'),
('u2', 'q2'), ('u2', 'q4'), ('u2', 'q5'),
('u3', 'q1'), ('u3', 'q2'), ('u3', 'q4')
])
You would like to count the occurrences of 2 queries for different users.
expected output:
[(('q4', 'q2'), 3),
(('q5', 'q2'), 1),
(('q3', 'q1'), 1),
(('q5', 'q4'), 1),
(('q4', 'q1'), 2),
(('q3', 'q2'), 1),
(('q2', 'q1'), 2),
(('q4', 'q3'), 1)]
pair_queries_count_rdd = users_queries_rdd\
.cartesian(users_queries_rdd)\
.filter(lambda line: line[0] > line[1])\
.filter(lambda line: line[0][0] == line[1][0])\
.map(lambda line: (line[0][1], line[1][1]))\
.map(lambda line: (line, 1))\
.reduceByKey(add)
pair_queries_count_rdd_no_cartesian = users_queries_rdd\
.map(lambda line: (line[0], [line[1]]))\
.reduceByKey(add)\
.map(lambda line: tuple(combinations(line[1], 2)))\
.flatMap(lambda line: [(x, 1) for x in line])\
.reduceByKey(add)
1.1 .cartesian(users_queries_rdd)
在rdd到自身之间创建笛卡尔乘积。
它会生成n ^ 2个组合。
1.2 .filter(lambda line:line [0]> line [1])
如果包含(q1,q2),则(q2,q1)将被过滤掉。
1.3 .filter(lambda line:line [0] [0] == line [1] [0])
按用户对查询进行分组(汇总)。
1.4 .map(lambda line:(line [0] [1],line [1] [1]))
忽略用户列。仅保留查询对。
1.5 .map(lambda line:(line,1))
每行将(q [i],q [j])映射到((q [i],q [j]),1)
1.6 .reduceByKey(add)
计算每个查询对的出现次数。
2.1 .map(lambda line:(line [0],[line [1]]))
将每行的(u [i],q [j])映射到(u [i],[q [j]])(查询被封装到列表中)
2.2 .reduceByKey(add)
为每个用户创建一个包含所有查询的列表。
2.3 .map(lambda line:tuple(combinations(line [1],2)))
对于每个用户,请省略“用户”列,并创建其所有查询的组合
2.4 .flatMap(lambda line:[(x,1)for x in line])
平面映射将所有键都平面化:(q [i],q [j])映射到((q [i],q [j]),1)
2.5 .reduceByKey(add)
计算每个查询对的出现次数。
我们假设每个用户的查询数量相对较少(恒定数量)。 因此,方法2的效率为O(n)
由于每个用户都使用iter.combinations函数,因此这一假设至关重要。
在大多数实际情况下,方法2更为有效。