在增加现有值的同时向字典添加新键

Cou*_*xer 8 python dictionary unique-key

我正在处理一个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
Run Code Online (Sandbox Code Playgroud)

第二种方式:

try:
    b[x[3]] += 1
except KeyError:
    b[x[3]] = 1
Run Code Online (Sandbox Code Playgroud)

第三种方式:

from collections import defaultdict
c = defaultdict(int)
c[x[3]] += 1
Run Code Online (Sandbox Code Playgroud)

我的问题是:哪种方式更有效......更干净......更好......等等还是有更好的方法.这两种方式都有效,并给出相同的答案,但我认为我会将蜂巢思维作为一个学习案例.

谢谢 -

Ste*_*ski 6

使用collections.Counter.Counter是语法糖defaultdict(int),但它的酷处在于它在构造函数中接受一个iterable,从而节省了一个额外的步骤(我假设你上面的所有例子都包含在for循环中.)

from collections import Counter
count = Counter(x[3] for x in my_csv_reader)
Run Code Online (Sandbox Code Playgroud)

之前引进的collections.Counter,collections.defaultdict是最地道的完成这个任务,所以对于用户<2.7,使用defaultdict.

from collections import defaultdict
count = defaultdict(int)
for x in my_csv_reader:
    count[x[3]] += 1
Run Code Online (Sandbox Code Playgroud)


Joh*_*hin 6

你问哪个更有效率.假设您正在谈论执行速度:如果您的数据很小,则无关紧要.如果它很大且很典型,那么"已经存在"的情况将比"不在字典"的情况更频繁地发生.这一观察结果解释了一些结果.

下面是一些可以与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
Run Code Online (Sandbox Code Playgroud)

典型运行(在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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

另一个计时试验:

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
Run Code Online (Sandbox Code Playgroud)

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

请注意,这Counter()不仅仅是"语法糖" defaultdict(int)- 它实现了一个完整的bagaka multiset对象 - 请参阅文档了解详细信息; 如果你需要一些花哨的后处理,它们可能会让你免于重新发明轮子.如果你想要做的就是数数,请使用defaultdict.

响应来自@Steven Rumbalski的问题更新:"""我很好奇,如果你将迭代器移动到Counter构造函数中会发生什么:d = Counter(可迭代)?(我有python 2.6并且无法测试它.)" ""

理货6:就是这样d = Count(iterable); return d,需要60.0 毫秒

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

            self_get = self.get
            for elem in iterable:
                self[elem] = self_get(elem, 0) + 1
Run Code Online (Sandbox Code Playgroud)

以前在任何地方看过那段代码?只需调用可在Python 1.5.2中运行的代码,就会有大量的随身携带:-O