【发布时间】:2016-07-02 16:32:50
【问题描述】:
我正在尝试在 Python 中复制一个 Perl 脚本,但遇到了一些主要的性能问题。
基本上,我有一个列表元组,一次处理一个列表。该列表包括 TYPE(字符串)、ID(字符串)、OVERALL_COUNT(整数)、TYPE_ID_COUNT(整数)和 DROP_KEEP_FLAG(字符串)。
我的目标是将数据读入内存高效且访问速度快的数据结构中。我想在每次处理新记录时修剪结构,修剪标准是如果当前 TYPE_ID_COUNT 超过 2 则不能包含任何 TYPE_ID_COUNT(即,如果历史 TYPE_ID_COUNT
当我将此与我的 Perl 代码进行比较时,它的速度要慢几个数量级。我已经包含了 2 个版本(我将 dict.keys() 替换为 dict)。我对 Python 比较陌生,所以我确信有更优化的方式来编写我的代码。 Numpy 数组会更好吗?
import timeit
from collections import defaultdict
from copy import deepcopy
# FIELDS: TYPE, ID, OVERALL_COUNT, TYPE_ID_COUNT, DROP_KEEP_FLAG
a = (['TYPE_1','000000001',1,1,'K'],['TYPE_2','000000002',2,1,'K'],['TYPE_3','000000001',3,1,'K'],
['TYPE_1','000000002',4,1,'K'],['TYPE_1','000000002',5,2,'K'],['TYPE_3','000000002',6,1,'K'],
['TYPE_1','000000002',7,3,'K'],['TYPE_1','000000002',8,4,'D'],['TYPE_1','000000002',9,5,'K'],
['TYPE_1','000000001',10,2,'K'],['TYPE_2','000000001',11,1,'K'],['TYPE_2','000000001',12,2,'K'],
['TYPE_2','000000001',13,3,'K'],['TYPE_3','000000001',14,2,'K'],['TYPE_3','000000001',15,3,'K'],
['TYPE_3','000000002',16,2,'K'],['TYPE_3','000000002',17,3,'K'],['TYPE_3','000000002',18,4,'K'])
expand = a
for x in range(0, 250):
expand = expand + a
window = 2
def version_1(data,window):
result = defaultdict(lambda: defaultdict(list))
output = {}
for i_idx,i_val in enumerate(data,start=1):
#ADD NEW ELEMENT IF IT IS A 'K'
if i_val[4] == 'K':
result[i_val[0]][i_val[1]].append(i_val[2])
# TRIM OLD ELEMENTS AND COMPUTE LENGTHS
for j_idx, j_key in enumerate(result.keys(),start=1):
j_val = result.get(j_key)
j_val_cp = deepcopy(j_val)
output[j_key] = 0
for k_idx, k_key in enumerate(j_val_cp.keys(),start=1):
k_val = j_val.get(k_key)
for item in (x for x in k_val if x < i_val[2] - window):
k_val.remove(item)
if not k_val:
del j_val[k_key]
if k_key == i_val[1]:
output[j_key] = len(k_val)
#print('Output ' + str(i_idx) + ': ID: ' + i_val[1] + ' , values: ' + str(output.items()))
return output
def version_2(data,window):
result = defaultdict(lambda: defaultdict(list))
output = {}
for i_idx,i_val in enumerate(data,start=1):
#ADD NEW ELEMENT IF IT IS A 'K'
if i_val[4] == 'K':
result[i_val[0]][i_val[1]].append(i_val[2])
# TRIM OLD ELEMENTS AND COMPUTE LENGTHS
for j_idx, j_key in enumerate(result,start=1):
j_val = result.get(j_key)
j_val_cp = deepcopy(j_val)
output[j_key] = 0
for k_idx, k_key in enumerate(j_val_cp,start=1):
k_val = j_val.get(k_key)
for item in (x for x in k_val if x < i_val[2] - window):
k_val.remove(item)
if not k_val:
del j_val[k_key]
if k_key == i_val[1]:
output[j_key] = len(k_val)
#print('Output ' + str(i_idx) + ': ID: ' + i_val[1] + ' , values: ' + str(output.items()))
return output
# timeit.timeit(version_1(2))
start_time = timeit.default_timer()
version_1(expand,2)
print(timeit.default_timer() - start_time)
start_time = timeit.default_timer()
version_2(expand,2)
print(timeit.default_timer() - start_time)
任何帮助将不胜感激!
【问题讨论】:
-
而不是遍历字典键,你可以不遍历项目本身,这肯定会提供一些好处,而不是迭代键然后使用该键查找项目。使用
itervalues()仅用于值,或iteritems用于键和值。 -
我建议你分析你的代码,看看它在哪里花费时间。请参阅问题How can you profile a Python script? 这是一件相当容易的事情,并且可能会告诉您要花时间优化哪些部分。
-
我不明白你的标准...你能扩展一下吗?您是说要保留最后两个 TYPE_ID_COUNT 还是删除所有 output[j_key] = 0 处丢弃输出中的数据,所以我无法弄清楚应该存在什么。为什么要重新处理每个值的所有内容?您不只是构建列表并在最后过滤它们吗?
-
您的 version2 代码返回
{'TYPE_1': 0, 'TYPE_2': 0, 'TYPE_3': 0},但我想不出对您的标准有任何解释可以做到这一点。你确定它正确吗? -
您有一个嵌套字典
result[TYPE][ID],但构建平面字典output[TYPE]。这行output[j_key] = len(k_val)不断覆盖您已经记录的ID,以支持碰巧处理的最后一个。那么为什么要处理其他的呢?
标签: python performance dictionary optimization hash