2012-03-30 16 views
14

Sto cercando di fare lo stesso come Get the key corresponding to the minimum value within a dictionary, dove vogliamo ottenere la chiave corrispondente al valore minimo in un dizionario.Python: ottieni la chiave con il minor valore da un dizionario MA multipli valori minimi

Il modo migliore sembra essere:

min(d, key=d.get) 

MA voglio applicare questo su un dizionario con più valori minimi:

d = {'a' : 1, 'b' : 2, 'c' : 1} 

Nota che la risposta da quanto sopra sarebbe :

Tuttavia, ho bisogno di entrambi i i due tasti che hanno un valore minimo, ovvero a e c.

Quale sarebbe l'approccio migliore?

(In definitiva, voglio scegliere uno dei due a caso, ma non penso che questo sia rilevante).

+0

Sai che da quando dict non sono allineati, si sta già raccogliendo un "random "uno tra quei due? –

+3

@Rik Poggi, se si definisce "casuale" come "ordinamento non specificato". –

+0

@DarenThomas: le parole non ordinate e casuali tra virgolette sono state utilizzate esattamente per questo. –

risposta

12

Una possibilità semplice è innanzitutto determinare il valore minimo e quindi selezionare tutte le chiavi di mappatura a quello minimo:

min_value = min(d.itervalues()) 
min_keys = [k for k in d if d[k] == min_value] 

Per Python 3 uso d.values() anziché d.itervalues().

Questo ha bisogno di due passaggi attraverso il dizionario, ma dovrebbe essere una delle opzioni più veloci per farlo comunque.

Utilizzando reservoir sampling, è possibile implementare un approccio unico passaggio che seleziona una delle voci a caso:

it = d.iteritems() 
min_key, min_value = next(it) 
num_mins = 1 
for k, v in it: 
    if v < min_value: 
     num_mins = 1 
     min_key, min_value = k, v 
    elif v == min_value: 
     num_mins += 1 
     if random.randrange(num_mins) == 0: 
      min_key = k 

Dopo scrivere questo codice, penso che questa opzione è di interesse piuttosto teorica ... :)

0
def get_rand_min(d): 
    min_val = min(d.values()) 
    min_keys = filter(lambda k: d[k] == min_val, d) 
    return random.choice(min_keys) 
0

È possibile utilizzare heapq.nsmallest per ottenere gli N membri più piccoli del dict, quindi filtrare tutto ciò che non è uguale a quello più basso. Questo è a condizione che tu conosca il numero massimo di membri più piccoli che puoi avere, supponiamo che sia N qui. qualcosa di simile:

from heapq import nsmallest 
from operator import itemgetter 

#get the N smallest members 
smallestN = nsmallest(N, myDict.iteritems(), itemgetter(1))) 

#leave in only the ones with a score equal to the smallest one 
smallest = [x for x in smallestN if x[1] == smallestN[0][1]] 
0

Questo funziona:

d = {'a' :1, 'b' : 2, 'c' : 1} 
min_value = min(d.values()) 
result = [x[0] for x in d.items() if x[1] == k] 

Hmpf. Dopo aver sistemato il codice a lavorare, ho finito con la risposta di @Sven Marnach, quindi, ignorare questo;)

+2

Il tuo risultato è vuoto. – agf

+0

oh, giusto. Ho provato con la seconda versione ('k = min (d.values ​​()') Ho aggiornato la mia risposta per risolvere questo problema –

0
minValue,minKey = min((v,k) for k,v in d.items()) 

causa tuoi semantica è necessario passare attraverso l'intero dizionario almeno una volta. Questo recupererà esattamente 1 elemento minimo.

Se si desidera che tutti gli elementi minimi in O (log (N)) tempo di interrogazione, è possibile inserire i tuoi elementi in una coda di priorità come li generate (se è possibile). La coda di priorità deve avere O (1) tempo di inserimento e O (log (N)) estratto-tempo minimo. (Questo sarà così brutto come l'ordinamento se tutti i tuoi elementi hanno lo stesso valore, ma altrimenti potrebbe funzionare abbastanza bene.)

1

CURA: Ora utilizzando setDefault come suggerito :)

Non so se questo ti aiuta, ma si potrebbe costruire un dizionario inverso con i valori come chiave e le chiavi (in un elenco come valori).

d = {'a' : 1, 'b' : 2, 'c' : 1} 
d2 = {} 
for k, v in d.iteritems(): 
    d2.setdefault(v, []).append(k) 
print d2[min(d2)] 

Sarà stampa questa:

['a', 'c'] 

Tuttavia, credo che le altre soluzioni sono più compatti e, probabilmente, più elegante ...

+1

Si desidera 'dict.setdefault' lì in modo da non dover eseguire il compito separato. – agf

+0

Grazie, non sapevo nemmeno che esistesse :) – katzenversteher

1
min_keys = [k for k in d if all(d[m] >= d[k] for m in d)] 

o, leggermente ottimizzato

min_keys = [k for k, x in d.items() if not any(y < x for y in d.values())] 

Non è così efficiente s altre soluzioni, ma dimostra la bellezza di Python (beh, almeno per me).

+0

Questo funziona davvero in O (n^2) quando c'è una soluzione semplice in O (n) (Sven Marnach's). – EOL

0

Ecco un altro modo per farlo in un solo passaggio:

d = {'foo': 2, 'a' : 1, 'b' : 2, 'c' : 1, 'z': 99, 'x': 1} 
current_min = d[d.keys()[0]] 
min_keys = [] 
for k, v in d.iteritems(): 
    if v < current_min: 
     current_min = v 
     min_keys = [k] 
    elif v == current_min: 
     min_keys.append(k) 
print min_keys 
['a', 'x', 'c'] 
0

Una soluzione passaggio potrebbe essere:

>>> result = [100000, []] 
>>> for key, val in d.items(): 
... if val < result[0]: 
... result[1] = [key]; result[0]=val; 
... elif val == result[0]: 
... result[1].append(key) 
... 
>>> result 
[1, ['a', 'c']] 
+0

'100000' non è robusto. 'float (" inf ")' sarebbe. – EOL

Problemi correlati