Pergunta

Eu tenho um dicionário de valores lidos a partir de dois campos em um banco de dados: um campo de cordas e um campo numérico. O campo de cadeia é único, de modo que é a chave do dicionário.

Eu posso classificar nas teclas, mas como posso classificar com base nos valores?

Nota: Eu li pergunta Stack Overflow aqui Como posso ordenar uma lista de dicionários por um valor do dicionário e provavelmente poderia alterar o código para ter uma lista de dicionários, mas desde que eu realmente não precisa de uma lista de dicionários eu queria saber se existe uma solução simples de tipo seja em ordem crescente ou decrescente.

Outras dicas

Tão simples como: sorted(dict1, key=dict1.get)

Bem, é realmente possível fazer uma "espécie de dicionário valores". Recentemente eu tive que fazer isso em uma questão Código Golf (Stack Overflow golf cupom: gráfico de freqüência de palavra ). Abreviada, o problema era do tipo: dado um texto, contar quantas vezes cada palavra é encontrado e exibir uma lista das principais palavras, classificado por ordem decrescente de frequência.

Se você construir um dicionário com as palavras como chaves e o número de ocorrências de cada palavra como valor, simplificado aqui como:

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

então você pode obter uma lista das palavras, ordenados por frequência de uso com sorted(d, key=d.get) - as iterações de classificação sobre as teclas do dicionário, usando o número de ocorrências da palavra como chave de ordenação.

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

Estou escrevendo esta explicação detalhada para ilustrar o que muitas vezes as pessoas querem dizer com "I pode facilmente classificar um dicionário por chave, mas como faço para classificar por valor" - e acho que o OP estava tentando abordar essa questão. E a solução é fazer uma espécie de lista de teclas, com base nos valores, como mostrado acima.

Você pode usar:

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

Isto irá classificar o dicionário pelos valores de cada entrada no dicionário do menor ao maior.

Para classificá-lo em ordem decrescente basta adicionar reverse=True:

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

dicts não pode ser resolvido, mas você pode construir uma lista ordenada dos mesmos.

A lista de valores dict ordenada:

sorted(d.values())

A lista de (chave, valor) pares, classificado por valor:

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

Em recente Python 2.7, temos o novo OrderedDict tipo, que lembra a ordem em que foram adicionados os itens.

>>> 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}

Para fazer um novo dicionário ordenado a partir do original, a triagem pelos valores:

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

Os comporta OrderedDict como um dicionário normal:

>>> 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)])

UPDATE: 05 de dezembro de 2015 usando Python 3.5

Enquanto eu encontrei a resposta aceita útil, eu também estava surpreso que não foi atualizado para referência OrderedDict a partir da biblioteca padrão coleções módulo como uma alternativa viável, moderno -. projetado para resolver exatamente esse tipo de problema

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)])

O funcionário OrderedDict documentação dá um exemplo muito semelhante também, mas usando um lambda para a função de classificação:

# 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)])

Praticamente o mesmo como do Hank Gay resposta :

sorted([(value,key) for (key,value) in mydict.items()])

Ou otimizado um pouco como sugerido por John Fouhy:

sorted((value,key) for (key,value) in mydict.items())

Muitas vezes pode ser muito útil para usar namedtuple . Por exemplo, você tem um dicionário de 'nome' como chaves e 'score' como valores e você deseja classificar 'score':

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

classificação com menor pontuação primeiro:

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

classificação com maior pontuação em primeiro lugar:

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

Agora você pode obter o nome e pontuação de, digamos que o segundo melhor jogador (index = 1) muito Pythonically assim:

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

Como de Python 3.6 o built-in dict será ordenada

Boas notícias, então caso de uso original do OP de pares de mapeamento recuperado de um banco de dados com ids string única como chaves e valores numéricos como valores em um + dict built-in Python v3.6, agora deve respeitar a ordem de inserção.

Se dizem que os dois expressões de tabela coluna resultantes de uma consulta de banco de dados como:

SELECT a_key, a_value FROM a_table ORDER BY a_value;

seria armazenado em dois tuplos Python, k_seq e v_seq (alinhado pelo índice numérico e com o mesmo comprimento de curso), então:

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

Permitir a saída mais tarde, como:

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

produzindo neste caso (para o novo Python 3.6+ built-in dict!):

foo 0
bar 1
baz 42

na mesma ordenação por valor de v.

Onde no Python 3.5 instalado em minha máquina que atualmente produz:

bar 1
foo 0
baz 42

Detalhes:

Tal como proposto em 2012 por Raymond Hettinger (correio cf. em python-dev com o assunto "dicionários mais compactos com mais rápido iteração" ) e agora (em 2016) anunciadas em um e-mail por Victor Stinner para python-dev com o assunto "Python 3.6 dict torna-se compacto e recebe uma versão privada e palavras-chave tornar-se ordenado" devido à correção / implementação de emitir 27350 "Compact e ordenou dict" em Python 3.6 que agora será capaz, para usar um built-in dict para manter a ordem de inserção !!

Esperamos que isto conduzirá a uma aplicação de camada fina OrderedDict como um primeiro passo. Como @ JimFasarakis-Hilliard indicado, alguns casos de uso see para o tipo OrderedDict também no futuro. Eu acho que a comunidade Python em geral vai cuidadosamente inspecionar, se isso vai resistir ao teste do tempo, e que os próximos passos será.

Hora de repensar nossos hábitos de codificação para não perder as possibilidades abertas pela ordenação estável de:

  • argumentos chave e
  • (intermédia) de armazenamento Dict

O primeiro porque facilita expedição na implementação de funções e métodos em alguns casos.

A segunda vez que incentiva a utilizar mais facilmente dicts como armazenamento intermédio no processamento de condutas.

Raymond Hettinger gentilmente cedido documentação explicando " The tech Atrás Python 3.6 Dicionários " - de sua apresentação San Francisco Python Grupo Meetup 2016-DEC-08

.

E talvez algum alta decorados páginas de perguntas e respostas Stack Overflow receberá variantes desta informação e muitas respostas de alta qualidade vai exigir uma atualização per versão também.

advertência Emptor (mas também ver abaixo atualização 2017/12/15):

Como @ajcr legitimamente notas: "O aspecto de preservação da ordem desta nova implementação é considerado um detalhe de implementação e não deve ser invocado." picking (a partir do whatsnew36 ) não nit, e a citação foi cortado um pouco ;-) pessimista. Ele continua como "(isso pode mudar no futuro, mas se deseja ter essa nova implementação dict na língua por alguns lançamentos antes de alterar a especificação de linguagem para impor semântica de preservação da ordem para todas as implementações atuais e futuras do Python, o que também ajuda a preservar a compatibilidade com versões anteriores com versões mais antigas do idioma em que ordem de iteração aleatória ainda está em vigor, por exemplo, Python 3.5) ".

Assim como em algumas línguas humanas (por exemplo alemão), o uso molda a linguagem, e agora vai ter sido declarada ... em whatsnew36 .

Atualização 2017/12/15:

Em um mail para a lista de python-dev , Guido van Rossum declarou:

Faça-o assim. "Dict mantém a ordem de inserção" é a decisão. Obrigado!

Assim, o CPython efeito colateral de dict ordenação inserção versão 3.6 agora está se tornando parte da especificação linguagem (e não mais unicamente um detalhe de implementação). Esse segmento mail também surgiram algumas metas de design distintivo para collections.OrderedDict como lembrado por Raymond Hettinger durante a discussão.

Eu tive o mesmo problema, e eu resolvido assim:

WantedOutput = sorted(MyDict, key=lambda x : MyDict[x]) 

(pessoas que respondem "Não é possível classificar um dicionário" não leu a pergunta! Na verdade, "Eu posso classificar as chaves, mas como posso classificar com base nos valores?" Significa claramente que ele quer uma lista das chaves classificadas de acordo com o valor de seus valores.)

Por favor note que a ordem não está bem definido (teclas com o mesmo valor será em uma ordem arbitrária na lista de saída).

Em Python 2.7, basta fazer:

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

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

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

copiar e colar a partir de: http://docs.python.org/ dev / biblioteca / collections.html # ordereddict-exemplos e-receitas

Aproveite; -)

Este é o código:

import operator
origin_list = [
    {"name": "foo", "rank": 0, "rofl": 20000},
    {"name": "Silly", "rank": 15, "rofl": 1000},
    {"name": "Baa", "rank": 300, "rofl": 20},
    {"name": "Zoo", "rank": 10, "rofl": 200},
    {"name": "Penguin", "rank": -1, "rofl": 10000}
]
print ">> Original >>"
for foo in origin_list:
    print foo

print "\n>> Rofl sort >>"
for foo in sorted(origin_list, key=operator.itemgetter("rofl")):
    print foo

print "\n>> Rank sort >>"
for foo in sorted(origin_list, key=operator.itemgetter("rank")):
    print foo

Aqui estão os resultados:

Original

{'name': 'foo', 'rank': 0, 'rofl': 20000}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Baa', 'rank': 300, 'rofl': 20}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Penguin', 'rank': -1, 'rofl': 10000}

Rofl

{'name': 'Baa', 'rank': 300, 'rofl': 20}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Penguin', 'rank': -1, 'rofl': 10000}
{'name': 'foo', 'rank': 0, 'rofl': 20000}

Ranking

{'name': 'Penguin', 'rank': -1, 'rofl': 10000}
{'name': 'foo', 'rank': 0, 'rofl': 20000}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Baa', 'rank': 300, 'rofl': 20}

Se os valores são numéricos você também pode usar Counter de < a href = "https://docs.python.org/library/collections.html" rel = "nofollow noreferrer"> coleções .

from collections import Counter

x = {'hello': 1, 'python': 5, 'world': 3}
c = Counter(x)
print(c.most_common())

>> [('python', 5), ('world', 3), ('hello', 1)]    

Tente a seguinte abordagem. Vamos definir um dicionário chamado mydict com os seguintes dados:

mydict = {'carl':40,
          'alan':2,
          'bob':1,
          'danny':3}

Se alguém quisesse classificar o dicionário por chaves, pode-se fazer algo como:

for key in sorted(mydict.iterkeys()):
    print "%s: %s" % (key, mydict[key])

Isso deve retornar o seguinte resultado:

alan: 2
bob: 1
carl: 40
danny: 3

Por outro lado, se alguém quisesse classificar um dicionário por valor (como é solicitado na pergunta), pode-se fazer o seguinte:

for key, value in sorted(mydict.iteritems(), key=lambda (k,v): (v,k)):
    print "%s: %s" % (key, value)

O resultado deste comando (classificando o dicionário por valor) deve retornar o seguinte:

bob: 1
alan: 2
danny: 3
carl: 40

Você pode criar um "índice invertido", também

from collections import defaultdict
inverse= defaultdict( list )
for k, v in originalDict.items():
    inverse[v].append( k )

Agora, o seu inverso tem os valores; cada valor tem uma lista de chaves aplicáveis.

for k in sorted(inverse):
    print k, inverse[k]

Você pode usar o collections.Counter . Note, isso vai funcionar para valores tanto numéricos e não numéricos.

>>> x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
>>> from collections import Counter
>>> #To sort in reverse order
>>> Counter(x).most_common()
[(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)]
>>> #To sort in ascending order
>>> Counter(x).most_common()[::-1]
[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]
>>> #To get a dictionary sorted by values
>>> from collections import OrderedDict
>>> OrderedDict(Counter(x).most_common()[::-1])
OrderedDict([(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)])

A partir de Python 3.6, objetos dict agora estão ordenados por ordem de inserção. É oficialmente nas especificações do Python 3.7.

>>> words = {"python": 2, "blah": 4, "alice": 3}
>>> dict(sorted(words.items(), key=lambda x: x[1]))
{'python': 2, 'alice': 3, 'blah': 4}

Antes disso, você tinha que usar OrderedDict.

Python 3.7 documentação diz:

alterado na versão 3.7: fim dicionário é garantido para ser de inserção ordem. Este comportamento era detalhe de implementação de CPython de 3,6.

Você pode usar um salto dict que é um dicionário que está permanentemente classificadas por valor.

>>> data = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
>>> SkipDict(data)
{0: 0.0, 2: 1.0, 1: 2.0, 4: 3.0, 3: 4.0}

Se você usar keys(), values() ou items() então você iterar na ordem de classificação por valor.

Ele é implementado utilizando o salto datastructure lista .

from django.utils.datastructures import SortedDict

def sortedDictByKey(self,data):
    """Sorted dictionary order by key"""
    sortedDict = SortedDict()
    if data:
        if isinstance(data, dict):
            sortedKey = sorted(data.keys())
            for k in sortedKey:
                sortedDict[k] = data[k]
    return sortedDict

Você também pode usar função personalizada que pode ser passado para chave.

def dict_val(x):
    return x[1]
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=dict_val)

Como apontado por Dilettant , Python 3.6 agora manter a ordem ! Eu pensei que eu iria partilhar uma função que eu escrevi que facilita a classificação de um iterável (tuple, lista, dict). Neste último caso, você pode classificar quer em chaves ou valores, e que pode levar comparação numérica em conta. Apenas para> = 3.6!

Quando você tentar usar classificadas em uma iterable que detém por exemplo cordas, bem como ints, classificado () irá falhar. Claro que você pode forçar comparação de string com str (). No entanto, em alguns casos, você quer fazer real comparação numérica onde 12 é menor do que 20 (que não é o caso, em comparação string). Então eu vim com o seguinte. Quando você quiser comparação numérica explícita você pode usar o num_as_num bandeira que vai tentar fazer triagem numérico explícito, tentando converter todos os valores para carros alegóricos. Se isso for bem sucedido, ele vai fazer a classificação numérica, caso contrário ele vai recorrer a comparação de cadeia.

Comentários para melhoria ou solicitações de envio bem-vindo.

def sort_iterable(iterable, sort_on=None, reverse=False, num_as_num=False):
    def _sort(i):
      # sort by 0 = keys, 1 values, None for lists and tuples
      try:
        if num_as_num:
          if i is None:
            _sorted = sorted(iterable, key=lambda v: float(v), reverse=reverse)
          else:
            _sorted = dict(sorted(iterable.items(), key=lambda v: float(v[i]), reverse=reverse))
        else:
          raise TypeError
      except (TypeError, ValueError):
        if i is None:
          _sorted = sorted(iterable, key=lambda v: str(v), reverse=reverse)
        else:
          _sorted = dict(sorted(iterable.items(), key=lambda v: str(v[i]), reverse=reverse))

      return _sorted

    if isinstance(iterable, list):
      sorted_list = _sort(None)
      return sorted_list
    elif isinstance(iterable, tuple):
      sorted_list = tuple(_sort(None))
      return sorted_list
    elif isinstance(iterable, dict):
      if sort_on == 'keys':
        sorted_dict = _sort(0)
        return sorted_dict
      elif sort_on == 'values':
        sorted_dict = _sort(1)
        return sorted_dict
      elif sort_on is not None:
        raise ValueError(f"Unexpected value {sort_on} for sort_on. When sorting a dict, use key or values")
    else:
      raise TypeError(f"Unexpected type {type(iterable)} for iterable. Expected a list, tuple, or dict")

Aqui está uma solução usando zip na d.values() e d.keys() . Algumas linhas abaixo deste link (no dicionário ver objetos) é:

Isso permite a criação de pares (valor, chave) usando zip ():. Pares = zip (d.values ??(), d.keys ())

Assim, podemos fazer o seguinte:

d = {'key1': 874.7, 'key2': 5, 'key3': 8.1}

d_sorted = sorted(zip(d.values(), d.keys()))

print d_sorted 
# prints: [(5, 'key2'), (8.1, 'key3'), (874.7, 'key1')]

Use ValueSortedDict de dicts :

from dicts.sorteddict import ValueSortedDict
d = {1: 2, 3: 4, 4:3, 2:1, 0:0}
sorted_dict = ValueSortedDict(d)
print sorted_dict.items() 

[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]

Iterate através de um dicionário e classificá-lo por seus valores em ordem decrescente:

$ python --version
Python 3.2.2

$ cat sort_dict_by_val_desc.py 
dictionary = dict(siis = 1, sana = 2, joka = 3, tuli = 4, aina = 5)
for word in sorted(dictionary, key=dictionary.get, reverse=True):
  print(word, dictionary[word])

$ python sort_dict_by_val_desc.py 
aina 5
tuli 4
joka 3
sana 2
siis 1

Se os seus valores são inteiros, e você usar Python 2.7 ou mais recente, você pode usar collections.Counter vez de dict. O método most_common lhe dará todos os itens, ordenados pelo valor.

Claro, lembre-se, você precisa usar OrderedDict porque dicionários regulares Python não manter a ordem original.

from collections import OrderedDict
a = OrderedDict(sorted(originalDict.items(), key=lambda x: x[1]))

Se você não tem Python 2.7 ou superior, o melhor que você pode fazer é repetir os valores em uma função de gerador. (Há uma OrderedDict para 2,4 e 2,6 aqui , mas

a) Não sei sobre o quão bem ele funciona

e

b) Você tem que baixar e instalá-lo, é claro. Se você não tem acesso administrativo, então eu tenho medo a opção está fora.)


def gen(originalDict):
    for x, y in sorted(zip(originalDict.keys(), originalDict.values()), key=lambda z: z[1]):
        yield (x, y)
    #Yields as a tuple with (key, value). You can iterate with conditional clauses to get what you want. 

for bleh, meh in gen(myDict):
    if bleh == "foo":
        print(myDict[bleh])

Você também pode imprimir todos os valores

for bleh, meh in gen(myDict):
    print(bleh, meh)

Por favor, lembre-se de remover os parênteses após impressão se não estiver usando Python 3.0 ou superior

Isso funciona em 3.1.x:

import operator
slovar_sorted=sorted(slovar.items(), key=operator.itemgetter(1), reverse=True)
print(slovar_sorted)

Por uma questão de exaustividade, estou postando uma solução usando heapq . Note, este método irá funcionar para ambos os valores numéricos e não numéricos

>>> x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
>>> x_items = x.items()
>>> heapq.heapify(x_items)
>>> #To sort in reverse order
>>> heapq.nlargest(len(x_items),x_items, operator.itemgetter(1))
[(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)]
>>> #To sort in ascending order
>>> heapq.nsmallest(len(x_items),x_items, operator.itemgetter(1))
[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]

Apenas aprendeu habilidade relevante de Python para Todos .

Você pode usar uma lista temporária para ajudá-lo a classificar o dicionário:

#Assume dictionary to be:
d = {'apple': 500.1, 'banana': 1500.2, 'orange': 1.0, 'pineapple': 789.0}

# create a temporary list
tmp = []

# iterate through the dictionary and append each tuple into the temporary list 
for key, value in d.items():
    tmptuple = (value, key)
    tmp.append(tmptuple)

# sort the list in ascending order
tmp = sorted(tmp)

print (tmp)

Se você quiser classificar a lista em ordem decrescente, basta alterar a linha de classificação original a:

tmp = sorted(tmp, reverse=True)

Usando compreensão da lista, a um forro seria:

#Assuming the dictionary looks like
d = {'apple': 500.1, 'banana': 1500.2, 'orange': 1.0, 'pineapple': 789.0}
#One liner for sorting in ascending order
print (sorted([(v, k) for k, v in d.items()]))
#One liner for sorting in descending order
print (sorted([(v, k) for k, v in d.items()], reverse=True))

Exemplo de saída:

#Asending order
[(1.0, 'orange'), (500.1, 'apple'), (789.0, 'pineapple'), (1500.2, 'banana')]
#Descending order
[(1500.2, 'banana'), (789.0, 'pineapple'), (500.1, 'apple'), (1.0, 'orange')]
months = {"January": 31, "February": 28, "March": 31, "April": 30, "May": 31,
          "June": 30, "July": 31, "August": 31, "September": 30, "October": 31,
          "November": 30, "December": 31}

def mykey(t):
    """ Customize your sorting logic using this function.  The parameter to
    this function is a tuple.  Comment/uncomment the return statements to test
    different logics.
    """
    return t[1]              # sort by number of days in the month
    #return t[1], t[0]       # sort by number of days, then by month name
    #return len(t[0])        # sort by length of month name
    #return t[0][-1]         # sort by last character of month name


# Since a dictionary can't be sorted by value, what you can do is to convert
# it into a list of tuples with tuple length 2.
# You can then do custom sorts by passing your own function to sorted().
months_as_list = sorted(months.items(), key=mykey, reverse=False)

for month in months_as_list:
    print month
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top