una linea imagen histograma graficos graficas graficar ejemplo crear calcular python histogram reduce counting

linea - histograma de una imagen en python



histograma de pitón de una sola línea (9)

Hay muchas formas de escribir un programa Python que calcula un histograma.

Por histograma, me refiero a una función que cuenta la aparición de objetos en un iterable y genera los conteos en un diccionario. Por ejemplo:

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

Una forma de escribir esta función es:

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

¿Hay formas más concisas de escribir esta función?

Si tuviéramos comprensión de diccionario en Python, podríamos escribir:

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

pero como Python 2.6 no los tiene, tenemos que escribir:

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

Aunque este enfoque puede ser legible, no es eficiente: L se recorre varias veces. Además, esto no funcionará para generadores de vida única; La función debería funcionar igualmente bien para generadores de iteradores como:

def gen(L): for x in L: yield x

Podríamos intentar usar la función de reduce (RIP):

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

Vaya, esto no funciona: el nombre de la clave es ''x'' , no x . :(

Terminé con:

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

(En Python 3, tendríamos que escribir list(d.items()) lugar de d.items() , pero es hipotético, ya que no hay reduce allí).

¡Por favor, dame una mejor frase más legible! ;)


Durante un tiempo, todo lo que usaba itertools era por definición Pythonic. Aún así, esto es un poco opaco:

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

Actualmente estoy ejecutando Python 2.5.4.


Es bastante barato importar módulos para oneliners, así que aquí hay un oneliner que es O (n) y funciona al menos tan atrás como Python2.4

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

Y si crees que los __ métodos son piratas, siempre puedes hacer esto.

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

:)


Necesitaba una implementación de histograma para trabajar en python 2.2 hasta 2.7, y se me ocurrió esto:

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

Me inspiré en la publicación de Eli Courtwright de un fallo por defecto. Estos se introdujeron en Python 2.5 por lo que no se pueden utilizar. Pero se pueden emular con el dict.setdefault (clave, predeterminado).

Esto es básicamente lo mismo que hace gnibbler, pero tuve que escribir esto primero antes de que pudiera entender completamente su función lambda.


Para Python 2.7, puede utilizar esta pequeña lista de comprensión:

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


Python 3.x tiene reduce , solo tiene que hacer una función from functools import reduce . También tiene "dictaduras de dictado", que tienen exactamente la sintaxis en su ejemplo.

Python 2.7 y 3.x también tienen una clase Counter que hace exactamente lo que quieres:

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

En Python 2.6 o versiones anteriores, personalmente uso un valor defaultdict y lo hago en 2 líneas:

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

Eso es limpio, eficiente, Pythonic y mucho más fácil de entender para la mayoría de las personas que cualquier otra cosa relacionada con reduce .


Tu uso de reduce una sola línea estaba casi bien, solo necesitabas modificarlo un poco:

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

Por supuesto, esto no superará a las soluciones in situ (ni a la velocidad ni a la pitonicidad), pero a cambio tienes un buen fragmento puramente funcional. Por cierto, esto sería algo más bonito si Python tuviera un método dict.merge() .


Una que funciona de nuevo a 2.3 (un poco más corta que la de Timmerman, creo que es más legible):

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}


import pandas as pd pd.Series(list(L)).value_counts()


$d{$_} += 1 for split //, ''abracadabra'';