La fonction sorted () de python est-elle garantie pour être stable?

96

La documentation ne le garantit pas. Y a-t-il un autre endroit où cela est documenté?

Je suppose que cela pourrait être stable puisque la méthode de tri sur les listes est garantie d'être stable (Notes 9e point: "À partir de Python 2.3, la méthode sort () est garantie d'être stable"), et trié est fonctionnellement similaire. Cependant, je ne suis pas en mesure de trouver une source définitive qui le dise.

Objectif: je dois trier en fonction d'une clé primaire et également d'une clé secondaire dans les cas où la clé primaire est égale dans les deux enregistrements. Si sorted () est garanti pour être stable, je peux trier sur la clé secondaire, puis trier sur la clé primaire et obtenir le résultat dont j'ai besoin.

PS: Pour éviter toute confusion, j'utilise stable dans le sens de "un tri est stable s'il garantit de ne pas changer l'ordre relatif des éléments qui se comparent égaux".

sundar - Réintégrer Monica
la source

Réponses:

127

Oui, l'intention du manuel est en effet de garantir qu'il sortedest stable et en effet qu'il utilise exactement le même algorithme que la sortméthode. Je me rends compte que les documents ne sont pas clairs à 100% sur cette identité; les correctifs doc sont toujours acceptés avec plaisir!

Alex Martelli
la source
2
J'ai trouvé que si je trie des tuples ou des listes, chaque fois que les clés de tri "primaires" sont égales, il finit par trier par la clé "secondaire". Par exemple, sorted([(1, 2), (1, 1)])renvoie [(1, 1), (1, 2)]au lieu de renvoyer l'entrée d'origine dans la même séquence / ordre. La garantie de stabilité ne devrait-elle pas signifier qu'il devrait renvoyer l' [(1, 2), (1, 1)]entrée d' origine ? Dans ce cas, vous devez être explicite et diresorted([(1, 2), (1, 1)], key=lambda t: t[0])
code_dredd
10
N'est-ce pas ce à quoi on s'attend dans ce cas? Python va comparer les tuples à travers tous les éléments par défaut, pas seulement le premier "primaire". Si vous ne souhaitez trier que sur le premier élément, vous pouvez passer le keyparamètre explicitement.
Matias Grioni
2
@code_dredd c'est le comportement attendu. Le point du tri stable est le tri en utilisant une "clé de tri" mais deux éléments différents qui ont la même clé de tri seront dans le même ordre. La clé de tri par défaut pour un tuple est tous les éléments du tuple.
guyarad
27

Ils sont stables .

Au fait: vous pouvez parfois ignorer si le tri et le tri sont stables, en combinant un tri multi-passes dans un tri en un seul passage.

Par exemple, si vous voulez trier les objets en fonction de leurs last_name, first_nameattributs, vous pouvez le faire en un seul passage:

sorted_list= sorted(
    your_sequence_of_items,
    key= lambda item: (item.last_name, item.first_name))

tirant parti de la comparaison de tuple.

Cette réponse, telle quelle, couvre la question initiale. Pour d'autres questions liées au tri, il y a le guide de tri Python .

tzot
la source
4
Cela peut avoir un effet indésirable si vous souhaitez inverser le tri. Par exemple, lors du tri sur les produits, vous voudrez peut-être d'abord trier par note (ordre croissant), puis par prix (également croissant). Si vous inversez cela, vous souhaitez trier par ordre décroissant, mais par prix par ordre croissant. Cela ne fonctionne pas avec cette solution.
Remco Wendt
2
@RemcoWendt: il n'y avait aucune exigence pour ce que vous décrivez. Dans tous les cas, considérez key= lambda item: (-item.rating, item.price)ou fournissez cmpun keyargument au lieu d'un argument. Cependant, je ne suis toujours pas sûr du but de votre commentaire.
tzot
1
En effet, ce n'était pas une exigence, mais je voulais souligner cette différence subtile lorsque d'autres personnes lisent ceci et font un choix entre votre solution ou l'utilisation de la fonction de tri stable de Python.
Remco Wendt
Je vois. En d'autres termes, le tri par paires est plus clair et est donc préférable, sauf si vous vous souciez des performances. J'imagine que deux types stables sont un peu plus rapides qu'un tri par paires, bien que la différence puisse être négligeable -?
Sergey Orshanskiy
8
@tzot Je veux mentionner, il y a toujours de telles exigences pour un tri stable. Par exemple, j'ai une liste de tuple (taux, commentaire), les commentaires sont enregistrés dans l'ordre où ils sont faits, et je veux trier par le taux et conserver l'ordre temporel, cependant, je n'ai pas enregistré le horodatage dans la liste. Pour le dire brièvement, je veux juste trier la liste par taux et garder le commentaire dans le même ordre.
wsysuper
3

La documentation a changé entre-temps ( commit pertinent ) et la documentation actuelle de sortedle garantit explicitement:

La sorted()fonction intégrée est garantie pour être stable. Un tri est stable s'il garantit de ne pas changer l'ordre relatif des éléments qui se comparent égaux - cela est utile pour le tri en plusieurs passes (par exemple, trier par département, puis par classe de salaire).

Cette partie de la documentation a été ajoutée à Python 2.7 et Python 3.4 (+), donc toute implémentation conforme de cette version du langage doit avoir un fichier stable sorted.

Notez que pour CPython, le list.sortest stable depuis Python 2.3

  • Tim Peters a réécrit son list.sort()implémentation - celle-ci est un "tri stable" (les entrées égales apparaissent dans le même ordre dans la sortie) et plus rapide qu'avant.

Je ne suis pas sûr à 100% sorted, de nos jours, c'est simple list.sort, mais je n'ai pas vérifié l'historique pour cela. Mais il est probable qu'il soit «toujours» utilisé list.sort.

MSeifert
la source
0

Les documents "What's New" pour Python 2.4 font effectivement le point que sorted () crée d'abord une liste, puis appelle sort () dessus, vous fournissant la garantie dont vous avez besoin mais pas dans la documentation "officielle". Vous pouvez également simplement vérifier la source, si vous êtes vraiment inquiet.

Peter Hansen
la source
1
Pourriez-vous indiquer où cela est dit? Il dit que sorted () "fonctionne comme la liste in-place list.sort ()" et "une copie nouvellement formée est triée", mais je ne le vois pas dire qu'il utilise en interne sort ().
sundar - Réintégrer Monica
La "copie" qui est formée est une liste (c'est ce que vous obtenez comme valeur de retour), et .sort () est appelée sur cette liste avant de retourner. QED. Non, ce n'est pas une preuve inattaquable, mais tant que Python n'aura pas une norme officielle, vous ne l'obtiendrez pas.
Peter Hansen
0

La documentation Python 3.6 sur le tri indique maintenant que

Les tris sont garantis stables

En outre, dans ce document, il y a un lien vers l'écurie Timsort , qui indique que

Timsort est l'algorithme de tri standard de Python depuis la version 2.3

Wolfgang Kuehn
la source