我正在处理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
我的问题是:哪种方式更有效......更干净......更好......等等还是有更好的方法。这两种方式都起作用并给出了相同的答案,但我认为我会将蜂巢头脑作为学习案例。
谢谢 -
答案 0 :(得分:6)
使用collections.Counter
。 Counter
是defaultdict(int)
的语法糖,但它很酷的是它在构造函数中接受了一个iterable,从而节省了一个额外的步骤(我假设你上面的所有例子都包含在for循环中。)
from collections import Counter
count = Counter(x[3] for x in my_csv_reader)
在引入collections.Counter
之前,collections.defaultdict
是此任务最惯用的,因此对于用户而言< 2.7,使用defaultdict
。
from collections import defaultdict
count = defaultdict(int)
for x in my_csv_reader:
count[x[3]] += 1
答案 1 :(得分: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
典型运行(在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的问题:“”“我很好奇,如果将iterable移动到Counter构造函数中会发生什么:d = Counter(可迭代)?(I有python 2.6并且无法测试它。)“”“
理货6:只需要d = Count(iterable); return d
,需要60.0毫秒
您可以查看源代码(SVN存储库中的collections.py)...这是我的Python27\Lib\collections.py
在iterable
不是Mapping实例时所执行的操作:
self_get = self.get
for elem in iterable:
self[elem] = self_get(elem, 0) + 1
以前在任何地方看过那段代码?只需调用可在Python 1.5.2 :-O
答案 2 :(得分:1)
from collections import Counter
Counter(a)
答案 3 :(得分:0)
由于您无法访问Counter,因此您最好的选择是第三种方法。它更清洁,更容易阅读。此外,它没有前两种方法的永久测试(和分支),这使得它更有效。
答案 4 :(得分:0)
使用setdefault
。
a[x[3]] = a.setdefault(x[3], 0) + 1
setdefault
获取指定键的值(在这种情况下为x[3]
),或者如果不存在,则获取指定值(在这种情况下为0
)。