Python:优化比较两个整数的列表理解
Python: Optimising a list comprehension which compares two integers
我有一个作用于两个整数列表的列表理解。它的作用类似于 itertools.product
,带有过滤器以丢弃两者相等的元素并进行比较以对它们进行排序。
代码如下:
to_add = [(min(atom_1, atom_2), max(atom_1, atom_2))
for atom_1 in atoms_1 for atom_2 in atoms_2
if atom_2 != atom_1]
add_dict = coll.defaultdict(list)
for k, v in to_add:
add_dict[k].append(v)
我在写的时候看到的最明显的事情是不需要先调用min
再调用max
。我真正想要的是 min
和另一个,但我想不出如何摆脱对 max
.
的冗余调用
我对其进行了分析并得到了以下结果,这些结果代表了 10 次重复(read_amber.py
是总体函数调用的名称):
62880808 function calls (62880792 primitive calls) in 14.746 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
19 6.786 0.357 10.688 0.563 read_amber.py:256(add_exclusions)
16431524 1.625 0.000 1.625 0.000 {min}
16431511 1.295 0.000 1.295 0.000 {max}
842947 1.051 0.000 1.051 0.000 {method 'format' of 'str' objects}
842865 1.031 0.000 1.557 0.000 {filter}
16457861 0.838 0.000 0.838 0.000 {method 'append' of 'list' objects}
1 0.793 0.793 3.757 3.757 read_amber.py:79(write_to)
8414872 0.526 0.000 0.526 0.000 read_amber.py:130(<lambda>)
1685897 0.266 0.000 0.266 0.000 {method 'write' of 'file' objects}
97489 0.142 0.000 0.142 0.000 {sorted}
1 0.130 0.130 0.300 0.300 read_amber.py:32(read_from)
247198 0.127 0.000 0.155 0.000 read_amber.py:134(data_cast)
848267/848263 0.042 0.000 0.042 0.000 {len}
1 0.038 0.038 0.038 0.038 read_amber.py:304(update_exclusion_list)
500352 0.028 0.000 0.028 0.000 {method 'lower' of 'str' objects}
有没有办法摆脱其中一个多余的 min/max
调用?还有其他明显的方法可以加快此代码段的速度吗?
我已经尝试过使用 itertools
生成器,但列表理解速度更快。我还尝试了 sorted
和必要的转换,但 min/max
比那快。
最后,我是 cProfile
的新手。按 'tottime'
排序是否合理?
怎么样:
import collections as coll
import itertools
add_dict = coll.defaultdict(list)
for atom_1, atom_2 in itertools.product(atoms_1, atoms_2):
if atom_1 == atom_2: continue
(atom_min, atom_max) = (atom_1, atom_2) if atom_1 < atom_2 else (atom_2, atom_1)
add_dict[atom_min].append(atom_max)
或者,如果额外的作业是个问题(我认为这不重要):
add_dict = coll.defaultdict(list)
for atom_1, atom_2 in itertools.product(atoms_1, atoms_2):
if atom_1 == atom_2: continue
if atom_1 < atom_2:
add_dict[atom_1].append(atom_2)
else:
add_dict[atom_2].append(atom_1)
虽然这看起来不太可读。
编辑: 时间结果:
看起来这种方法将运行时间减少了一半。
import collections as coll
import itertools
atoms_1 = [1,2,3,4,5,6]
atoms_2 = [2,4,6,1,2,3]
def old():
to_add = [(min(atom_1, atom_2), max(atom_1, atom_2)) for atom_1 in atoms_1 for atom_2 in atoms_2 if atom_2 != atom_1]
add_dict = coll.defaultdict(list)
for k, v in to_add:
add_dict[k].append(v)
return add_dict
def new():
add_dict = coll.defaultdict(list)
for atom_1, atom_2 in itertools.product(atoms_1, atoms_2):
if atom_1 == atom_2: continue
(atom_min, atom_max) = (atom_1, atom_2) if atom_1 < atom_2 else (atom_2, atom_1)
add_dict[atom_min].append(atom_max)
return add_dict
import timeit
print(timeit.timeit("old()", setup="from __main__ import old")) # 20.76972103
print(timeit.timeit("new()", setup="from __main__ import new")) # 10.9827100827
编辑 2: timeit 结果——更长的列表,更少的 timeit 迭代
atoms_1 = [1,2,3,4,5,6] * 5
atoms_2 = [2,4,6,1,2,3] * 5
print(timeit.timeit("old()", setup="from __main__ import old", number=100000)) # 46.2878425701
print(timeit.timeit("new()", setup="from __main__ import new", number=100000)) # 21.9272824532
我有一个作用于两个整数列表的列表理解。它的作用类似于 itertools.product
,带有过滤器以丢弃两者相等的元素并进行比较以对它们进行排序。
代码如下:
to_add = [(min(atom_1, atom_2), max(atom_1, atom_2))
for atom_1 in atoms_1 for atom_2 in atoms_2
if atom_2 != atom_1]
add_dict = coll.defaultdict(list)
for k, v in to_add:
add_dict[k].append(v)
我在写的时候看到的最明显的事情是不需要先调用min
再调用max
。我真正想要的是 min
和另一个,但我想不出如何摆脱对 max
.
我对其进行了分析并得到了以下结果,这些结果代表了 10 次重复(read_amber.py
是总体函数调用的名称):
62880808 function calls (62880792 primitive calls) in 14.746 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
19 6.786 0.357 10.688 0.563 read_amber.py:256(add_exclusions)
16431524 1.625 0.000 1.625 0.000 {min}
16431511 1.295 0.000 1.295 0.000 {max}
842947 1.051 0.000 1.051 0.000 {method 'format' of 'str' objects}
842865 1.031 0.000 1.557 0.000 {filter}
16457861 0.838 0.000 0.838 0.000 {method 'append' of 'list' objects}
1 0.793 0.793 3.757 3.757 read_amber.py:79(write_to)
8414872 0.526 0.000 0.526 0.000 read_amber.py:130(<lambda>)
1685897 0.266 0.000 0.266 0.000 {method 'write' of 'file' objects}
97489 0.142 0.000 0.142 0.000 {sorted}
1 0.130 0.130 0.300 0.300 read_amber.py:32(read_from)
247198 0.127 0.000 0.155 0.000 read_amber.py:134(data_cast)
848267/848263 0.042 0.000 0.042 0.000 {len}
1 0.038 0.038 0.038 0.038 read_amber.py:304(update_exclusion_list)
500352 0.028 0.000 0.028 0.000 {method 'lower' of 'str' objects}
有没有办法摆脱其中一个多余的 min/max
调用?还有其他明显的方法可以加快此代码段的速度吗?
我已经尝试过使用 itertools
生成器,但列表理解速度更快。我还尝试了 sorted
和必要的转换,但 min/max
比那快。
最后,我是 cProfile
的新手。按 'tottime'
排序是否合理?
怎么样:
import collections as coll
import itertools
add_dict = coll.defaultdict(list)
for atom_1, atom_2 in itertools.product(atoms_1, atoms_2):
if atom_1 == atom_2: continue
(atom_min, atom_max) = (atom_1, atom_2) if atom_1 < atom_2 else (atom_2, atom_1)
add_dict[atom_min].append(atom_max)
或者,如果额外的作业是个问题(我认为这不重要):
add_dict = coll.defaultdict(list)
for atom_1, atom_2 in itertools.product(atoms_1, atoms_2):
if atom_1 == atom_2: continue
if atom_1 < atom_2:
add_dict[atom_1].append(atom_2)
else:
add_dict[atom_2].append(atom_1)
虽然这看起来不太可读。
编辑: 时间结果:
看起来这种方法将运行时间减少了一半。
import collections as coll
import itertools
atoms_1 = [1,2,3,4,5,6]
atoms_2 = [2,4,6,1,2,3]
def old():
to_add = [(min(atom_1, atom_2), max(atom_1, atom_2)) for atom_1 in atoms_1 for atom_2 in atoms_2 if atom_2 != atom_1]
add_dict = coll.defaultdict(list)
for k, v in to_add:
add_dict[k].append(v)
return add_dict
def new():
add_dict = coll.defaultdict(list)
for atom_1, atom_2 in itertools.product(atoms_1, atoms_2):
if atom_1 == atom_2: continue
(atom_min, atom_max) = (atom_1, atom_2) if atom_1 < atom_2 else (atom_2, atom_1)
add_dict[atom_min].append(atom_max)
return add_dict
import timeit
print(timeit.timeit("old()", setup="from __main__ import old")) # 20.76972103
print(timeit.timeit("new()", setup="from __main__ import new")) # 10.9827100827
编辑 2: timeit 结果——更长的列表,更少的 timeit 迭代
atoms_1 = [1,2,3,4,5,6] * 5
atoms_2 = [2,4,6,1,2,3] * 5
print(timeit.timeit("old()", setup="from __main__ import old", number=100000)) # 46.2878425701
print(timeit.timeit("new()", setup="from __main__ import new", number=100000)) # 21.9272824532