web-dev-qa-db-ja.com

pythonヒストグラムワンライナー

ヒストグラムを計算する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がないため、仮定に基づいています。そこ。)

より良く、より読みやすいワンライナーで私を打ち負かしてください! ;)

47
mykhal

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を含むものよりも理解しやすいものです。

76
Eli Courtwright

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}

:)

7
John La Rooy
$d{$_} += 1 for split //, 'abracadabra';
6
perl
import pandas as pd

pd.Series(list(L)).value_counts()
6
mirandes

python 2.7の場合、この小さなリストの内包表記を使用できます。

v = list('abracadabra')
print {x: v.count(x) for x in set(v)}
5
Walter Cacau

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}
4
dgulino

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が行っていることと同じですが、彼のラムダ関数を完全に理解する前に最初にこれを書く必要がありました。

1
Jens Timmerman

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()があった場合、いくらかきれいになります。

1
tokland

しばらくの間、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。

1
PaulMcG