【问题标题】:make a dictionary out of first elements in a list of list用列表列表中的第一个元素制作字典
【发布时间】:2015-01-28 13:27:11
【问题描述】:

这是一个关于在字典理解内的列表理解上使用 set() 与字典理解和重复分配给新字典的性能的问题

所以我碰巧有一个数据集,它是一个列表列表,我需要在大列表中的每个列表中获取索引为“0”的唯一元素列表,以便能够制作来自他们的新字典.. 像 dict.fromkeys() .. 在这里我需要提供唯一键列表..

我正在使用

[1]{ x : [] for x in set([i[0] for i in data])}

也使用

[2]{ i[0] : [] for i in data}

此处供参考的示例数据可能如下: [[1,3,4], [3,5,2], [1,5,2]]

上面运行 [1] 和 [2] 的结果将是: { 1:[], 3: [] }

我在这两个语句上都尝试了 %timeit 并且都给出了几乎相同的结果,这使得很难确定哪个是最好的,性能方面,对于大列表列表

我如何在这里识别潜在的瓶颈?

编辑:

如果这有助于解释结果..

In [172]: data_new = data * 10000

In [173]: %timeit { i[0] : [] for i in data_new}
10 loops, best of 3: 160 ms per loop

In [174]: %timeit { x : [] for x in set([i[0] for i in data_new])}
10 loops, best of 3: 131 ms per loop

In [175]: data_new = data * 100000

In [176]: %timeit { x : [] for x in set([i[0] for i in data_new])}
1 loops, best of 3: 1.37 s per loop

In [177]: %timeit { i[0] : [] for i in data_new}
1 loops, best of 3: 1.58 s per loop

In [178]: data_new = data * 1000000

In [179]: %timeit { i[0] : [] for i in data_new}
1 loops, best of 3: 15.8 s per loop

In [180]: %timeit { x : [] for x in set([i[0] for i in data_new])}
1 loops, best of 3: 13.6 s per loop

【问题讨论】:

  • 生成大量随机数据并使用它并重新计时... 2 最终会更快出来,因为它做的工作更少(例如:不是建立一个列表,然后建立一个集合)
  • 时间上的差异等于在#1 中产生额外的set 所花费的时间......在百万元素列表上只有几毫秒。换句话说,在别处寻找瓶颈。
  • 您不能在此处使用dict.fromkeys,因为dict.fromkeys(..., []) 将共享列表,这可能是您不想要的
  • @thefourtheye 我同意。我没有。提到它只是为了让我了解我在这里想要实现的目标..
  • 更快的是使用 set-comp { x: [] for x in {i[0] for i in data}}... - 这意味着 dicts 替换重复键是瓶颈

标签: python python-2.7 set list-comprehension dictionary-comprehension


【解决方案1】:

构建一个更大的数据集,然后计时:

代码:

import random
data = [ [random.randint(1, 9) for _ in range(3)] for _ in range(1000000)]

时间安排:

%timeit { x : [] for x in set([i[0] for i in data])}
# 10 loops, best of 3: 94.6 ms per loop
%timeit { i[0] : [] for i in data}
# 10 loops, best of 3: 106 ms per loop
%timeit { x: [] for x in set(i[0] for i in data)}
# 10 loops, best of 3: 114 ms per loop
%timeit { x: [] for x in {i[0] for i in data}}
# 10 loops, best of 3: 77.7 ms per loop

理由

首先限制可用键空间意味着字典只需分配(给定上面的randint)9 个唯一键给 9 个新列表。使用 dict comp 时,字典必须重复创建并将其键的值重新分配给 新创建的 列表...不同之处在于释放丢弃的空列表(即垃圾)的开销收集)以及创建一个 new 空列表所花费的时间。

鉴于randint 的均匀分布,那么在一组 1,000,000 个元素中,有 9 个唯一值的空列表分配和取消分配 111,111 次——这远远超过 9 个。

【讨论】:

    【解决方案2】:

    这取决于您期望的重复次数。在较短的代码中,为输入列表中的每个项目创建了一个空列表,这非常昂贵。使用静态值,越短越快。

    下面L = [[1,3,4], [3,5,2], [1,5,2]] * 100000

    In [1]: %timeit { x : [] for x in {i[0] for i in L]}}
    10 loops, best of 3: 58.9 ms per loop
    
    In [2]: %timeit { i[0] : [] for i in L}
    10 loops, best of 3: 68.1 ms per loop
    

    现在在这里使用常量None 值进行测试:

    In [3]: %timeit { x : None for x in set([i[0] for i in L])}
    10 loops, best of 3: 59 ms per loop
    
    In [4]: %timeit { i[0] : None for i in L}
    10 loops, best of 3: 54.3 ms per loop
    

    因此,不必要的列表创建使较短的列表执行缓慢,而使用恒定值绝对更快。


    我没有适用于 Python 2 的 ipython,而且我有点懒惰地计时,但您会想注意到 Python 2.7 支持 集合推导,至少在 Python 3.4 上要快得多而不是从列表中创建集合:

    In [7]: %timeit { x : [] for x in {i[0] for i in L}}
    10 loops, best of 3: 48.9 ms per loop
    

    【讨论】:

      猜你喜欢
      • 2022-07-10
      • 1970-01-01
      • 2018-01-17
      • 2020-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-15
      相关资源
      最近更新 更多