将元组列表的转换速度提高到dict

时间:2018-04-26 20:39:41

标签: python python-3.x

我有一个由长度为5的元组组成的列表l。前四个条目是字符串,最后一个是整数。 创建此类列表的虚函数可能如下所示:

import numpy as np
import uuid
def get_dummy_data(n=10000):
    l = []
    for i in range(n):
        name = np.random.choice(["Cat", "Dog", "Duck"], 1)[0]
        c_id = uuid.uuid4().hex
        t_id = uuid.uuid4().hex
        l.append((c_id, t_id, name, "canFly", 1))
        if np.random.random() < 0.8:
            l.append((c_id, t_id, name, "isHungry", 0))
    return l

现在这个列表l包含具有相同前三个元素但在后两个元素中不同的元组。这里通过再次附加相同的元组以80%的几率但更改最后两个元素来举例说明。

目标是将这个长度为5元组的列表转换为字典,其中键是元组的第一个条目(c_id),值的结构如下(t_id,(name,{&#34; isHungry&#34;:0}))或者:(t_id,(名称,{&#34; canFly&#34;:1,&#34; isHungry&#34;:0}))。

这可以通过以下循环实现:

res = {}
for y in l:
    if y[0] not in res:
        res[y[0]] = (y[1], (y[2], {y[3]: y[4]}))
    else:
        res[y[0]][1][1].update({y[3]: y[4]}) 

现在的问题是:我可以加快速度吗?列表l中可能有两个以上的元组具有相同的c_id(与get_dummy_data函数相反),我们不能假设l中的任何顺序。 我总是有一种不好的感觉,当做一个明确的循环来填充一个字典,所以我打赌有一个很好的方法来使这更快。

2 个答案:

答案 0 :(得分:2)

您可以进行基本的微优化,使您的代码更具可读性。较大的一个没有使用some_dict.update({x:y})而不是some_dict[x] = y。但是这里有一些时间差异:

In [12]: %%timeit
    ...: res = {}
    ...: for y in data:
    ...:     if y[0] not in res:
    ...:         res[y[0]] = (y[1], (y[2], {y[3]: y[4]}))
    ...:     else:
    ...:         res[y[0]][1][1].update({y[3]: y[4]})
    ...:
100 loops, best of 3: 15.3 ms per loop

In [13]: %%timeit
    ...: res = {}
    ...: for a,b,c,d,e in data:
    ...:     if a not in res:
    ...:         res[a] = (b, (c, {d: e}))
    ...:     else:
    ...:         res[a][1][1][d] = e
    ...:
100 loops, best of 3: 11 ms per loop

这是.update。请注意,每个y[...]都是一个方法调用,会减慢速度。但节省时间的最大部分是避免.update({...}。注意,这种方法需要创建一个完整的dict对象,没有充分的理由:

In [18]: %%timeit
    ...: res = {}
    ...: for a,b,c,d,e in data:
    ...:     if a not in res:
    ...:         res[a] = (b, (c, {d: e}))
    ...:     else:
    ...:         res[a][1][1].update({d:e})
    ...:
100 loops, best of 3: 13.8 ms per loop

答案 1 :(得分:1)

这种循环通常很慢:

res = {}
for y in l:
    if y[0] not in res:
        res[y[0]] = (y[1], (y[2], {y[3]: y[4]}))
    else:
        res[y[0]][1][1].update({y[3]: y[4]}) 

因为您正在测试密钥是否属于字典两次且是if/else语句。

我会在lambda&amp;中使用变量的绑定属性。拆包(从juanpa回答借来):

import collections
res = collections.defaultdict(lambda : (b, (c, {d: e})))

for a,b,c,d,e in l:
    res[a][1][1][d] = e

如果密钥不在字典defaultdict中,则使用ab ...的当前值创建密钥,(感谢lambda评估值时的值执行,而不是在声明时)保存测试并每次创建正确的密钥。现在update部分有点多余,但它仍然应该更快,因为没有if/then测试。

这个解决方案比我的机器上的juanpa(已经很好)答案快(0.23秒vs 0.27秒)。我认为这是一个很好的合作努力,因为我的第一个版本速度较慢。