2015-10-13 54 views
8

Voglio trovare la chiave corrispondente al valore minimo o massimo di un dizionario in julia. In Python lo farei al seguente:julia - Come trovare la chiave per il valore minimo/massimo di un Dict?

my_dict = {1:20, 2:10} 
min(my_dict, my_dict.get) 

Che sarebbe restituire la chiave 2.

Come posso fare lo stesso in julia?

my_dict = Dict(1=>20, 2=>10) 
minimum(my_dict) 

Quest'ultima restituisce 1 => 20 invece di 2 => 10 o 2.

+0

'indmin()' o 'findmin()' dovrebbe funzionare. Non so perché 'minimum()' fa la cosa sbagliata ... – daycaster

+0

'findmin' non funziona in realtà; dà anche uno strano risultato in questo caso: '(1 => 20,16)' –

+0

@ DavidP.Sanders Strange, ottengo (10,2). – daycaster

risposta

5

un'altra opzione è:

collect(keys(d))[indmin(collect(values(d)))] 

dipende proprietà di chiavi e valori iteratori non garantite, ma nel lavoro fatto per Dicts (e sono garantito per OrderedDicts). come la risposta reduce, d deve essere non vuoto.

perché menzionare questo, quando il reduce, praticamente unghie esso? è da 3 a 4 volte più veloce (almeno sul mio computer)!

+1

Penso che sia necessario garantire che 'keys' e' values' iterator restituiscano gli elementi nello stesso ordine. L'ordine in sé non è definito, ma sarebbe piuttosto folle se facessero qualcosa di diverso l'uno dall'altro. –

+1

@ MattB Non penso che dovresti fare affidamento su questo. Finché non è documentato, questo è un dettaglio di implementazione. Ma sono d'accordo che questo * dovrebbe * essere garantito. – user4235730

+1

Documentiamolo, allora. Cura di inviare una richiesta di pull? –

3

Se avete solo bisogno il valore minimo, è possibile utilizzare

minimum(values(my_dict)) 

Se avete bisogno della chiave così, non so una funzione built-in di farlo, ma si può facilmente scrivere voi stessi per le chiavi e valori numerici:

function find_min_key{K,V}(d::Dict{K,V}) 

    minkey = typemax(K) 
    minval = typemax(V) 

    for key in keys(d) 
     if d[key] < minval 
      minkey = key 
      minval = d[key] 
     end 
    end 

    minkey => minval 
end 

my_dict = Dict(1=>20, 2=>10) 

find_min_key(my_dict) 
+1

(Ho rimosso il mio altro commento poiché ora è obsoleto.) Ma questo non ha tipo problemi di stabilità, ad esempio se 'd :: Dict {Int, Int}'? Non è quindi il confronto tra 'Int' e 'Int' o' Float'? Mi sto perdendo qualcosa? – user4235730

+0

Ah, capisco cosa intendi. Pensavo volevi dire che l'output della funzione non era di tipo stabile, ma vuoi dire che 'minkey' e' minval' cambiano tipo * durante * la funzione. Darò un altro tentativo ... –

+0

Grazie. Risolto l'uso di 'typemax'. Ciò funzionerà per tutti i tipi numerici, ma non per es. per ogni tipo di 'String's. È sorprendentemente difficile renderlo generale! La soluzione 'reduce' funziona per quel caso, però. –

8

si potrebbe noi e reduce come questo, che restituirà la chiave del primo valore più piccolo d:

reduce((x, y) -> d[x] ≤ d[y] ? x : y, keys(d)) 

Questo funziona solo per non vuote Dict s, però. (Ma la nozione di “chiave del valore minimo di nessun valore” non ha molto senso, in modo che di solito caso dovrebbe essere gestita separatamente in ogni caso.)


Modifica per quanto riguarda l'efficienza.

considerare queste definizioni (nessuno dei quali gestisce collezioni vuote) ...

m1(d) = reduce((x, y) -> d[x] ≤ d[y] ? x : y, keys(d)) 

m2(d) = collect(keys(d))[indmin(collect(values(d)))] 

function m3(d) 
    minindex(x, y) = d[x] ≤ d[y] ? x : y 
    reduce(minindex, keys(d)) 
end 

function m4(d) 
    minkey, minvalue = next(d, start(d))[1] 
    for (key, value) in d 
    if value < minvalue 
     minkey = key 
     minvalue = value 
    end 
    end 
    minkey 
end 

... con questo codice:

function benchmark(n) 
    d = Dict{Int, Int}(1 => 1) 
    m1(d); m2(d); m3(d); m4(d); m5(d) 

    while length(d) < n 
    setindex!(d, rand(-n:n), rand(-n:n)) 
    end 

    @time m1(d) 
    @time m2(d) 
    @time m3(d) 
    @time m4(d) 
end 

Calling benchmark(10000000) stamperà qualcosa di simile:

1.455388 seconds (30.00 M allocations: 457.748 MB, 4.30% gc time) 
0.380472 seconds (6 allocations: 152.588 MB, 0.21% gc time) 
0.982006 seconds (10.00 M allocations: 152.581 MB, 0.49% gc time) 
0.204604 seconds 

Da questo possiamo vedere che m2 (da user3580870's answer) è effettivamente più veloce della mia soluzione originale m1 da un fattore di circa 3 a 4 e utilizza anche meno memoria. Ciò è apparentemente dovuto alla funzione di overhead delle chiamate, ma anche al fatto che l'espressione λ in m1 non è ottimizzata molto bene. Siamo in grado di alleviare il secondo problema definendo una funzione di supporto come in m3, che è meglio di m1, ma non buono come m2.

Tuttavia, m2 assegna ancora O (n) memoria, che può essere evitato: se si ha realmente bisogno l'efficienza, è necessario utilizzare un ciclo esplicito come in m4, che assegna quasi nessun memoria ed è anche più veloce.

+0

una versione senza unicode e funzionante per vuoto 'Dicts': ' reduce ((x, y) -> (isa (x, Void) || (d [x]> d [y]))? Y: x , niente, chiavi (d)) ' –

+1

Confronto molto bello. Mi piace usare 'next' per ottenere il primo valore nel dizionario, anche se la sintassi non è elegante! –

+0

'd.keys [indmin (values ​​(d))]' è molto leggermente più lento del ciclo esplicito, ma 3 volte più veloce delle altre opzioni brevi. (Tuttavia, 'd.keys' è una struttura dati interna.) –

Problemi correlati