Vraag Hoe sorteer ik een woordenboek op waarde?


Ik heb een waardenwoordenboek gelezen uit twee velden in een database: een stringveld en een numeriek veld. Het stringveld is uniek, dus dat is de sleutel van het woordenboek.

Ik kan sorteren op de toetsen, maar hoe kan ik sorteren op basis van de waarden?

Opmerking: ik heb Stack Overflow-vraag gelezen Hoe sorteer ik een lijst met woordenboeken op waarden van het woordenboek in Python? en waarschijnlijk kon mijn code worden gewijzigd om een ​​lijst met woordenboeken te hebben, maar omdat ik niet echt een lijst met woordenboeken nodig heb, wilde ik weten of er een eenvoudiger oplossing is.


2932
2018-03-05 00:49


oorsprong


antwoorden:


Het is niet mogelijk om een ​​woordenboek te sorteren, alleen om een ​​representatie van een gesorteerd woordenboek te krijgen. Woordenboeken zijn inherent ordeloos, maar andere typen, zoals lijsten en tuples, zijn dat niet. U hebt dus een geordend gegevenstype nodig om gesorteerde waarden te vertegenwoordigen, wat een lijst zal zijn - waarschijnlijk een lijst met tupels.

Bijvoorbeeld,

import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(1))

sorted_x wordt een lijst met tuples gesorteerd op het tweede element in elk tuple. dict(sorted_x) == x.

En voor diegenen die willen sorteren op sleutels in plaats van waarden:

import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(0))

In Python3 omdat uitpakken niet is toegestaan [1] we kunnen gebruiken

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_by_value = sorted(x.items(), key=lambda kv: kv[1])

3550
2018-03-05 00:59



Zo simpel als: sorted(dict1, key=dict1.get)

Nou, het is eigenlijk mogelijk om een ​​"sort by dictionary values" te doen. Onlangs moest ik dat doen in een Code Golf (Stack Overflow-vraag Code golf: Word-frequentiegrafiek). Verkort, het probleem was van dien aard: geef een tekst, tel hoe vaak elk woord wordt aangetroffen en toon een lijst van de topwoorden, gesorteerd op afnemende frequentie.

Als u een woordenboek construeert met de woorden als sleutels en het aantal keren dat elk woord als waarde voorkomt, wordt dit hier vereenvoudigd als:

from collections import defaultdict
d = defaultdict(int)
for w in text.split():
  d[w] += 1

dan kun je een lijst van de woorden krijgen, gerangschikt op gebruiksfrequentie met sorted(d, key=d.get) - de sortering wordt herhaald boven de woordenboeksleutels, waarbij het aantal woordoccurrences als sorteersleutel wordt gebruikt.

for w in sorted(d, key=d.get, reverse=True):
  print w, d[w]

Ik schrijf deze gedetailleerde uitleg om te illustreren wat mensen vaak bedoelen met "Ik kan gemakkelijk een woordenboek sorteren per sleutel, maar hoe sorteer ik op waarde" - en ik denk dat het OP een probleem probeerde op te lossen. En de oplossing is om een ​​soort lijst met sleutels te maken, gebaseerd op de waarden, zoals hierboven getoond.


967
2017-07-05 08:01



Je zou kunnen gebruiken:

sorted(d.items(), key=lambda x: x[1])

Hierdoor wordt het woordenboek gesorteerd op de waarden van elk item in het woordenboek van het kleinst tot het grootst.


601
2018-02-13 16:33



Dicts kunnen niet worden gesorteerd, maar u kunt er een gesorteerde lijst van maken.

Een gesorteerde lijst met dicteerwaarden:

sorted(d.values())

Een lijst met (sleutel, waarde) paren, gesorteerd op waarde:

from operator import itemgetter
sorted(d.items(), key=itemgetter(1))

165
2018-03-05 01:05



In de recente Python 2.7 hebben we de nieuwe OrderedDict type, dat de volgorde onthoudt waarin de items zijn toegevoegd.

>>> d = {"third": 3, "first": 1, "fourth": 4, "second": 2}

>>> for k, v in d.items():
...     print "%s: %s" % (k, v)
...
second: 2
fourth: 4
third: 3
first: 1

>>> d
{'second': 2, 'fourth': 4, 'third': 3, 'first': 1}

Om een ​​nieuw geordend woordenboek uit het origineel te maken, sorteert u op de volgende waarden:

>>> from collections import OrderedDict
>>> d_sorted_by_value = OrderedDict(sorted(d.items(), key=lambda x: x[1]))

De OrderedDict gedraagt ​​zich als een normale dict:

>>> for k, v in d_sorted_by_value.items():
...     print "%s: %s" % (k, v)
...
first: 1
second: 2
third: 3
fourth: 4

>>> d_sorted_by_value
OrderedDict([('first': 1), ('second': 2), ('third': 3), ('fourth': 4)])

128
2017-07-05 02:50



UPDATE: 5 DECEMBER 2015 met Python 3.5

Hoewel ik het geaccepteerde antwoord nuttig vond, was ik ook verbaasd dat het niet is bijgewerkt om te verwijzen OrderedDict van de standaardbibliotheek verzamelingen module als een haalbaar, modern alternatief - ontworpen om precies dit soort problemen op te lossen.

from operator import itemgetter
from collections import OrderedDict

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = OrderedDict(sorted(x.items(), key=itemgetter(1)))
# OrderedDict([(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)])

De officiële OrderedDict documentatie biedt ook een zeer vergelijkbaar voorbeeld, maar met behulp van een lambda voor de sorteerfunctie:

# regular unsorted dictionary
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

# dictionary sorted by value
OrderedDict(sorted(d.items(), key=lambda t: t[1]))
# OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

75
2017-12-05 09:46



Het kan vaak erg handig zijn om te gebruiken namedtuple. U hebt bijvoorbeeld een woordenboek met 'naam' als sleutels en 'scoort' als waarden en wilt sorteren op 'score':

import collections
Player = collections.namedtuple('Player', 'score name')
d = {'John':5, 'Alex':10, 'Richard': 7}

eerst sorteren met de laagste score:

worst = sorted(Player(v,k) for (k,v) in d.items())

eerst sorteren met de hoogste score:

best = sorted([Player(v,k) for (k,v) in d.items()], reverse=True)

Nu kun je de naam en score krijgen van, laten we zeggen de op een na beste speler (index = 1) heel Pythonisch als volgt:

player = best[1]
player.name
    'Richard'
player.score
    7

64
2017-08-30 00:30



Vrijwel hetzelfde als het antwoord van Hank Gay;


    gesorteerd ([(waarde, sleutel) voor (sleutel, waarde) in mydict.items ()])

Of een beetje geoptimaliseerd zoals voorgesteld door John Fouhy;


    gesorteerd ((waarde, sleutel) voor (sleutel, waarde) in mydict.items ())


57
2018-03-05 01:06



Vanaf Python 3.6 het ingebouwde dictaat wordt besteld

Goed nieuws, dus het oorspronkelijke gebruik van het OP voor het in kaart brengen van paren die zijn opgehaald uit een database met unieke string-id's als sleutels en numerieke waarden als waarden in een ingebouwd Python v3.6 + dictaat, moet nu voldoen aan de invoegvolgorde.

Als u de resulterende twee kolomtabel-expressies opgeeft uit een databasequery, zoals:

SELECT a_key, a_value FROM a_table ORDER BY a_value;

zou worden opgeslagen in twee Python-tuples, k_seq en v_seq (uitgelijnd op basis van numerieke index en met dezelfde lengte natuurlijk), dan:

k_seq = ('foo', 'bar', 'baz')
v_seq = (0, 1, 42)
ordered_map = dict(zip(k_seq, v_seq))

Laat het later uitvoeren als:

for k, v in ordered_map.items():
    print(k, v)

In dit geval levert dit op (voor het nieuwe ingebouwde Python 3.6+ dictaat!):

foo 0
bar 1
baz 42

in dezelfde volgorde per waarde van v.

Waar in de Python 3.5 op mijn machine wordt geïnstalleerd, levert het momenteel:

bar 1
foo 0
baz 42

Details:

Zoals voorgesteld in 2012 door Raymond Hettinger (zie mail over python-dev met onderwerp "Meer compacte woordenboeken met snellere iteratie") en nu (in 2016) aangekondigd in een mail van Victor Stinner aan python-dev met onderwerp "Python 3.6 dict wordt compact en krijgt een privéversie en zoekwoorden worden besteld" vanwege de oplossing / implementatie van probleem 27350 "Compact en besteld dict" in Python 3.6 kunnen we nu een ingebouwd dictaat gebruiken om de invoegvolgorde te behouden !!

Hopelijk leidt dit tot een eerste stap in de implementatie van OrderedDict in een dunne laag. Zoals @ JimFasarakis-Hilliard aangaf, zien sommigen in de toekomst ook use cases voor het type OrderedDict. Ik denk dat de Python-gemeenschap in het algemeen zorgvuldig zal inspecteren, of dit de tand des tijds zal doorstaan, en wat de volgende stappen zullen zijn.

Tijd om onze coderingsgewoonten te heroverwegen om de mogelijkheden te missen die worden geopend door een stabiele volgorde van:

  • Trefwoordargumenten en
  • (tussentijdse) opslag van dictaten

De eerste omdat het de verzending vergemakkelijkt bij de implementatie van functies en methoden in sommige gevallen.

De tweede omdat het aanmoedigt om gemakkelijker te gebruiken dicts als tussentijdse opslag in verwerkingspijplijnen.

Raymond Hettinger heeft vriendelijk documentatie verstrekt waarin "De technologie achter Python 3.6-woordenboeken"- van zijn San Francisco Python Meetup Group-presentatie 2016-DEC-08.

En misschien kunnen heel wat Stack Overflow-hoog gedecoreerde vraag- en antwoordpagina's varianten van deze informatie ontvangen en veel antwoorden van hoge kwaliteit vereisen ook een update per versie.

Waarschuwing Emptor (maar zie ook onderstaande update 2017-12-15):

Zoals @ajcr terecht opmerkt: "Het orderbehoudende aspect van deze nieuwe implementatie wordt beschouwd als een implementatiedetail en er mag niet op vertrouwd worden." (van de whatsnew36) niet nit picking, maar het citaat was een beetje pessimistisch gesneden ;-). Het gaat verder als: "(dit kan in de toekomst veranderen, maar het is wenselijk om deze nieuwe dicteertaalimplementatie in de taal te hebben voor een paar releases voordat de taalspecificatie wordt gewijzigd om opdrachtbehoudende semantiek te mandateren voor alle huidige en toekomstige Python-implementaties; helpt backwards-compatibiliteit te behouden met oudere versies van de taal waarbij willekeurige iteratieorders nog steeds van kracht zijn, bijvoorbeeld Python 3.5). "

Dus zoals in sommige menselijke talen (bijvoorbeeld Duits), vormt het gebruik de taal, en de wil is nu verklaard ... in whatsnew36.

Update 2017-12-15:

In een mail naar de python-dev lijst, Verklaarde Guido van Rossum:

Maak het zo. "Dict keep insertion order" is de uitspraak. Bedankt!

Dus de CPython-side-effect versie 3.6 van de opdracht voor het invoegen van dictaten wordt nu een onderdeel van de taalspecificatie (en niet meer alleen een implementatiedetail). Die e-mailthread heeft ook een aantal onderscheidende ontwerpdoelen opgeleverd collections.OrderedDict zoals herinnerd door Raymond Hettinger tijdens de discussie.


48
2017-09-10 10:05



Gegeven woordenboek

e = {1:39, 4:34, 7:110, 2:87}

sorteer-

sred = sorted(e.items(), key=lambda value: value[1])

Resultaat

[(4, 34), (1, 39), (2, 87), (7, 110)]

Je kunt een lambda-functie gebruiken om dingen op waarde te sorteren en ze in dit geval in een variabele te verwerken SRED met e het originele woordenboek.

Ik hoop dat het helpt!


41
2018-01-25 14:54