web-dev-qa-db-fra.com

Trier la liste des listes avec une fonction de comparaison personnalisée dans Python

Je sais qu'il y a plusieurs questions nommées comme ça, mais je n'arrive pas à obtenir leurs réponses au travail.

J'ai une liste de listes, 50 fois 5 éléments. Maintenant, je veux trier cette liste en appliquant une fonction de comparaison personnalisée à chaque élément. Cette fonction calcule l'aptitude de la liste selon laquelle les éléments doivent être triés. J'ai créé deux fonctions, comparer et fitness:

def compare(item1, item2):
    return (fitness(item1) < fitness(item2))

et

def fitness(item):
    return item[0]+item[1]+item[2]+item[3]+item[4]

Ensuite, j'ai essayé de les appeler par:

sorted(mylist, cmp=compare)

ou

sorted(mylist, key=fitness)

ou

sorted(mylist, cmp=compare, key=fitness)

ou

sorted(mylist, cmp=lambda x,y: compare(x,y))

J'ai également essayé list.sort () avec les mêmes paramètres. Mais dans tous les cas, les fonctions n'obtiennent pas de liste en argument mais un None. Je ne sais pas pourquoi, venant principalement de C++, cela contredit toute idée d'une fonction de rappel pour moi. Comment puis-je trier ces listes avec une fonction personnalisée?

Modifier J'ai trouvé mon erreur. Dans la chaîne qui crée la liste d'origine, une fonction n'a rien renvoyé, mais la valeur de retour a été utilisée. Désolé pour le dérangement

49
DaClown
>>> l = [list(range(i, i+4)) for i in range(10,1,-1)]
>>> l
[[10, 11, 12, 13], [9, 10, 11, 12], [8, 9, 10, 11], [7, 8, 9, 10], [6, 7, 8, 9], [5, 6, 7, 8], [4, 5, 6, 7], [3, 4, 5, 6], [2, 3, 4, 5]]
>>> sorted(l, key=sum)
[[2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8], [6, 7, 8, 9], [7, 8, 9, 10], [8, 9, 10, 11], [9, 10, 11, 12], [10, 11, 12, 13]]

Les travaux ci-dessus. Faites-vous quelque chose de différent?

Notez que votre fonction clé est simplement sum; il n'est pas nécessaire de l'écrire explicitement.

27
Katriel

En outre, votre fonction de comparaison est incorrecte. Il doit renvoyer -1, 0 ou 1, pas un booléen comme vous l'avez. La fonction de comparaison correcte serait:

def compare(item1, item2):
    if fitness(item1) < fitness(item2):
        return -1
    Elif fitness(item1) > fitness(item2):
        return 1
    else:
        return 0
63
Michael Mouse

Vous devez modifier légèrement votre fonction compare et utiliser functools.cmp_to_key pour le passer à sorted. Exemple de code:

import functools

lst = [list(range(i, i+5)) for i in range(5, 1, -1)]

def fitness(item):
    return item[0]+item[1]+item[2]+item[3]+item[4]
def compare(item1, item2):
    return fitness(item1) - fitness(item2)

sorted(lst, key=functools.cmp_to_key(compare))

Sortie:

[[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9]]

Travaux :)

13
JustAC0der

Étant donné que l'OP demandait d'utiliser une fonction de comparaison personnalisée (et c'est ce qui m'a amené à cette question également), je veux donner une réponse solide ici:

En règle générale, vous souhaitez utiliser la fonction sorted() intégrée qui prend un comparateur personnalisé comme paramètre. Nous devons faire attention au fait qu'en Python 3 le nom du paramètre et la sémantique ont changé.

Fonctionnement du comparateur personnalisé

Lors de la fourniture d'un comparateur personnalisé, il doit généralement renvoyer une valeur entière/flottante qui suit le modèle suivant (comme avec la plupart des autres langages de programmation et frameworks):

  • renvoie une valeur négative (< 0) lorsque l'élément de gauche doit être trié avant l'élément de droite
  • renvoie une valeur positive (> 0) lorsque l'élément de gauche doit être trié après l'élément de droite
  • retourner 0 lorsque les articles de gauche et de droite ont le même poids et doivent être commandés "également" sans priorité

Dans le cas particulier de la question du PO, la fonction de comparaison personnalisée suivante peut être utilisée:

def compare(item1, item2):
    return fitness(item1) - fitness(item2)

L'utilisation de l'opération moins est une astuce astucieuse car elle donne des valeurs positives lorsque le poids de l'élément gauche (ici: item1) Est plus grand que le poids de l'élément droit (ici: item2). Par conséquent, l'élément1 sera trié après l'élément2.

Si vous souhaitez inverser l'ordre de tri, inversez simplement la soustraction: return fitness(item2) - fitness(item1)

Appel de sorted () dans Python 2

sorted(mylist, key=cmp(compare))

ou:

sorted(mylist, cmp=lambda item1, item2: fitness(item1) - fitness(item2))

Appel de sorted () dans Python 3

from functools import cmp_to_key
sorted(mylist, key=cmp_to_key(compare))

ou:

from functools import cmp_to_key
sorted(mylist, key=cmp_to_key(lambda item1, item2: fitness(item1) - fitness(item2)))
1
Lars Blumberg