【问题标题】:Incrementing dict values with dict comprehension用 dict 理解增加 dict 值
【发布时间】:2023-03-23 00:10:01
【问题描述】:

我正在尝试在 python 中使用字典理解和三元运算来执行以下表达式:

for num in ar:
     if num in seen_dict:
         seen_dict[num] += 1
     else:
         seen_dict[num] = 1

我试过了:

seen_dict = { num: seen_dict[num] += 1 for num in ar if num in seen_dict else seen_dict[num] = 1}

及其几种排列,但我不断收到语法错误。有没有可能做我想做的事?

更新

这是正确的语法,但不是我的 dict 只返回 1: seen_dict = { num: (seen_dict[num] + 1) if num in seen_dict else 1 for num in ar }

有人可以解释为什么这与 for 循环的功能不同吗?谢谢。

【问题讨论】:

  • 您不能在字典理解中使用赋值或递增运算符

标签: python python-3.x ternary dictionary-comprehension


【解决方案1】:

不要。 似乎为此使用 dict 理解应该是一个好主意,但它实际上是一个可怕的陷阱。使用collections.Counter:

import counts

seen_dict = collections.Counter(ar)

或者如果你不想这样做,那就坚持循环。

尝试使用字典推导的问题在于字典推导没有好的方法来维护状态或交错计算每个键的值。每个值都必须在单个表达式中计算。相比之下,解决计数问题的最佳方法是对ar 进行一次遍历,并随时更新每个元素的计数。

理解的限制会导致非常低效的尝试,例如

seen_dict = {val: ar.count(val) for val in ar}

这使得通过ar 的次数等于ar 的长度,或者效率稍高但仍然非常不理想

seen_dict = {val: ar.count(val) for val in set(ar)}

只需要让len(set(ar))通行证,或者为人a bit more familiar with the standard library

from itertools import groupby
seen_dict = {val: sum(1 for _ in group) for val, group in groupby(sorted(ar))}

这至少不是二次时间,但对于长度为 n 的 ar 仍然是 O(nlogn)。

如果我们 run a timing 这四个 sn-ps 输入 list(range(10000)):

from collections import Counter
from itertools import groupby
from timeit import timeit

ar = list(range(10000))

print(timeit('Counter(ar)', number=1, globals=globals()))
print(timeit('{val: ar.count(val) for val in ar}', number=1, globals=globals()))
print(timeit('{val: ar.count(val) for val in set(ar)}', number=1, globals=globals()))
print(timeit('{val: sum(1 for _ in group) for val, group in groupby(sorted(ar))}',
             number=1, globals=globals()))

我们得到以下输出:

0.0005530156195163727
1.0503493696451187
1.0463058911263943
0.00422721728682518

Counter 在半毫秒内完成,而count sn-ps 都需要一秒钟。 (set 版本似乎具有较低的运行时间,因为某种首次运行效果会减慢另一个版本的速度;交换set 和非set 版本的顺序通常会颠倒这些版本的相对时间. set 的重复数据删除对这个测试没有帮助,因为输入没有重复。)

对于更长的输入,依赖count 将更加昂贵。依靠count 很容易花费几天的时间来输入Counter 仍然会在一秒钟内完成。

【讨论】:

  • 称其为可怕的陷阱很可笑。谢谢你的回答。
  • 我很感兴趣,为什么集合完成的速度比正常列表快,因为 ar 只有不同的值。
  • @teclnol:可能只是不相关的时序变化。 set 调用只是该输入运行时间的一小部分;几乎所有的运行时间都花在了count 调用上。
  • 经过更多测试后,它看起来太一致了,不可能是随机变化。它看起来更像是首先必须在某个地方做更多工作的那个。
【解决方案2】:

在这里,它实际上比我想象的要简单得多。基本上你想要的是列表中出现的次数,你可以通过说ar.count(num)来做到这一点。您可以轻松地做到这一点,而无需像这样的三元运算符:

ar = [1,2,3,2]
seen_dict = { num:ar.count(num) for num in ar}
print(seen_dict)# {1:1, 2:2, 3:1}

【讨论】:

  • 这是非常低效的(大多数尝试使用 dict 理解来执行聚合的常见属性)。每个count 调用都需要完整传递arr
  • 我明白了,虽然效率低下,但它正在执行操作要求的操作。
【解决方案3】:

您似乎正在尝试获取列表中所有值的外观。 (如果不是,请告诉我。)以下是我的处理方法:

seen_dict = {num: arr.count(num) for num in list(set(arr))}

解释:

  • arr.count(num)list.count(element)方法返回elementlist中出现的次数
  • set(arr):创建一个 set 对象,将其转换回列表时会删除所有重复项,或者换句话说,获取列表的所有不同值
  • list(set(arr))arr 中的不同值

字典返回将有 number-# of appearances of number in arr 的键值对。

【讨论】:

  • 哇,我们同时得到了相同的答案。
  • 这是非常低效的(大多数尝试使用 dict 理解来执行聚合的常见属性)。每个count 调用都需要完整传递arr
  • 嗯,你是对的。有没有其他方法可以在不经过原始arr 的情况下做到这一点?
  • 事实证明,为此使用 dict 理解只是一个可怕的陷阱。一开始它似乎应该是一个好主意,但 dict 理解永远不会像正常循环那样有效。
  • 感谢您提供的所有有用反馈! .count 方法的答案和解释为什么效率低下!
猜你喜欢
  • 1970-01-01
  • 2018-07-05
  • 1970-01-01
  • 2018-01-12
  • 1970-01-01
  • 2020-09-17
  • 2021-06-08
  • 2021-07-11
  • 1970-01-01
相关资源
最近更新 更多