Frage

Es gibt viele Möglichkeiten, um ein Python-Programm zu schreiben, das ein Histogramm berechnet.

Mit dem Histogramm, meine ich eine Funktion, die zählt das Auftreten von Objekten in einem iterable und gibt die Zählungen in einem Wörterbuch. Zum Beispiel:

>>> L = 'abracadabra'
>>> histogram(L)
{'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}

Eine Möglichkeit, diese Funktion zu schreiben:

def histogram(L):
    d = {}
    for x in L:
        if x in d:
            d[x] += 1
        else:
            d[x] = 1
    return d

Gibt es prägnante Möglichkeiten, um diese Funktion zu schreiben?

Wenn wir Wörterbuch Comprehensions in Python hätten, könnten wir schreiben:

>>> { x: L.count(x) for x in set(L) }

aber da Python 2.6 nicht sie haben, müssen wir schreiben:

>>> dict([(x, L.count(x)) for x in set(L)])

Obwohl dieser Ansatz lesbar sein kann, ist es nicht effizient: L ging Durch mehrere Male. Darüber hinaus wird diese Arbeit nicht für Single-Leben-Generatoren;

: Die Funktion sollte für Iterator Generatoren wie gleich gut funktionieren
def gen(L):
    for x in L:
        yield x

Wir könnten versuchen, die reduce Funktion zu verwenden (R.I.P.):

>>> reduce(lambda d,x: dict(d, x=d.get(x,0)+1), L, {}) # wrong!

Oops, das nicht funktioniert: der Schlüssel Name 'x' ist, nicht x. : (

I endete mit:

>>> reduce(lambda d,x: dict(d.items() + [(x, d.get(x, 0)+1)]), L, {})

(In Python 3, möchten wir schreiben list(d.items()) statt d.items() haben, aber es ist hypothethical, da es keine reduce da ist.)

Bitte schlagen Sie mich mit ein besseren, lesbaren Einzeiler! ;)

War es hilfreich?

Lösung

Python 3.x reduce hat, man muss nur ein from functools import reduce tun. Es hat auch "dict Comprehensions", die genau die Syntax in Ihrem Beispiel haben.

Python 2.7 und 3.x haben auch einen Zähler Klasse, die genau das tut, was Sie wollen:

from collections import Counter
cnt = Counter("abracadabra")

In Python 2.6 oder früher, würde ich persönlich verwenden defaultdict und es in zwei Linien tun:

d = defaultdict(int)
for x in xs: d[x] += 1

Das ist sauber, effizient, Pythonic und viel einfacher für die meisten Menschen als alles beteiligt reduce zu verstehen.

Andere Tipps

Es ist irgendwie cheaty auf die Importmodule für oneliners, so ist hier eine oneliner, die O (n) und Werke mindestens so weit zurück wie python2.4

ist
>>> 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}

Und wenn Sie denken __ Methoden Hacky sind, können Sie immer das tun

>>> 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()

Für Python 2.7, können Sie dieses kleine Liste Verständnis verwenden:

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

Eine, die zurück bis 2,3 arbeitet (etwas kürzer als Timmerman ist, glaube ich, besser lesbar):

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}

Für eine Weile dort etwas itertools Verwendung war per Definition Pythonic. Dennoch ist dies ein bisschen auf der opaken Seite:

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

Ich laufe derzeit Python 2.5.4.

Ihre Einzeiler mit reduce fast ok, nur brauchte es ein wenig zu zwicken:

>>> reduce(lambda d, x: dict(d, **{x: d.get(x, 0) + 1}), L, {})
{'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}

Natürlich wird dies nicht schlagen in-Place-Lösungen (noch in der Geschwindigkeit, noch in pythonicity), aber im Gegenzug haben Sie sich einen schönen rein funktionalen Schnipsel bekam. BTW, das wäre etwas hübschere wenn Python ein Verfahren dict.merge() hat.

ich brauchte eine Histogramm-Implementierung Arbeit in Python 2.2 bis 2.7, und kam mit dieser:

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

Ich war inspiriert von Eli Courtwright des Postens eines defaultdict. Diese wurden in Python 2.5 eingeführt, so können nicht verwendet werden. Aber sie können mit dem dict.setdefault (key, default) emuliert werden.

Dies ist im Grunde die gleiche Sache gnibbler tut, aber ich hatte das erste zu schreiben, bevor ich völlig seine Lambda-Funktion verstehen konnte.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top