2012-10-04 17 views
11

Spesso desidero eseguire il bucket di una raccolta non ordinata in python. itertools.groubpy fa il genere giusto di cose ma quasi sempre richiede il massaggio per ordinare prima gli elementi e catturare gli iteratori prima che vengano consumati.Un modo semplice per raggruppare gli oggetti in bucket

C'è un modo rapido per ottenere questo comportamento, tramite un modulo python standard o un semplice idioma 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

devo aver distorto la discussione verso stipare tutto in una riga chiedendo di un "one-liner". L'ho appena cambiato in "idioma python", ma ovviamente non mi lamento se è veramente breve. –

risposta

17

Questo ha messo a punto diverse volte prima - (1), (2), (3) - e c'è una ricetta partizione nel itertools recipes, ma a mia conoscenza non c'è niente nella libreria standard .. anche se mi ha sorpreso alcuni settimane fa da accumulate, quindi chissà cosa c'è in agguato lì in questi giorni? : ^)

Quando ho bisogno di questo comportamento, io uso

from collections import defaultdict 

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

e andare avanti con la mia giornata.

+0

+1 Soluzione semplice, chiara e chiara – wim

+1

+1 per l'uso di defaultdict, ho sempre dimenticato che esiste e lo avvolge in una bella funzione. – grieve

4

Ecco un semplice rivestimento due

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

Edit:

Basta aggiungere il vostro altro caso per completezza.

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

Trovo sempre che 'defaultdict' sia più bello dei trucchi del tipo d.setdefault.etc – wim

+0

@wim: Sì, ho sempre dimenticato che esiste. Questo è il motivo per cui ho votato la risposta del DSM. – grieve

+2

wim: in realtà, mi piace 'setdefault' meglio di' defaultdict'. È praticamente la stessa quantità di codice in ogni caso, ma "setdefault" è esplicito al riguardo, puoi usarlo su dicts esistenti appena trovi che ne hai bisogno. –

-1

Edit:

Utilizzando la risposta di DSM come un inizio, ecco un po 'più conciso, risposta generale:

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

Qui è un due di linea :

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

Quale può naturalmente essere fatto una battuta:

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

-1 I tasti non sono sempre False e True, dovrebbero essere gli output del callable – wim

+0

Mi raccomando il file 'for x in 'thequickbrownfoxjumpsoverthelazydog: d [x in' aeiou ']. Append (x)' (come nella risposta di Graeve). Non sono davvero un fan dell'uso di 'map' per gli effetti collaterali e per buttare via il valore. –

+0

Il problema con la risposta di grieves, sebbene funzioni bene, è che costa un po 'più computazionalmente. (richiama setdefault per ogni elemento, ripetuto o meno.) Mi piace la risposta del DSM, ma volevo vedere se potevo avere un solo liner (o vicino ad esso). – korylprince

2

Ecco una variante di partition() dall'alto quando il predicato è booleano, evitando il costo di una dict/defaultdict:

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

Esempio di utilizzo:

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

Se è un pandas.DataFrame quanto segue funziona anche, 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) 

cedendo

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

Nel caso in cui non si imposta la labels, l'uscita sta andando come questo

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