【问题标题】:Python Dictionary OptimizationPython 字典优化
【发布时间】: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


【解决方案1】:

我很难理解你的问题,所以我不知道你想要的输出应该是什么。在 python 中使用大量嵌套循环是一个坏主意。您是否尝试过使用查找表方法来获取要过滤的项目?例如:

# a is your 'historical' data
# Now create a look-up table to access elements of interest:
types = defaultdict(list)

for i, item in enumerate(a):
    t = item[0]
    types[t].append(i)

# You can now use your look-up table to grab items of interest e.g:
new_record = ['TYPE_3','000000002',100,2,'K']
type_idxs = types[new_record[0]]

old_types_id_counts = [a[i][2] for i in type_idxs]

# Collect the indexes of the items you want to remove:
f = [type_idxs[i] for i in range(len(type_idxs)) if old_types_id_counts[i] < current_id_count - 2]

# Once you have your indexes of items to remove, you just need to remove those items and update your table

【讨论】:

    猜你喜欢
    • 2020-02-05
    • 2011-06-21
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多