【问题标题】:Add new keys to a dictionary while incrementing existing values在增加现有值的同时向字典添加新键
【发布时间】:2010-10-27 18:32:30
【问题描述】:

我正在处理一个 CSV 文件并计算第 4 列的唯一值。到目前为止,我已经编写了这三种方式。一个使用“if key in dictionary”,第二个捕获 KeyError,第三个使用“DefaultDictionary”。例如(其中 x[3] 是文件中的值,“a”是字典):

第一种方式:

if x[3] in a:
    a[x[3]] += 1
else:
    a[x[3]] = 1

第二种方式:

try:
    b[x[3]] += 1
except KeyError:
    b[x[3]] = 1

第三种方式:

from collections import defaultdict
c = defaultdict(int)
c[x[3]] += 1

我的问题是:哪种方式更有效...更清洁...更好...等等。或者有更好的方法。两种方法都有效并给出相同的答案,但我认为我会利用蜂巢思维作为学习案例。

谢谢-

【问题讨论】:

  • 附加编辑:我正在运行 2.7 版。应该早点添加这个!
  • Counter 较慢 但是它比defaultdict(int) 有更多的功能——请看我的回答。
  • 你能增加多个值吗?

标签: python dictionary unique-key


【解决方案1】:

使用collections.CounterCounterdefaultdict(int) 的语法糖,但它很酷的是它在构造函数中接受一个可迭代对象,从而节省了一个额外的步骤(我假设您上面的所有示例都包含在一个 for 循环中。)

from collections import Counter
count = Counter(x[3] for x in my_csv_reader)

在引入collections.Counter之前,collections.defaultdict是这个任务最惯用的,所以对于defaultdict。

from collections import defaultdict
count = defaultdict(int)
for x in my_csv_reader:
    count[x[3]] += 1

【讨论】:

  • 我将在此澄清中声明新手身份。 “for x”部分中的“x”是否指的是 csv.reader 返回的单个行?我“认为”我实现了 csv 阅读器: my_csv_reader = csv.reader(open('c:/Test.csv', 'rb'), delimiter=',', quotechar='|') d = Counter(x[ 3] for x in my_csv_reader) 但 d 是其他三种方法的两倍多。
  • 是的,x 指的是 csv 阅读器返回的行。 d 是原来的两倍多是什么意思?它是否给出了错误的计数?请澄清。
  • 澄清:我的代码不正确,我的问题不完整!前三个方法周围有一个 if 语句,通过在将值添加到字典之前将当前行与前一行匹配来删除重复值。新方法不在那个条件下,所以我得到了所有行的总数,而不是唯一的行!
  • @Count Boxer 我明白了,您肯定需要围绕它编写代码。一种方法是使用set。比如my_data = set(my_csv_reader),然后是d = Counter(x[3] for x in my_data)
  • @everybody:计数器速度较慢。看我的回答。
【解决方案2】:

您问哪个更有效。假设您正在谈论执行速度:如果您的数据很小,那没关系。如果它是大而典型的,“已经存在”的情况将比“不在字典中”的情况发生得更频繁。这一观察结果解释了一些结果。

下面是一些代码,可以与timeit 模块一起使用,以在没有文件读取开销的情况下探索速度。我冒昧地添加了第 5 种方法,它并非没有竞争力,并且可以在至少 1.5.2 [测试] 之后的任何 Python 上运行。

from collections import defaultdict, Counter

def tally0(iterable):
    # DOESN'T WORK -- common base case for timing
    d = {}
    for item in iterable:
        d[item] = 1
    return d

def tally1(iterable):
    d = {}
    for item in iterable:
        if item in d:
            d[item] += 1
        else:
            d[item] = 1
    return d

def tally2(iterable):
    d = {}
    for item in iterable:
        try:
            d[item] += 1
        except KeyError:
            d[item] = 1
    return d

def tally3(iterable):
    d = defaultdict(int)
    for item in iterable:
        d[item] += 1

def tally4(iterable):
    d = Counter()
    for item in iterable:
        d[item] += 1

def tally5(iterable):
    d = {}
    dg = d.get
    for item in iterable:
        d[item] = dg(item, 0) + 1
    return d

典型运行(在 Windows XP“命令提示符”窗口中):

prompt>\python27\python -mtimeit -s"t=1000*'now is the winter of our discontent made glorious summer by this son of york';import tally_bench as tb" "tb.tally1(t)"
10 loops, best of 3: 29.5 msec per loop

以下是结果(每循环毫秒):

0 base case   13.6
1 if k in d   29.5
2 try/except  26.1
3 defaultdict 23.4
4 Counter     79.4
5 d.get(k, 0) 29.2

另一个计时试验:

prompt>\python27\python -mtimeit -s"from collections import defaultdict;d=defaultdict(int)" "d[1]+=1"
1000000 loops, best of 3: 0.309 usec per loop

prompt>\python27\python -mtimeit -s"from collections import Counter;d=Counter()" "d[1]+=1"
1000000 loops, best of 3: 1.02 usec per loop

Counter 的速度可能是由于它部分用 Python 代码实现,而 defaultdict 完全用 C 语言实现(至少在 2.7 中)。

请注意,Counter() 不仅仅是defaultdict(int) 的“语法糖”——它实现了一个完整的bag 又名multiset 对象——有关详细信息,请参阅文档;如果您需要一些花哨的后期处理,它们可能会让您免于重新发明轮子。如果您只想计算事物,请使用defaultdict

更新以回应@Steven Rumbalski 的问题:“””我很好奇,如果将可迭代对象移入 Counter 构造函数会发生什么情况:d = Counter(iterable)?(我有python 2.6,无法测试。) """

tally6:d = Count(iterable); return d,需要 60.0 毫秒

您可以查看源代码(SVN 存储库中的collections.py)...这是我的Python27\Lib\collections.pyiterable 不是映射实例时所做的:

            self_get = self.get
            for elem in iterable:
                self[elem] = self_get(elem, 0) + 1

以前在任何地方见过该代码吗?只是为了调用可在 Python 1.5.2 中运行的代码:-O

【讨论】:

  • 不错的时机。我很好奇,如果将可迭代对象移到 Counter 构造函数中会发生什么情况:d = Counter(iterable)? (我有 python 2.6,无法测试。)
【解决方案3】:
from collections import Counter
Counter(a)

【讨论】:

  • "a" 是我的字典。这是“计数器(x[3])吗?
  • “计数器”不是 3.1 中的新功能吗?我正在运行 2.7。
  • @Count: it exist in 2.7
  • 在 Count Boxer 的示例中,a 是他的计数字典,而不是他的源数据。在他的 3 个示例中隐含的是,它们位于遍历他的 csv 文件的 for 循环体中。说了这么多,a 不应该传递给构造函数,因为这是他的结果,而不是他的来源。
  • 该死!被我的 IDE 骗了!它没有出现在下拉列表中,所以我只是假设...
【解决方案4】:

由于您无权访问Counter,因此最好的选择是您的第三种方法。它更干净,更容易阅读。此外,它没有前两种方法所具有的永久测试(和分支),这使得它更有效。

【讨论】:

    【解决方案5】:

    使用setdefault

    a[x[3]] = a.setdefault(x[3], 0) + 1
    

    setdefault 获取指定键的值(本例中为x[3]),如果不存在,则获取指定值(本例中为0)。

    【讨论】:

    • 请解释为什么您认为d[k] = d.setdefault(k, 0) + 1d[k] = d.get(k, 0) + 1 更好
    • 是的,你是对的。我正在考虑它,因为它会设置值,但无论如何你都在这样做。
    猜你喜欢
    • 2015-05-01
    • 1970-01-01
    • 2013-05-20
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多