Una forma sencilla de agrupar artículos en cubos

A menudo quiero agregar una colección desordenada en python. itertools.groubpy hace el tipo correcto de cosas, pero casi siempre requiere un masaje para ordenar primero los elementos y capturar los iteradores antes de que se consumn.

¿Hay alguna forma rápida de obtener este comportamiento, ya sea a través de un módulo estándar de python o un simple lenguaje 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]} 

Esto ha surgido varias veces antes (1) , (2) , (3) y hay una receta de partición en las recetas de itertools , 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, uso

 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 dia

Aquí hay un simple forro de dos

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

Editar:

Simplemente agregando su otro caso para la integridad.

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

Aquí hay una variante de partition() desde arriba cuando el predicado es booleano, evitando el costo de un 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] 

Si es un pandas.DataFrame lo siguiente también funciona, utilizando 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) 

flexible

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

En caso de que no establezca las labels , a la salida le va a gustar esto.

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

Editar:

Usando la respuesta de DSM como comienzo, aquí hay 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í hay un trazador de líneas dos:

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

Lo que, por supuesto, se puede hacer de una sola línea:

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