2016-04-24 11 views
5

Ho due liste di uguale lunghezza. Il primo elenco l1 contiene dati.Mapping due liste senza looping

l1 = [2, 3, 5, 7, 8, 10, ... , 23] 

Il secondo elenco l2 contiene la categoria dei dati in l1 appartiene a:

l2 = [1, 1, 2, 1, 3, 4, ... , 3] 

Come posso dividere la prima lista in base alle posizioni definite da numeri come 1, 2, 3, 4 nel secondo elenco, utilizzando una comprensione elenco o funzione lambda. Ad esempio, 2, 3, 7 dal primo elenco appartiene alla stessa partizione in quanto hanno valori corrispondenti nel secondo elenco.

Il numero di partizioni è noto all'inizio.

+1

Che cosa si intende per partizione? Qual è l'infrastruttura di output prevista? Una lista? Un dittico? – timgeb

+0

@timgeb tutto funziona. L'obiettivo è trovare il centroide su [questo algoritmo] (http://stanford.edu/~cpiech/cs221/handouts/kmeans.html) –

risposta

9

È possibile utilizzare un dizionario:

>>> l1 = [2, 3, 5, 7, 8, 10, 23] 
>>> l2 = [1, 1, 2, 1, 3, 4, 3] 

>>> d = {} 
>>> for i, j in zip(l1, l2): 
...  d.setdefault(j, []).append(i) 
... 
>>> 
>>> d 
{1: [2, 3, 7], 2: [5], 3: [8, 23], 4: [10]} 
+0

Che dire del * "(...) senza eseguire il ciclo" * parte? – jDo

8

Se un dict va bene, io suggerisco di usare un defaultdict:

>>> from collections import defaultdict 
>>> d = defaultdict(list) 
>>> for number, category in zip(l1, l2): 
...  d[category].append(number) 
... 
>>> d 
defaultdict(<type 'list'>, {1: [2, 3, 7], 2: [5], 3: [8, 23], 4: [10]}) 

considerare l'utilizzo di itertools.izip per l'efficienza della memoria se si utilizza Python 2.

Questa è fondamentalmente la stessa soluzione di Kasramvd, ma penso che lo defaultdict lo renda un po 'più semplice er da leggere.

+0

può essere ottimizzato per le prestazioni? Il mio set di dati è davvero molto grande. –

+1

@SantoshLinkha sì, usa 'itertools.izip' per le grandi liste. – timgeb

2

Questo darà un elenco delle partizioni che utilizzano lista di comprensione:

>>> l1 = [2, 3, 5, 7, 8, 10, 23] 
>>> l2 = [1, 1, 2, 1, 3, 4, 3] 
>>> [[value for i, value in enumerate(l1) if j == l2[i]] for j in set(l2)] 
[[2, 3, 7], [5], [8, 23], [10]] 
1

Una lista annidata di comprensione:

[ [ l1[j] for j in range(len(l1)) if l2[j] == i ] for i in range(1, max(l2)+1)]

1

Se è ragionevole avere i dati memorizzati in numpy s' ndarrays è possibile utilizzare l'indicizzazione estesa

{i:l1[l2==i] for i in set(l2)} 

per costruire un dizionario di ndarrays indicizzato per codice di categoria.

C'è un overhead associato con l2==i (ad esempio, la costruzione di un nuovo array booleano per ogni categoria) che cresce con il numero di categorie, in modo che si consiglia di controllare che alternative, sia numpy o defaultdict, è più veloce con la vostra dati.

ho provato con n=200000, nc=20 e numpy era più veloce di defaultdict + izip (124 vs 165 ms), ma con nc=10000numpy era (molto) più lenti (11300 vs 251 ms)

1

Utilizzando alcuni itertools e operator chicche e una sorta si può fare questo in un uno di linea:

>>> l1 = [2, 3, 5, 7, 8, 10, 23] 
>>> l2 = [1, 1, 2, 1, 3, 4, 3] 
>>> itertools.groupby(sorted(zip(l2, l1)), operator.itemgetter(0)) 

Il risultato di questo è un oggetto itertools.groupby che può essere iterato:

>>> for g, li in itertools.groupby(sorted(zip(l2, l1)), operator.itemgetter(0)): 
>>>  print(g, list(map(operator.itemgetter(1), li))) 

1 [2, 3, 7] 
2 [5] 
3 [8, 23] 
4 [10] 
+0

Con 'n = 200000' e' nc = 20' ho misurato un tempismo migliore di 1960 ms. Poiché la maggior parte del tempo è trascorsa in 'ordinati' (solo un'ipotesi ragionevole) succede che, per' nc' più grandi si hanno scadenze leggermente migliori, ad esempio 'nc = 200' -> 1880 ms,' nc = 2000' - > 1820 ms e 'nc = 10000' -> 1680 ms. Mi chiedo se l'algoritmo di ordinamento sottostante si comporta meglio quando ci sono più differenze nei dati da ordinare ... – gboffi

1

Questa non è una comprensione di una lista ma una comprensione di dizionario. Assomiglia @ soluzione cromod ma conserva le "categorie" di l2:

{k:[val for i, val in enumerate(l1) if k == l2[i]] for k in set(l2)} 

uscita:

>>> l1 
[2, 3, 5, 7, 8, 10, 23] 
>>> l2 
[1, 1, 2, 1, 3, 4, 3] 
>>> {k:[val for i, val in enumerate(l1) if k == l2[i]] for k in set(l2)} 
{1: [2, 3, 7], 2: [5], 3: [8, 23], 4: [10]} 
>>>