web-dev-qa-db-fra.com

Comment la méthode transpose () de NumPy permute-t-elle les axes d'un tableau?

In [28]: arr = np.arange(16).reshape((2, 2, 4))

In [29]: arr
Out[29]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])


In [32]: arr.transpose((1, 0, 2))
Out[32]: 
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],

       [[ 4,  5,  6,  7],
        [12, 13, 14, 15]]])

Lorsque nous passons un tuple d'entiers à la fonction transpose(), que se passe-t-il?

Pour être précis, il s’agit d’un tableau 3D: comment NumPy transforme-t-il le tableau lorsque je passe le tuple d’axes (1, 0 ,2)? Pouvez-vous expliquer à quelle ligne ou colonne ces nombres entiers font référence? Et que sont les nombres d'axes dans le contexte de NumPy?

52
Frank Hu

Pour transposer un tableau, NumPy échange simplement les informations de forme et de pas pour chaque axe. Voici les progrès:

>>> arr.strides
(64, 32, 8)

>>> arr.transpose(1, 0, 2).strides
(32, 64, 8)

Notez que l'opération de transposition a permuté les pas pour les axes 0 et 1. Les longueurs de ces axes ont également été permutées (les deux longueurs sont 2 Dans cet exemple).

Aucune donnée ne doit être copiée pour que cela se produise; NumPy peut simplement changer son apparence dans la mémoire sous-jacente pour construire le nouveau tableau.


Visualiser les progrès

La valeur stride représente le nombre d'octets qui doivent être parcourus en mémoire pour atteindre la valeur suivante d'un axe d'un tableau.

Maintenant, notre tableau 3D arr ressemble à ceci (avec des axes étiquetés):

enter image description here

Ce tableau est stocké dans un bloc de mémoire contig ; essentiellement, il est unidimensionnel. Pour être interprété comme un objet 3D, NumPy doit sauter par-dessus un certain nombre d'octets constant afin de pouvoir se déplacer sur l'un des trois axes:

enter image description here

Étant donné que chaque entier occupe 8 octets de mémoire (nous utilisons le type int64), la valeur de pas pour chaque dimension est 8 fois le nombre de valeurs à sauter. Par exemple, pour se déplacer le long de l'axe 1, quatre valeurs (32 octets) sont sautées et pour se déplacer le long de l'axe 0, huit valeurs (64 octets) doivent être sautées.

Lorsque nous écrivons arr.transpose(1, 0, 2), nous échangeons les axes 0 et 1. Le tableau transposé ressemble à ceci:

enter image description here

NumPy doit simplement échanger les informations de foulée contre les axes 0 et 1 (l'axe 2 est inchangé). Maintenant, il faut sauter plus loin pour se déplacer le long de l'axe 1 que de l'axe 0:

enter image description here

Ce concept de base fonctionne pour toute permutation des axes d'un tableau. Le code qui gère la transposition est écrit en C et peut être trouvé ici .

113
Alex Riley

Comme expliqué dans la documentation :

Par défaut, inversez les dimensions, sinon permutez les axes en fonction des valeurs indiquées.

Ainsi, vous pouvez passer un paramètre facultatif axes définissant le nouvel ordre de dimensions.

Par exemple. transposer les deux premières dimensions d'une matrice de pixels RVB VGA:

 >>> x = np.ones((480, 640, 3))
 >>> np.transpose(x, (1, 0, 2)).shape
 (640, 480, 3)
6
Falko

En notation C, votre tableau serait:

int arr[2][2][4]

qui est un tableau 3D ayant 2 tableaux 2D. Chacun de ces tableaux 2D a 2 tableaux 1D, chacun de ces tableaux 1D a 4 éléments.

Donc, vous avez trois dimensions. Les axes sont 0, 1, 2, de tailles 2, 2, 4. C'est exactement comme cela que numpy traite les axes d'un tableau à N dimensions.

Donc, arr.transpose((1, 0, 2)) prend l'axe 1 et le place en position 0, l'axe 0 et le place en position 1, et l'axe 2 et le laisse en position 2. Vous permutez effectivement les axes:

0 -\/-> 0
1 -/\-> 1
2 ----> 2

En d'autres termes, 1 -> 0, 0 -> 1, 2 -> 2. Les axes de destination sont toujours dans l'ordre, il vous suffit donc de spécifier les axes source. Lisez le tuple dans cet ordre: (1, 0, 2).

Dans ce cas, vos nouvelles dimensions de tableau sont à nouveau [2][2][4], Uniquement parce que les axes 0 et 1 ont la même taille (2).

Plus intéressant est une transposition par (2, 1, 0) Qui vous donne un tableau de [4][2][2].

0 -\ /--> 0
1 --X---> 1
2 -/ \--> 2

En d'autres termes, 2 -> 0, 1 -> 1, 0 -> 2. Lisez le tuple dans cet ordre: (2, 1, 0).

>>> arr.transpose((2,1,0))
array([[[ 0,  8],
        [ 4, 12]],

       [[ 1,  9],
        [ 5, 13]],

       [[ 2, 10],
        [ 6, 14]],

       [[ 3, 11],
        [ 7, 15]]])

Vous vous êtes retrouvé avec un int[4][2][2].

Vous obtiendrez probablement une meilleure compréhension si toutes les dimensions étaient de tailles différentes. Vous pourrez ainsi voir où chaque axe est allé.

Pourquoi le premier élément interne [0, 8]? Parce que si vous visualisez votre matrice 3D sous forme de deux feuilles de papier, 0 Et 8 Sont alignés, une sur une et une sur l'autre, les deux en haut à gauche. En transposant (2, 1, 0), Vous dites que vous voulez que le sens du papier à papier défile maintenant le long du papier de gauche à droite, et que le sens de gauche à droite passe maintenant de papier en papier. Vous avez eu 4 éléments allant de gauche à droite, donc maintenant vous avez quatre feuilles de papier à la place. Et comme vous aviez 2 papiers, vous avez maintenant 2 éléments qui vont de gauche à droite.

Désolé pour le terrible ASCII art. ¯\_(ツ)_/¯

2
Robert B