[Python 3] Méthodes de tri

ARTICLE EN COURS DE CONSTRUCTION

### 2 - TRI - ###

### 2.1 TRI : tuple
aa = (8, 4, 6, 1, 9, 5, 2, 7, 0, 3)
### 2.1.1 TRI : tuple : sorted()
print("### 2.1.1 TRI : tuple : sorted()")
sorted(aa)
print(aa)
print(sorted(aa))
print()

### 2.2 TRI : list à une dimension
aa = [8, 4, 6, 1, 9, 5, 2, 7, 0, 3]
### 2.2.1 TRI : list à une dimension : sorted()
print("### 2.2.1 TRI : list à une dimension : sorted()")
sorted(aa)
print(aa)
print(sorted(aa))
print()

### 2.2.2 TRI : list à une dimension : sort()
print("### 2.2.2 TRI : list à une dimension : sort()")
aa.sort()
print(aa)
print()

### 2.3 TRI : list à plusieurs dimensions
aa = [
("h", "J", 2),
("j", "I", 8),
("a", "G", 4),
("g", "H", 3),
("i", "E", 6),
("b", "D", 5),
("c", "B", 0),
("d", "F", 7),
("f", "C", 1),
("e", "A", 9)
]
### 2.3.1 TRI : list à plusieurs dimensions : sorted()
print("### 2.3 TRI : list à plusieurs dimensions : sorted()")
sorted(aa)
print(aa)
print(sorted(aa))
print()

### 2.3.2 TRI : list à plusieurs dimensions : lambda
print("### 2.3 TRI : list à plusieurs dimensions : lambda")
xx = sorted(aa, key=lambda x: x[0])
print(*xx)
yy = sorted(aa, key=lambda x: x[1])
print(*yy)
zz = sorted(aa, key=lambda x: x[2])
print(*zz)
print()

### 2.3.3 TRI : list à plusieurs dimensions : itemgetter
aa = [
("abricot ", "catégorie 1 ", "prix 1"),
("pomme ", "catégorie 1 ", "prix 1"),
("orange ", "catégorie 1 ", "prix 1"),
("orange ", "catégorie 2 ", "prix 3"),
("raisin ", "catégorie 1 ", "prix 3"),
("abricot ", "catégorie 2 ", "prix 2"),
("pomme ", "catégorie 1 ", "prix 1"),
("abricot ", "catégorie 2 ", "prix 2"),
("pomme ", "catégorie 1 ", "prix 1"),
("orange ", "catégorie 2 ", "prix 2")
]
from operator import itemgetter as ig
print("### 2.3 TRI : list à plusieurs dimensions : itemgetter")
zz = sorted(aa, key=ig(0,1,2))
print(*zz)
zz = sorted(aa, key=ig(1,0,2))

for i, j, k in zz:
print(i, j, k)

print()

### 2.4 TRI : dict
dico = {"Alain": 2, "Bernard": 3, "Christian": 4, "Dominique": 1}

### 2.4.1 TRI : dict : lambda
print("### 2.4.1 TRI : dict : lambda")
aa = dico.items()

zz = sorted(aa, key=lambda x: x[0])
print(*zz)
zz = sorted(aa, key=lambda x: x[1])
print(*zz)
print()

### 2.4.2 TRI : dict : itemgetter
from operator import itemgetter as ig
print("### 2.4.2 TRI : dict : itemgetter")

aa = dico.items()

zz = sorted(aa, key=ig(0))
print(*zz)
zz = sorted(aa, key=ig(1))
print(*zz)
print()

### 2.5 TRI : class
### 2.5.1 TRI : class : list (ou tuple) : attrgetter
class Fruits:
def __init__(self, aa, bb):
self.prod = aa
self.prix = bb

def __repr__(self):
return "\n{} : {}€".format(self.prod, self.prix)

fruits = [
Fruits("poires ", 3),
Fruits("pommes ", 2),
Fruits("oranges", 2),
Fruits("cerises", 4),
Fruits("abricot", 2)
]

from operator import attrgetter as ag
print("### 2.5.1 TRI : class : list (ou tuple) : attrgetter")
zz = sorted(fruits, key=ag("prod", "prix"))
print(*zz)

zz = sorted(fruits, key=ag("prix", "prod"))
print(*zz)
print()

### 2.5.2 TRI : class : dict : attrgetter
dico = {}
dico["poires"] = Fruits("poires ", 3)
dico["pommes"] = Fruits("pommes ", 2)
dico["oranges"] = Fruits("oranges ", 2)
dico["cerises"] = Fruits("cerises ", 4)
dico["abricots"] = Fruits("abricots", 2)

from operator import attrgetter as ag
print("### 2.5.2 TRI : class : dict : attrgetter")

zz = sorted(dico.values(), key=ag("prod", "prix"))
print(*zz)
zz = sorted(dico.values(), key=ag("prix", "prod"))
print(*zz)
Publicités

[Python 3] Trier un « Dictionnaire »

Dans de nombreux langages informatiques, on retrouve le « Dictionnaire », cette variable un petit peu particulière qui regroupe l’association d’un ensemble de « clés » à leur « valeur », comme un dictionnaire est le regroupement d’un ensemble de mots associés à leur définition.

Je ne m’étendrai pas, ici, sur le fonctionnement du dictionnaire; je rappellerai néanmoins que dans un dictionnaire, chaque clé (le mot) est unique (mais plusieurs clés peuvent avoir la même valeur).

En Python, on remarquera que l’affichage du contenu d’un dictionnaire est aléatoire; ainsi, si j’établis mon dictionnaire dico de la façon suivante et que j’en demande l’affichage par une fonction print()

dico = {"zéro" : 0, "un" : 1, "deux" : 2, "trois" : 3, "quatre" : 4, "cinq" : 5}
print(dico)

Résultat : {‘un’: 1, ‘quatre’: 4, ‘deux’: 2, ‘zéro’: 0, ‘cinq’: 5, ‘trois’: 3}

je constate que, même si l’affichage n’est pas particulièrement attrayant, les éléments qui composent notre dictionnaire sont présentés de façon erratique.

N’ayant pas la possibilité de modifier l’ordre d’affichage dans un dictionnaire, nous allons contourner ce problème en enregistrant l’ensemble de notre dictionnaire dans une variable que nous appellerons ici mavar

mavar = dico.items()

Notons simplement que pour réussir notre opération, nous devons faire appel à la méthode items().
Ensuite, nous appliquons la méthode sorted() pour effectuer le tri et l’enregistrons dans une variable que nous appelons montri

montri = sorted(mavar, key=lambda x: x[1])

La méthode sorted() appelle tout d’abord l’objet que nous souhaitons trier, à savoir la variable mavar dans laquelle nous avons enregistré l’ensemble des binômes contenus dans notre dictionnaire.

Nous choisissons ensuite, avec la suite key=lambda x: x[1], la « clé » key de notre tri (à ne pas confondre avec les clés du dictionnaire), qui va définir le paramètre de tri (tri par clés du dictionnaire – zéro, un, deux etc… – auquel cas la valeur entre crochets sera x[0], ou par valeurs – 0, 1, 2 – avec x[1]).

A noter que si nous voulions notre affichage dans l’ordre descendant, nous aurions rajouté à notre méthode sorted() la commande reverse=True, comme ceci

montri = sorted(mavar, key=lambda x: x[1], reverse=True)

Pour info : il existe d’autres méthodes pour faire un tri en Python, notamment en faisant appel à la commande itemgetter de la bibliothèque operator.

Il ne nous reste plus qu’à afficher le résultat de notre tri, avec une « liste en intention », par exemple, alternative intéressante à la traditionnelle boucle en for

[print(j, i) for i, j in montri]

Quelques explications concernant cette ligne : rappelons que la variable montri est composée de binômes :
zéro, 0
un, 1
deux, 2…
i et j vont respectivement venir extraire les clés (zéro, un, deux, trois…) et les valeurs (0, 1, 2..).
Notre ligne de code demande donc d’afficher successivement chaque binôme j, i (print(j, i) nous avons inversé l’ordre d’affichage) par l’intermédiaire d’une boucle (for i, j) contenu dans notre objet montri.

Voici le code dans sa totalité :

dico = {"zéro" : 0, "un" : 1, "deux" : 2, "trois" : 3, "quatre" : 4, "cinq" : 5}
mavar = dico.items()
montri = sorted(mavar, key=lambda x: x[1])
[print(j, i) for i, j in montri]


0 zéro
1 un
2 deux
3 trois
4 quatre
5 cinq

[VBA] Trier un tableau avec un double-clic

Voici un petit script qui permet de trier un tableau en fonction de la colonne du tableau dans laquelle l’utilisateur fait un double-clic; nous allons aborder ici 2 fonctions d’Excel : la programmation des macros dites évènementielles et la fonction de tri.

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)

Dim myRange As Range

Cancel = True

Set myRange = Range("D10").CurrentRegion

If Not Intersect(myRange, Target) Is Nothing Then
    myRange.Sort key1:=Cells(myRange.Cells(1).Row, Target.Column), Header:=xlYes
End If

Target.Select

End Sub

Explications

La première notion que nous allons aborder est celle d’évènement.
Un évènement peut être l’ouverture d’un classeur, un clic droit, un changement d’onglet etc… ici, l’évènement qui va automatiquement déclencher notre macro est le double-clic.

Pour configurer l’évènement, nous ouvrons la fenêtre VBA (ALT + F11) et accédons à la fenêtre de projets (CTRL + R) si celle-ci n’est pas ouverte.
Evenement

Nous pouvons choisir d’activer l’évènement à tout le classeur ou bien à une seule des feuilles de ce classeur.
Dans notre cas de figure, il s’agit de l’appliquer à une seule feuille, nous allons donc là sélectionner dans notre fenêtre de projets à gauche et allons ensuite sélectionner l’option Worksheet du petit menu déroulant comme indiqué sur l’image.
Si nous avions voulu appliquer l’évènement à tout le classeur, nous aurions sélectionné l’option ThisWorkbook dans la fenêtre de gauche et ensuite l’option Workbook du menu déroulant.
En sélectionnant l’option Worksheet, le message suivant apparait dans notre fenêtre de droite

Private Sub Worksheet_SelectionChange(ByVal Target As Range)

End Sub

et l’option SelectionChange dans le menu déroulant de droite.
Nous pouvons effacer Private Sub Worksheet_SelectionChange(ByVal Target As Range)
End Sub
et nous allons sélectionner l’option BeforeDoubleClick dans le menu déroulant de droite.
Apparait alors le code suivant dans la fenêtre générale :

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)

End Sub

Après avoir déclaré notre variable Dim myRange As Range, nous instruisons Excel sur la gestion du double-clic Cancel = True : en temps normal, lorsque nous double-cliquons dans une cellule, le curseur sélectionne cette cellule et se met en position d’écriture. Cette commande empêche le curseur de sélectionner la cellule car ce n’est pas, ici, le but recherché par le double-clic.

L’étape suivante consiste à définir la plage pour laquelle le double-clic sera actif :
Set myRange = Range("D10").CurrentRegion
Une plage est un « objet », donc la variable myRange à laquelle nous allons allouer cette plage est suivi de la commande Set (voir l’article : Déterminer la dernière ligne/colonne d’un tableau pour de plus amples explications).
La commande .CurrentRegion qui suit la cellule D10 (que nous avons choisie arbitrairement, toute autre cellule appartenant au tableau ou adjacente à celui-ci fonctionne)  demande à Excel de prendre en compte la plage de cellules adjacentes et non vides autour de la cellule D10. Pour mieux comprendre son fonctionnement, voici une image sur laquelle apparait en jaune la plage sélectionnée par la commande Range("B4").CurrentRegion.
CurrentRegion

Dans notre cas, la plage correspond au tableau que l’on va trier.

La commande If Not Intersect(myRange, Target) Is Nothing Then va vérifier si le double-clic doit générer le tri du tableau ou pas; pour cela elle va contrôler que le double-clic a lieu dans notre plage myRange précédemment définie, autrement dit, dans le tableau.
Nous connaissons donc la plage définie par myRange; Target correspond à la cellule sur laquelle nous double-cliquons.
Traduisons notre commande : si (If) l’intersection (Intersect) entre la plage de mon tableau et la cellule sur laquelle nous double-cliquons (myRange, Target ) n’est pas rien (Not … Is Nothing) Then (alors exécuter…). autrement dit si la cellule sur laquelle nous double-cliquons se trouve dans la plage de notre tableau, alors exécuter…

myRange.Sort effectue le tri pour la plage myRange.

Header:=xlYes demande à la macro de considérer la première ligne de la plage comme étant l’en-tête du tableau et de ne pas l’inclure dans le tri; une valeur xlNo aurait également procédé au tri de la première ligne de la plage séléctionnée.

key1:=Cells(myRange.Cells(1).Row, Target.Column) cette dernière partie du code détermine la clé du tri; dans un cas figure normal, on peut trier un tableau avec plusieurs clés de tri (Key1, Key2, Key3… en fonction de l’ordre du tri).
Mais, dans notre cas de figure, puisqu’on ne peut double-cliquer que sur une seule cellule à la fois, nous sommes limités à une seule valeur de tri. La clé de tri correspond à la cellule de l’en-tête choisie pour réaliser le tri : Key1 sera donc suivi de l’adresse d’une cellule appartenant à l’en-tête du tableau.

La syntaxe de la commande Cells est la suivante : Cells(N° de ligne de la cellule, N° de colonne de la cellule).
myRange.Cells(1).Row représente donc le N° d’une ligne et Target.Column, le N° d’une colonne.
Target.Column identifie le N° de colonne de la cellule sur laquelle nous avons double-cliqué; .Column signifie N° de colonne, à ne pas confondre avec Columns qui représente une colonne physique.

Pour myRange.Cells(1).Row, c’est à peine plus compliqué. Myrange identifie notre plage; une plage est composée de cellules.
Puisque nous avons une collection (une série) identifiée de cellules, nous savons que chaque cellule de cette collection possède un index (autrement dit un N° d’ordre classant cette cellule par rapport aux autres cellules de la plage; voir l’article Classer les onglets d’un classeur par ordre alphanumérique pour plus d’informations concernant le fonctionnement de l’index). Nous savons que la première cellule de notre plage myRange.Cells(1) correspond à la première cellule composant l’en-tête de notre tableau, autrement dit la cellule B2 nommée N° INSEE.
Pour vous en convaincre, tapez myRange.Cells(1).Select dans le code programme juste sous l’instruction Set myRange = Range("D10").CurrentRegion

Il ne nous en faut pas plus pour définir le N° de ligne de notre cellule. Comme nous avons extrait le N° de colonne, nous allons récupérer le N° de ligne en ajoutant la commande .Row (qui est le N° de ligne à ne pas confondre avec Rows qui représente une ligne physique dans la grille).
Les paramètres de la cellule servant de clé pour le tri sont maintenant définis; il nous reste à spécifier la commande Target.Select afin de maintenir le curseur sur la cellule sur laquelle a été effectué le double-clic.

Fichier au format .xls ci-dessous
Tri Auto