2013-04-16 17 views
6

Ho una lista di liste in questo modo:minimo di lista di liste

[[10564, 15], [10564, 13], [10589, 18], [10637, 39], [10662, 38], [10712, 50], [10737, 15], [10762, 14], [10787, 9], [10812, 12], [10837, 45], [3, 17], [7, 21], [46, 26], [48, 12], [49, 24], [64, 14], [66, 
17], [976, 27], [981, 22], [982, 22], [983, 17], [985, 13], [517, 9], [521, 15], [525, 11], [526, 13], [528, 14], [698, 14], [788, 24], [792, 19]] 

Sto cercando di trovare il valore più basso per il secondo elemento in ogni elenco (in modo da confrontare 15 a 13 a 18, ecc non si confrontano 10564 e 15), ma anche per separarlo in intervalli, quindi potrei dire, secondo elemento più basso [1] in ogni lista, solo se l'elemento [0] è superiore a 10000 ecc. Come posso fare questo? L'ho provato e posso solo confrontare elementi della stessa lista fino ad ora, che non è quello che voglio. Nel caso in cui menzioni, restituirei [10787, 9] ma se ci fosse un altro valore oltre il 10000 con 9, vorrei anche restituirlo.

risposta

9

Questo dipende da ciò che si desidera per l'output. In primo luogo, è necessario filtrare l'elenco in base alle "gamme"

gen = (x for x in lists if x[0] > 10000) 

La condizione if può essere così complicato come si vuole (entro sintassi valida). per esempio:

gen = (x for x in lists if 5000 < x[0] < 10000) 

Va perfettamente bene.


Ora, se si desidera solo il secondo elemento dalle sottoliste:

min(x[1] for x in gen) 

Naturalmente, si potrebbe inline il tutto:

min(x[1] for x in lists if x[0] > 10000) 

Se si desidera che l'intero sottolista :

from operator import itemgetter 
min(gen,key=itemgetter(1)) 

esempio:

>>> lists = [[10564, 15], [10564, 13], [10589, 18], [10637, 39], [10662, 38], [10712, 50], [10737, 15], [10762, 14], [10787, 9], [10812, 12], [10837, 45], [3, 17], [7, 21], [46, 26], [48, 12], [49, 24], [64, 14], [66,17], [976, 27], [981, 22], [982, 22], [983, 17], [985, 13], [517, 9], [521, 15], [525, 11], [526, 13], [528, 14], [698, 14], [788, 24], [792, 19]] 
>>> gen = (x for x in lists if x[0] > 10000) 
>>> min(x[1] for x in gen) 
9 
>>> gen = (x for x in lists if x[0] > 10000) 
>>> from operator import itemgetter 
>>> min(gen,key=itemgetter(1)) 
[10787, 9] 

Purtroppo, questi solo darvi la sottolista prima che soddisfa i criteri. Per ottenere tutti loro:

target = min(x[1] for x in lists if x[0] > 10000) 
matches = [x for x in lists if (x[1] == target) and (x[0] > 10000)] 

Se si sa per certo che ci saranno meno di N partite, si potrebbe fare di questo un po 'più efficiente con heapq e itertools.takewhile. Nel caso generale in cui non si conosce un limite superiore al numero di corrispondenze, penso che questa soluzione sia migliore (è O (N) rispetto all'ordinamento che è O (NlogN)).


Si noti che il "Generatore espressione" può essere iterata soltanto sopra una volta prima che sia esaurito

+0

Ottima risposta. Sì, voglio tornare [10787, 9] Avrò una lettura di itemgetter. Non seguo ciò che intendi quando l'espressione del generatore è esaurita. Che non riesco a ripetere l'iterazione per qualche motivo o? – Paul

+0

@Paul - Esattamente. Il generatore può essere ripetuto solo una volta. Di solito questo non è un problema (puoi sempre crearne un altro). Tuttavia, se si tratta di un problema, è possibile utilizzare invece una comprensione di lista: 'lst = [x per x negli elenchi se x [0]> 10000]' – mgilson

+0

Ah, vedo, perfetto. Grazie. Più familiarità con la comprensione delle liste. Non riesco a vedere alcuna differenza oltre alle parentesi, dovrò leggere le differenze con il generatore, grazie. – Paul

1
a=[[10564, 15], [10564, 13], [10589, 18], [10637, 39], [10662, 38], [10712, 50], [10737, 15], [10762, 14], [10787, 9], [10812, 12], [10837, 45], [3, 17], [7, 21], [46, 26], [48, 12], [49, 24], [64, 14], [66, 17], [976, 27], [981, 22], [982, 22], [983, 17], [985, 13], [517, 9], [521, 15], [525, 11], [526, 13], [528, 14], [698, 14], [788, 24], [792, 19]] 

print min(map(lambda y: y[1] ,filter(lambda x: x[0]>10000,a))) 
+0

Grazie, darò una prova anche a capirlo. – Paul

2
>>> l=[[10564, 15], [10564, 13], [10589, 18], [10637, 39]] 
>>> min(x[1] for x in l if x[0] > 10000) 
13 
>>> 

aggiornamento per il tuo commento (è possibile utilizzare lambda per la chiave nella funzione min , itemgetter un po 'più veloce su grandi liste):

>>> min((x for x in l if x[0] > 10000), key=lambda k:k[1]) 
[10564, 13] 
+0

Grazie carino e semplice, voglio tornare [10564, 13] in questo caso. – Paul

+0

Questo sarebbe il mio preferito, tranne che se nessuno dei 'x [0]> 10000' tiene premuto, ottieni' ValoreError: min() arg è una sequenza vuota'. Quindi dovresti racchiuderlo in un 'try: - except' block, a meno che tu non riesca a trovare un modo per intrufolarsi in un valore sentinella per' min' (non penso sia facile) –

+0

@Paul Per rendere questo codice più autodocumentante, considera la decompressione 'x [0]' e 'x [1]' e dai loro nomi descrittivi, come questo, dove sto solo indovinando cosa 'x [0]' e 'x [1]' potrebbe essere: 'min (id per punteggio, id in 1 se punteggio> 10000)'. –

4

Ecco un modo molto semplice approccio che j ust trova il valore minimo e quindi crea l'elenco in base a quel valore.

>>> a = [[10564, 15], [10564, 13], [10589, 18], [10637, 39], [10662, 38], [10712, 50], [10737, 15], [10762, 14], [10787, 9], [10812, 12], [10837, 45], [3, 17], [7, 21], [46, 26], [48, 12], [49, 24], [64, 14], [66, 
... 17], [976, 27], [981, 22], [982, 22], [983, 17], [985, 13], [517, 9], [521, 15], [525, 11], [526, 13], [528, 14], [698, 14], [788, 24], [792, 19]] 
>>> a_min = min(i[1] for i in a) 
>>> [i[0] for i in a if i[1] == a_min and i[0] > 10000] + [a_min] 
[10787, 9] 

Il codice visualizza correttamente i valori multipli:

>>> a += [[10391, 9]] #add another pair with a first value > 10000 
>>> [i[0] for i in a if i[1] == a_min and i[0] > 10000] + [a_min] 
[10787, 10391, 9] 
+0

Molto bello, mi piace quella piccola lista di tutti i possibili valori uguali! – Paul

+0

Penso che preferirei scrivere 'a.append (lst)' di 'a + = [lst]' – mgilson

+0

Inoltre, perché: '[i [0] per i in a if i [1] == a_min e i [ 0]> 10000] + [a_min] 'invece di:' [i for i in a if i [1] == a_min e i [0]> 10000] ' – mgilson

2

Se sono necessari più min s, allora forse si sta meglio di filtrare elementi applicabili e l'ordinamento ...

vals = sorted((el for el in your_list if el[0] >= 10000), key=lambda L: L[1]) 
# [[10787, 9], [10812, 12], [10564, 13], [10762, 14], [10564, 15], [10737, 15], [10589, 18], [10662, 38], [10637, 39], [10837, 45], [10712, 50]] 

Quindi è possibile prendere vals[0] per ottenere il primo, vals[1] per ottenere il secondo o utilizzare il taglio come vals[:5]. ..

+0

Mi fa piacere che ci fosse un metodo' Counter.least_common' ... – mgilson

+0

Qui puoi anche usare 'itertools.takewhile' per tirare prendere' L' dove 'L [1] == vals [0] [1]' – mgilson