ヒストグラムを計算するPythonプログラムを書く方法はたくさんあります。
ヒストグラムとは、iterable
内のオブジェクトの発生をカウントし、そのカウントを辞書に出力する関数を意味します。例えば:
_>>> L = 'abracadabra'
>>> histogram(L)
{'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
_
この関数を記述する1つの方法は次のとおりです。
_def histogram(L):
d = {}
for x in L:
if x in d:
d[x] += 1
else:
d[x] = 1
return d
_
この関数を記述するより簡潔な方法はありますか?
Pythonで辞書の理解があれば、次のように書くことができます。
_>>> { x: L.count(x) for x in set(L) }
_
しかし、Python 2.6にはそれらがないので、次のように書かなければなりません:
_>>> dict([(x, L.count(x)) for x in set(L)])
_
このアプローチは読みやすいかもしれませんが、効率的ではありません。Lは複数回ウォークスルーされます。さらに、これは単一寿命のジェネレーターでは機能しません。この関数は、次のようなイテレータジェネレータでも同様に機能する必要があります。
_def gen(L):
for x in L:
yield x
_
reduce
関数(R.I.P.)を使用しようとする場合があります。
_>>> reduce(lambda d,x: dict(d, x=d.get(x,0)+1), L, {}) # wrong!
_
おっと、これは機能しません。キー名はx
ではなく_'x'
_です。 :(
で終わった:
_>>> reduce(lambda d,x: dict(d.items() + [(x, d.get(x, 0)+1)]), L, {})
_
(Python 3では、list(d.items())
の代わりにd.items()
を記述する必要がありますが、reduce
がないため、仮定に基づいています。そこ。)
より良く、より読みやすいワンライナーで私を打ち負かしてください! ;)
Python 3.xにはreduce
があります。ただfrom functools import reduce
。また、「dict内包表記」もあります。これは、例の構文とまったく同じです。
Python 2.7および3.xには Counter クラスもあり、これはまさに必要なことを行います。
from collections import Counter
cnt = Counter("abracadabra")
Python 2.6以前では、個人的に defaultdict を使用し、2行で実行します。
d = defaultdict(int)
for x in xs: d[x] += 1
それは、クリーンで効率的、Python的なものであり、ほとんどの人がreduce
を含むものよりも理解しやすいものです。
Onelinersのモジュールをインポートするのはちょっとずるいので、ここではO(n)であり、少なくともPython2.4で動作するonelinerがあります
>>> f=lambda s,d={}:([d.__setitem__(i,d.get(i,0)+1) for i in s],d)[-1]
>>> f("ABRACADABRA")
{'A': 5, 'R': 2, 'B': 2, 'C': 1, 'D': 1}
そしてあなたが__
メソッドはハックです。いつでも実行できます
>>> f=lambda s,d=lambda:0:vars(([setattr(d,i,getattr(d,i,0)+1) for i in s],d)[-1])
>>> f("ABRACADABRA")
{'A': 5, 'R': 2, 'B': 2, 'C': 1, 'D': 1}
:)
$d{$_} += 1 for split //, 'abracadabra';
import pandas as pd
pd.Series(list(L)).value_counts()
python 2.7の場合、この小さなリストの内包表記を使用できます。
v = list('abracadabra')
print {x: v.count(x) for x in set(v)}
2.3に戻ります(Timmermanのものより少し短く、読みやすいと思います):
L = 'abracadabra'
hist = {}
for x in L: hist[x] = hist.pop(x,0) + 1
print hist
{'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1}
python 2.2から2.7までで動作するヒストグラム実装が必要でしたが、これを思いつきました:
>>> L = 'abracadabra'
>>> hist = {}
>>> for x in L: hist[x] = hist.setdefault(x,0)+1
>>> print hist
{'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1}
Eli Courtwrightのdefaultdictの投稿に触発されました。これらはpython 2.5で導入されたため使用できません。しかし、dict.setdefault(key、default)でエミュレートできます。
これは基本的にgnibblerが行っていることと同じですが、彼のラムダ関数を完全に理解する前に最初にこれを書く必要がありました。
reduce
を使用するワンライナーはほとんど問題ありませんでした。少し調整するだけで済みました。
_>>> reduce(lambda d, x: dict(d, **{x: d.get(x, 0) + 1}), L, {})
{'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
_
もちろん、これはその場での解決策に勝るものではありません(速度もPythonicityも)。代わりに、あなたは自分でニースの純粋に機能的なスニペットを手に入れました。ところで、これはPythonにメソッドdict.merge()
があった場合、いくらかきれいになります。
しばらくの間、itertools
を使用するものはすべて定義上Pythonicでした。それでも、これは不透明な面に少しあります:
>>> from itertools import groupby
>>> grouplen = lambda grp : sum(1 for i in grp)
>>> hist = dict((a[0], grouplen(a[1])) for a in groupby(sorted("ABRACADABRA")))
>>> print hist
{'A': 5, 'R': 2, 'C': 1, 'B': 2, 'D': 1}
現在、Python 2.5.4。