我目前正在研究大数据,这项工作的一个重要步骤是获得数千个字符串的所有已排序组合。
在python 3.4中,我尝试了三种方法来执行此操作。
首先,将数据伪造成字符串列表,有或没有GO术语:
# -*- coding: utf-8 -*-
import random as rd
# With GO term
# data = ["GO:" + str(i) for i in range(0, 10000)]
# Xor, without GO term
data = [str(i) for i in range(0, 10000)]
rd.shuffle(data)
print("shuffle done")
GO术语只是添加到所有数据字符串开头的常量字符串。有和没有GO术语的结果将随之而来。
现在,执行基准测试:
from itertools import combinations, product
def test1(): # use condition
g = ((i,j) if (i < j) else (j,i) for i, j in combinations(data, 2))
print(len([(i, j) for i, j in g]))
def test2(): # use filter
g = ((i, j) for i,j in product(data, data) if (i < j))
print(len([(i, j) for i, j in g]))
def test3(): # sort before combine
g = ((i,j) for i, j in combinations(sorted(data), 2))
print(len([(i, j) for i, j in g]))
import timeit
print(timeit.timeit(stmt=test1, number=3))
print(timeit.timeit(stmt=test2, number=3))
print(timeit.timeit(stmt=test3, number=3))
使用GO术语输出:
49995000
49995000
49995000
23.490827083587646
49995000
49995000
49995000
31.04393219947815
49995000
49995000
49995000
16.878661155700684
没有GO术语的输出:
49995000
49995000
49995000
22.99237084388733
49995000
49995000
49995000
29.025460958480835
49995000
49995000
49995000
16.596422910690308
每次通话打印的49995000证明我们所有方法都会产生相同数量的数据。 现在的问题是:
我的第一个猜测是排序+组合产生的数据比较少得多,而另外两种方法对每对字符串进行比较,但是,由于排序听起来像是一个繁重的操作,我不完全确定
答案 0 :(得分:1)
i<j
成立的组合,而product创建所有组合,包括i<j
不为真的组合if (i < j) else (j,i)
中的test1
是多余的。在test1
中省略此检查会大大缩短执行时间,如下所示。 i<j
签入test1
:
shuffle done
49995000
49995000
49995000
31.66194307899991
49995000
49995000
49995000
37.66488860800018
49995000
49995000
49995000
22.706632076000005
没有i<j
签入test1
:
shuffle done
49995000
49995000
49995000
25.07709688900013
49995000
49995000
49995000
39.405620851000094
49995000
49995000
49995000
23.54182383899979
答案 1 :(得分:1)
我非常确定您所观察到的性能差异的重要贡献在于检查if (i < j)
49995000次与排序10000个元素的列表,而不是假定的排序与未排序的可迭代。
combinations
应该在两种情况下都做同样的工作,因为它们产生相同数量的元素,并且不对元素进行排序并按字典顺序返回它们。
要正确测试排序是否有所不同:
使用相同的数据集执行相同的条件检查,但已排序且未排序:
sorted_data = sorted(data)
def test1():
g = ((i,j) if (i < j) else (j,i) for i, j in combinations(sorted_data, 2))
return len([(i, j) for i, j in g])
def test2():
g = ((i,j) if (i < j) else (j,i) for i, j in combinations(data, 2))
return len([(i, j) for i, j in g])
%timeit test1()
1 loops, best of 3: 23.5 s per loop
%timeit test2()
1 loops, best of 3: 24.6 s per loop
在没有条件的情况下执行测试:
def test3():
g = ((i,j) for i, j in combinations(sorted_data, 2))
return len([(i, j) for i, j in g])
def test4():
g = ((i,j) for i, j in combinations(data, 2))
return len([(i, j) for i, j in g])
%timeit test3()
1 loops, best of 3: 20.7 s per loop
%timeit test4()
1 loops, best of 3: 21.3 s per loop
为什么第一种方法与第二种方法相比如此之快?使用过滤器是我用于过滤数据的主要方式。到目前为止,我认为过滤器形式已经过大量优化。
使用组合产生的元素少于检查条件的元素。 10000C2 = 49995000
用于产品的组合与10000**2 = 100000000
。
为什么GO术语对第一种和第二种方法的影响大于第三种方法?
第一种和第二种方法受到用于比较49995000和100000000次的附加字符的影响。第三个仅受到对10000个项目进行排序所需的比较的影响。
经过一些摆弄后,似乎排序可能会产生一些差异,但不会像使用条件那样大。不知道是什么原因引起的。
from itertools import combinations
import random as rd
data = ["{0:04d}".format(i) for i in range(0, 10000)] # Normalize str length
rd.shuffle(data)
sorted_data = sorted(data)
reversed_sorted_data = sorted_data[::-1]
def test1():
g = list((i,j) if (i < j) else (j,i) for i, j in combinations(data, 2))
print('unsorted with conditional: ', len(g))
%timeit test1()
# unsorted with conditional: 49995000
# unsorted with conditional: 49995000
# unsorted with conditional: 49995000
# unsorted with conditional: 49995000
# 1 loops, best of 3: 20.7 s per loop
def test2():
g = list((i,j) if (i < j) else (j,i) for i, j in combinations(sorted_data, 2))
print('sorted with conditional: ', len(g))
%timeit test2()
# sorted with conditional: 49995000
# sorted with conditional: 49995000
# sorted with conditional: 49995000
# sorted with conditional: 49995000
# 1 loops, best of 3: 19.6 s per loop
def test3():
g = list((i,j) for i, j in combinations(data, 2))
print('unsorted without conditional: ', len(g))
%timeit test3()
# unsorted without conditional: 49995000
# unsorted without conditional: 49995000
# unsorted without conditional: 49995000
# unsorted without conditional: 49995000
# 1 loops, best of 3: 15.7 s per loop
def test4():
g = list((i,j) for i, j in combinations(sorted_data, 2))
print('sorted without conditional: ', len(g))
%timeit test4()
# sorted without conditional: 49995000
# sorted without conditional: 49995000
# sorted without conditional: 49995000
# sorted without conditional: 49995000
# 1 loops, best of 3: 15.3 s per loop
def test5():
g = list((i,j) for i, j in combinations(reversed_sorted_data, 2))
print('reverse sorted without conditional: ', len(g))
%timeit test5()
# reverse sorted without conditional: 49995000
# reverse sorted without conditional: 49995000
# reverse sorted without conditional: 49995000
# reverse sorted without conditional: 49995000
# 1 loops, best of 3: 15 s per loop