186 votes

Rotation d'un tableau bidimensionnel en Python

Dans un programme que j'écris, la nécessité de faire pivoter un tableau à deux dimensions est apparue. En cherchant la solution optimale, j'ai trouvé cette impressionnante ligne simple qui fait l'affaire :

rotated = zip(*original[::-1])

Je l'utilise actuellement dans mon programme et il fonctionne comme prévu. Mon problème, cependant, est que je ne comprends pas comment il fonctionne.

J'apprécierais que quelqu'un puisse expliquer comment les différentes fonctions impliquées permettent d'atteindre le résultat souhaité.

10 votes

En effet. Je l'ai trouvé dans ce Une question.

166voto

kindall Points 60645

C'est très malin.

Premièrement, comme indiqué dans un commentaire, dans Python 3 zip() renvoie un itérateur, il faut donc enfermer le tout dans list() pour obtenir une liste réelle, donc à partir de 2020, c'est en fait.. :

list(zip(*original[::-1]))

Voici le détail :

  • [::-1] - fait une copie superficielle de la liste originale dans l'ordre inverse. On peut aussi utiliser reversed() qui produirait un itérateur inversé sur la liste plutôt que de copier réellement la liste (plus efficace en mémoire).
  • * - fait de chaque sous-liste de la liste originale un argument séparé pour zip() (c'est-à-dire qu'il dépile la liste)
  • zip() - prend un élément de chaque argument et en fait une liste (enfin, un tuple), et répète jusqu'à ce que toutes les sous-listes soient épuisées. C'est ici que la transposition se produit réellement.
  • list() convertit la sortie de zip() à une liste.

Donc, en supposant que vous avez ceci :

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

Vous obtenez d'abord ceci (copie inversée et peu profonde) :

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

Ensuite, chacune des sous-listes est passée en argument à zip :

zip([7, 8, 9], [4, 5, 6], [1, 2, 3])

zip() consomme de manière répétée un élément du début de chacun de ses arguments et en fait un tuple, jusqu'à ce qu'il n'y ait plus d'éléments, ce qui donne (après conversion en liste) :

[(7, 4, 1), 
 (8, 5, 2), 
 (9, 6, 3)]

Et Bob est ton oncle.

Pour répondre à la question de @IkeMiguel dans un commentaire concernant la rotation dans l'autre sens, c'est assez simple : il suffit d'inverser les deux séquences qui vont dans zip et le résultat. Le premier peut être obtenu en supprimant le [::-1] et la seconde peut être réalisée en lançant un reversed() autour de tout ça. Depuis reversed() renvoie un itérateur sur la liste, nous devrons mettre la fonction list() autour de que pour le convertir. Avec un peu plus de list() pour convertir les itérateurs en une liste réelle. Donc :

rotated = list(reversed(list(zip(*original))))

Nous pouvons simplifier un peu cela en utilisant la tranche "smiley martien" plutôt que reversed() ... alors nous n'avons pas besoin de l'extérieur. list() :

rotated = list(zip(*original))[::-1]

Bien sûr, vous pouvez aussi simplement faire tourner la liste trois fois dans le sens des aiguilles d'une montre :-)

5 votes

Est-il possible de tourner dans le sens inverse des aiguilles d'une montre ?

8 votes

@MiguelIke oui, faites zip(*matrix)[::-1]

4 votes

Notez que vous devez convertir le résultat de la fonction zip à une liste en Python 3.x !

121voto

Andrew Clark Points 77748

Considérons la liste bidimensionnelle suivante :

original = [[1, 2],
            [3, 4]]

Allons-y étape par étape :

>>> original[::-1]   # elements of original are reversed
[[3, 4], [1, 2]]

Cette liste est passée dans zip() en utilisant déballage des arguments donc le zip l'appel finit par être l'équivalent de ceci :

zip([3, 4],
    [1, 2])
#    ^  ^----column 2
#    |-------column 1
# returns [(3, 1), (4, 2)], which is a original rotated clockwise

Nous espérons que les commentaires indiquent clairement ce que zip regroupe les éléments de chaque itérable d'entrée en fonction de l'index, ou en d'autres termes, il regroupe les colonnes.

4 votes

Un proche. Mais j'ai choisi la vôtre en raison de l'art ASCII soigné ;)

2 votes

Et l'astérisque ? ??

0 votes

@johnktejik - c'est la partie "déballage des arguments" de la réponse, cliquez sur le lien pour plus de détails.

22voto

setrofim Points 440

Il y a trois parties à cela :

  1. original[::-1] inverse le tableau original. Cette notation est un découpage de liste Python. Cela vous donne une "sous-liste" de la liste originale décrite par [start:end:step], start est le premier élément, end est le dernier élément à utiliser dans la sous-liste. step indique de prendre chaque élément de step du premier au dernier. L'omission du début et de la fin signifie que la tranche sera la liste entière, et le pas négatif signifie que vous obtiendrez les éléments en sens inverse. Donc, par exemple, si l'original était [x,y,z], le résultat serait [z,y,x].
  2. L'astérisque *, lorsqu'il précède une liste/tuple dans la liste d'arguments d'un appel de fonction, signifie "développer" la liste/tuple de sorte que chacun de ses éléments devienne un argument distinct de la fonction, plutôt que la liste/tuple elle-même. Ainsi, si, par exemple, args = [1,2,3], zip(args) est identique à zip([1,2,3]), mais zip(*args) est identique à zip(1,2,3).
  3. zip est une fonction qui prend n arguments dont chacun est de longueur m et produit une liste de longueur m, dont les éléments sont de longueur n et contiennent les éléments correspondants de chacune des listes originales. Par exemple, zip([1,2],[a,b],[x,y]) donne [[1,a,x],[2,b,y]]. Voir aussi Documentation Python.

0 votes

+1 puisque vous étiez le seul à expliquer probablement la première étape.

10voto

MarkS Points 400

Juste une observation. L'entrée est une liste de listes, mais la sortie de la très belle solution : rotated = zip(*original[::-1]) retourne une liste de tuples.

Cela peut être un problème ou non.

Il est cependant facile à corriger :

original = [[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]
            ]

def rotated(array_2d):
    list_of_tuples = zip(*array_2d[::-1])
    return [list(elem) for elem in list_of_tuples]
    # return map(list, list_of_tuples)

print(list(rotated(original)))

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

Le comp. de liste ou le map reconvertiront tous deux les tuples intérieurs en listes.

3voto

user9402118 Points 29
def ruota_orario(matrix):
   ruota=list(zip(*reversed(matrix)))
   return[list(elemento) for elemento in ruota]
def ruota_antiorario(matrix):
   ruota=list(zip(*reversed(matrix)))
   return[list(elemento)[::-1] for elemento in ruota][::-1]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X