Découpage d'un tableau 2d NumPy, ou comment puis-je extraire un sous-compatriote mxm d'un tableau nxn (n>m)?

je veux découper une rangée de NXN. Je veux extraire une sélection arbitraire de Lignes m et de colonnes de ce tableau (i.e. sans aucun motif dans les nombres de lignes/colonnes), ce qui en fait un nouveau tableau mxm. Pour cet exemple disons que le tableau est 4x4 et je veux en extraire un tableau 2x2.

voici notre tableau:

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

La ligne et les colonnes à supprimer sont les mêmes. Le cas le plus facile est quand je veux extraire une sous-matrice 2x2 qui est au début ou à la fin, i.e.:

In [33]: x[0:2,0:2]
Out[33]: 
array([[0, 1],
       [4, 5]])

In [34]: x[2:,2:]
Out[34]: 
array([[10, 11],
       [14, 15]])

mais que faire si je dois enlever un autre mélange de rangées / colonnes? Que se passe-t-il si je dois enlever les première et troisième lignes/rangées, en extrayant ainsi le submatrix [[5,7],[13,15]] ? Il peut y avoir n'importe quelle composition de lignes/lignes. J'ai lu quelque part que j'ai juste besoin d'indexer mon tableau en utilisant des tableaux / listes d'indices pour les lignes et les colonnes, mais cela ne semble pas fonctionner:

In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])

j'ai trouvé un moyen, qui est:

    In [61]: x[[1,3]][:,[1,3]]
Out[61]: 
array([[ 5,  7],
       [13, 15]])

le premier problème avec ceci est qu'il est à peine lisible, bien que je puisse vivre avec cela. Si quelqu'un a une meilleure solution, j'aimerais l'entendre.

autre chose est que je lis sur un forum que l'indexation des tableaux avec les forces de tableaux NumPy pour faire une copie du tableau désiré, donc en traitant avec de grands tableaux cela pourrait devenir un problème. Pourquoi est-ce si / comment est-ce le mécanisme fonctionne?

146
demandé sur Alex Riley 2010-11-23 18:05:08

7 réponses

comme Sven l'a mentionné, x[[[0],[2]],[1,3]] donnera les lignes 0 et 2 qui correspondent aux colonnes 1 et 3 tandis que x[[0,2],[1,3]] renverra les valeurs x[0,1] et x[2,3] dans un tableau.

il y a une fonction utile pour faire le premier exemple que j'ai donné, numpy.ix_ . Vous pouvez faire la même chose que mon premier exemple avec x[numpy.ix_([0,2],[1,3])] . Cela peut vous épargner d'avoir à entrer dans tous ces supports.

46
répondu Justin Peel 2010-11-23 16:07:25

pour répondre À cette question, nous devons examiner comment l'indexation d'un tableau multidimensionnel travaille dans Numpy. Disons d'abord que vous avez le tableau x de votre question. Le tampon assigné à x contiendra 16 entiers ascendants de 0 à 15. Si vous accédez à un élément, dites x[i,j] , NumPy doit trouver l'emplacement mémoire de cet élément par rapport au début de la mémoire tampon. Ceci est fait en calculant en vigueur i*x.shape[1]+j (et en multipliant avec le de la taille d'un int pour obtenir un réel décalage mémoire).

si vous extrayez un subarray par tranchage basique comme y = x[0:2,0:2] , l'objet résultant partagera le tampon sous-jacent avec x . Mais que se passe-t-il si vous accédez à y[i,j] ? NumPy ne peut pas utiliser i*y.shape[1]+j pour calculer l'offset dans le tableau, parce que les données appartenant à y ne sont pas consécutives en mémoire.

NumPy résout ce problème en introduisant foulées . Lors du calcul de l'offset mémoire pour accéder à x[i,j] , ce qui est effectivement calculé est i*x.strides[0]+j*x.strides[1] (et cela inclut déjà le facteur pour la taille d'un int):

x.strides
(16, 4)

quand y est extrait comme ci-dessus, NumPy ne crée pas un nouveau buffer, mais il crée un nouvel objet de tableau référençant le même buffer (sinon y serait juste égal à x .) Le nouveau tableau objet aura une forme différente que x et peut-être un décalage de départ différent dans le tampon, mais partagera les pas avec x (dans ce cas au moins):

y.shape
(2,2)
y.strides
(16, 4)

de cette façon, calculer l'offset mémoire pour y[i,j] donnera le résultat correct.

mais Qu'est-ce que NumPy devrait faire pour quelque chose comme z=x[[1,3]] ? Le mécanisme des enjambées ne permet pas une indexation correcte si le tampon d'origine est utilisé pour z . NumPy théoriquement pourrait ajouter un mécanisme plus sophistiqué que les enjambées, mais cela rendrait l'accès à l'élément relativement coûteux, défiant d'une certaine façon l'idée d'un ensemble. De plus, une vue ne serait plus un objet vraiment léger.

ceci est couvert en profondeur dans la documentation NumPy sur l'indexation .

OH, et j'ai presque oublié votre question actuelle: Voici comment faire fonctionner l'indexation avec des listes multiples comme prévu:

x[[[1],[3]],[1,3]]

c'est parce que les matrices d'index sont diffusé à une forme commune. Bien sûr, pour cet exemple particulier, vous pouvez également faire avec le tranchage de base:

x[1::2, 1::2]
102
répondu Sven Marnach 2013-08-28 09:14:45

Je ne pense pas que x[[1,3]][:,[1,3]] soit difficilement lisible. Si vous voulez être plus clair sur votre intention, Vous pouvez faire:

a[[1,3],:][:,[1,3]]

Je ne suis pas un expert en découpage mais typiquement, si vous essayez de découper dans un tableau et les valeurs sont continues, vous obtenez en arrière une vue où la valeur de foulée est changée.

par exemple dans vos entrées 33 et 34, bien que vous ayez un tableau 2x2, la foulée est 4. Ainsi, lorsque vous indexez la ligne suivante, le pointeur se déplace vers le position correcte dans la mémoire.

Clairement, ce mécanisme ne fait pas bien dans le cas d'un tableau d'indices. Par conséquent, numpy devra faire la copie. Après tout, de nombreuses autres fonctions de mathématiques matricielles dépendent de la taille, de la foulée et de l'attribution continue de la mémoire.

11
répondu Dat Chu 2018-06-10 23:37:21

si vous voulez sauter toutes les autres rangées et toutes les autres colonnes, alors vous pouvez le faire avec le tranchage de base:

In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]: 
array([[ 5,  7],
       [13, 15]])

Cela renvoie une vue, pas une copie de votre tableau.

In [51]: y=x[1:4:2,1:4:2]

In [52]: y[0,0]=100

In [53]: x   # <---- Notice x[1,1] has changed
Out[53]: 
array([[  0,   1,   2,   3],
       [  4, 100,   6,   7],
       [  8,   9,  10,  11],
       [ 12,  13,  14,  15]])

tandis que z=x[(1,3),:][:,(1,3)] utilise l'indexation Avancée et retourne donc une copie:

In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]

In [60]: z
Out[60]: 
array([[ 5,  7],
       [13, 15]])

In [61]: z[0,0]=0

noter que x est inchangé:

In [62]: x
Out[62]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

si vous souhaitez sélectionner des lignes et des colonnes arbitraires, alors vous Je ne peux pas utiliser de tranchage basique. Vous devrez utiliser l'indexation avancée, en utilisant quelque chose comme x[rows,:][:,columns] , où rows et columns sont des séquences. Bien sûr, cela va vous donner une copie, pas de vue, de votre tableau d'origine. C'est ce à quoi on devrait s'attendre, puisqu'un tableau numpy utilise une mémoire contiguë (avec des pas constants), et il n'y aurait aucun moyen de générer une vue avec des lignes et des colonnes arbitraires (puisque cela nécessiterait des pas non constants).

9
répondu unutbu 2015-02-24 11:50:51

avec numpy, vous pouvez passer une tranche pour chaque composante de l'index - ainsi, votre exemple x[0:2,0:2] ci-dessus fonctionne.

si vous voulez juste sauter uniformément des colonnes ou des rangées, vous pouvez passer des tranches avec trois composants (c'est à dire de démarrage, d'arrêt, de l'étape).

encore une fois, pour votre exemple ci-dessus:

>>> x[1:4:2, 1:4:2]
array([[ 5,  7],
       [13, 15]])

Qui est fondamentalement: tranche dans la première dimension, avec départ à l'indice 1, arrête lorsque l'indice est égale ou supérieure à 4, et ajouter 2 à l'index dans chaque passage. La même chose pour la deuxième dimension. Encore une fois: cela ne fonctionne que pour les pas constants.

la syntaxe que vous devez faire quelque chose de très différent en interne - ce que x[[1,3]][:,[1,3]] fait réellement est de créer un nouveau tableau comprenant seulement les lignes 1 et 3 du tableau original (fait avec la partie x[[1,3]] ), et puis re-trancher que - la création d'un troisième tableau - y compris seulement les colonnes 1 et 3 du tableau précédent.

5
répondu jsbueno 2015-10-17 18:06:03

j'ai une question similaire ici: ecrivant dans le sub-ndarray d'andarray de la manière la plus pythonienne. Python 2 .

suite à la solution de post précédent pour votre cas, la solution ressemble à:

columns_to_keep = [1,3] 
rows_to_keep = [1,3]

An utilisant ix_:

x[np.ix_(rows_to_keep, columns_to_keep)] 

qui est:

array([[ 5,  7],
       [13, 15]])
2
répondu Rafael Valero 2018-01-29 10:37:40

Je ne sais pas si c'est efficace, mais vous pouvez utiliser range() pour trancher dans les deux axes

 x=np.arange(16).reshape((4,4))
 x[range(1,3), :][:,range(1,3)] 
0
répondu Valery Marcel 2018-08-26 05:32:49