2012-10-04 16 views
11

A menudo quiero dividir una colección no ordenada en python. itertools.groubpy hace el tipo correcto de cosas, pero casi siempre requiere un masaje para ordenar primero los elementos y atrapar los iteradores antes de que se consuman.Forma simple de agrupar elementos en cubos

¿Hay alguna forma rápida de obtener este comportamiento, ya sea a través de un módulo de python estándar o un simple modismo de python?

>>> bucket('thequickbrownfoxjumpsoverthelazydog', lambda x: x in 'aeiou') 
{False: ['t', 'h', 'q', 'c', 'k', 'b', 'r', 'w', 'n', 'f', 'x', 'j', 'm', 'p', 
    's', 'v', 'r', 't', 'h', 'l', 'z', 'y', 'd', 'g'], 
True: ['e', 'u', 'i', 'o', 'o', 'u', 'o', 'e', 'e', 'a', 'o']} 
>>> bucket(xrange(21), lambda x: x % 10) 
{0: [0, 10, 20], 
1: [1, 11], 
2: [2, 12], 
3: [3, 13], 
4: [4, 14], 
5: [5, 15], 
6: [6, 16], 
7: [7, 17], 
8: [8, 18], 
9: [9, 19]} 
+1

Debo haber sesgado la discusión hacia abarrotar todo en una línea preguntando sobre un "trazador de líneas". Simplemente cambié eso a "idiomático de pitón", pero por supuesto no me voy a quejar si es realmente corto. –

Respuesta

17

Esto ha llegado varias veces antes - (1), (2), (3) - y no hay una receta partición en el itertools recipes, pero que yo sepa no hay nada en la biblioteca estándar .. aunque me sorprendió un poco Hace semanas por accumulate, ¿quién sabe qué hay al acecho en estos días? : ^)

Cuando necesito este comportamiento, utilizo

from collections import defaultdict 

def partition(seq, key): 
    d = defaultdict(list) 
    for x in seq: 
     d[key(x)].append(x) 
    return d 

y seguir con mi día.

+0

+1 Solución buena, simple y clara – wim

+1

+1 para el uso de defaultdict, siempre me olvido de que existe, y lo envuelve en una bonita función. – grieve

4

Aquí es un simple revestimiento de dos

d = {} 
for x in "thequickbrownfoxjumpsoverthelazydog": d.setdefault(x in 'aeiou', []).append(x) 

Editar:

Simplemente añadiendo el otro caso para la integridad.

d={} 
for x in xrange(21): d.setdefault(x%10, []).append(x) 
+0

Siempre encuentro que 'defaultdict' es mejor que d.setdefault.etc type tricks – wim

+0

@wim: Sí, siempre olvido que existe. Es por eso que voté por la respuesta de DSM. – grieve

+2

wim: en realidad, me gusta 'setdefault' mejor que' defaultdict'. Es más o menos la misma cantidad de código en ambos sentidos, pero 'setdefault' es explícito al respecto, puedes usarlo en los dicts existentes cuando lo necesites. –

-1

Editar:

Usando la respuesta de DSM como punto de partida, aquí es una, respuesta general un poco más concisa:

d = defaultdict(list) 
map(lambda x: d[x in 'aeiou'].append(x),'thequickbrownfoxjumpsoverthelazydog') 

o

d = defaultdict(list) 
map(lambda x: d[x %10].append(x),xrange(21)) 
#

Aquí está una de dos forro :

d = {False:[],True:[]} 
filter(lambda x: d[True].append(x) if x in 'aeiou' else d[False].append(x),"thequickbrownfoxjumpedoverthelazydogs") 

que por supuesto puede ser hecha una sola línea:

d = {False:[],True:[]};filter(lambda x: d[True].append(x) if x in 'aeiou' else d[False].append(x),"thequickbrownfoxjumpedoverthelazydogs") 
+0

-1 Las claves no son siempre Falsas y Verdaderas, deberían ser las salidas del llamativo – wim

+0

Recomendaría el 'for x in 'thequickbrownfoxjumpsoverthelazydog': d [x en 'aeiou']. Append (x)' formulario (como en la respuesta del duelo). No soy realmente un fanático de usar 'map' para los efectos secundarios y tirar el valor. –

+0

El problema con la respuesta de duelo, aunque funciona bien, es que cuesta un poco más computacionalmente. (Llama setdefault para cada elemento, repetido o no). Me gusta la respuesta de DSM, pero quería ver si podía obtener un trazador de líneas (o cerca de él) – korylprince

2

Así es una variante de partition() desde arriba cuando el predicado es boolean, evitando el coste de una dict/defaultdict:

def boolpartition(seq, pred): 
    passing, failing = [], [] 
    for item in seq: 
     (passing if pred(item) else failing).append(item) 
    return passing, failing 

Ejemplo de uso:

>>> even, odd = boolpartition([1, 2, 3, 4, 5], lambda x: x % 2 == 0) 
>>> even 
[2, 4] 
>>> odd 
[1, 3, 5] 
0

Si es un pandas.DataFrame lo siguiente también funciona, utilizi ng pd.cut()

from sklearn import datasets 
import pandas as pd 

# import some data to play with 
iris = datasets.load_iris() 
df_data = pd.DataFrame(iris.data[:,0]) # we'll just take the first feature 

# bucketize 
n_bins = 5 
feature_name = iris.feature_names[0].replace(" ", "_") 
my_labels = [str(feature_name) + "_" + str(num) for num in range(0,n_bins)] 
pd.cut(df_data[0], bins=n_bins, labels=my_labels) 

rendimiento

0  0_1 
1  0_0 
2  0_0 
[...] 

En caso de que no se establece la labels, la salida va a gustar este

0  (5.02, 5.74] 
1  (4.296, 5.02] 
2  (4.296, 5.02] 
[...] 
Cuestiones relacionadas