J'ai examiné des documentations et aussi d'autres questions ici, mais il semble que je n'ai pas encore le bricolage de sous-ensembles numpy.
J'ai un tableau numpy, .__ et pour les besoins de l’argumentation, qu’il soit défini comme suit:
import numpy as np
a = np.arange(100)
a.shape = (10,10)
# array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
# [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
# [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
# [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
# [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
# [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
# [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
# [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
# [80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
# [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
maintenant, je veux choisir les lignes et les colonnes de a
spécifiées par les vecteurs n1
et n2
. Par exemple:
n1 = range(5)
n2 = range(5)
Mais quand j'utilise:
b = a[n1,n2]
# array([ 0, 11, 22, 33, 44])
Ensuite, seuls les cinq premiers éléments diagonaux sont choisis, pas le bloc entier 5x5. La solution que j'ai trouvée est de le faire comme ceci:
b = a[n1,:]
b = b[:,n2]
# array([[ 0, 1, 2, 3, 4],
# [10, 11, 12, 13, 14],
# [20, 21, 22, 23, 24],
# [30, 31, 32, 33, 34],
# [40, 41, 42, 43, 44]])
Mais je suis sûr qu'il devrait y avoir un moyen de faire cette tâche simple en une seule commande.
Vous avez une poignée d'exemples intéressants sur la manière de faire ce que vous voulez. Cependant, il est également utile de comprendre ce qui se passe et pourquoi les choses fonctionnent comme elles le font. Il existe quelques règles simples qui vous aideront à l’avenir.
Il y a une grande différence entre l'indexation "de fantaisie" (c'est-à-dire en utilisant une liste/séquence) et l'indexation "normale" (en utilisant une tranche). La raison sous-jacente est liée à la question de savoir si le tableau peut ou non être "parcouru régulièrement", et par conséquent si une copie doit ou non être réalisée. Les séquences arbitraires doivent donc être traitées différemment si l'on veut pouvoir créer des "vues" sans faire de copies.
Dans ton cas:
import numpy as np
a = np.arange(100).reshape(10,10)
n1, n2 = np.arange(5), np.arange(5)
# Not what you want
b = a[n1, n2] # array([ 0, 11, 22, 33, 44])
# What you want, but only for simple sequences
# Note that no copy of *a* is made!! This is a view.
b = a[:5, :5]
# What you want, but probably confusing at first. (Also, makes a copy.)
# np.meshgrid and np.ix_ are basically equivalent to this.
b = a[n1[:,None], n2[None,:]]
Une indexation sophistiquée avec des séquences 1D revient à les compresser et à les indexer avec le résultat.
print "Fancy Indexing:"
print a[n1, n2]
print "Manual indexing:"
for i, j in Zip(n1, n2):
print a[i, j]
Cependant, si les séquences avec lesquelles vous indexez correspondent à la dimensionnalité du tableau que vous indexez (2D dans ce cas), l'indexation est traitée différemment. Au lieu de "compresser les deux ensemble", numpy utilise les index comme un masque.
En d'autres termes, a[[[1, 2, 3]], [[1],[2],[3]]]
est traité de manière complètement différente de a[[1, 2, 3], [1, 2, 3]]
, car les séquences/tableaux que vous transmettez sont bidimensionnels.
In [4]: a[[[1, 2, 3]], [[1],[2],[3]]]
Out[4]:
array([[11, 21, 31],
[12, 22, 32],
[13, 23, 33]])
In [5]: a[[1, 2, 3], [1, 2, 3]]
Out[5]: array([11, 22, 33])
Pour être un peu plus précis,
a[[[1, 2, 3]], [[1],[2],[3]]]
est traité exactement comme:
i = [[1, 1, 1],
[2, 2, 2],
[3, 3, 3]])
j = [[1, 2, 3],
[1, 2, 3],
[1, 2, 3]]
a[i, j]
En d'autres termes, le fait que l'entrée soit un vecteur ligne/colonne est un raccourci pour savoir comment les index doivent se répéter dans l'indexation.
np.meshgrid
et np.ix_
ne sont que des moyens pratiques de transformer vos séquences 1D en versions 2D pour l'indexation:
In [6]: np.ix_([1, 2, 3], [1, 2, 3])
Out[6]:
(array([[1],
[2],
[3]]), array([[1, 2, 3]]))
De même (l'argument sparse
le rendrait identique à ix_
ci-dessus):
In [7]: np.meshgrid([1, 2, 3], [1, 2, 3], indexing='ij')
Out[7]:
[array([[1, 1, 1],
[2, 2, 2],
[3, 3, 3]]),
array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3]])]
Un autre moyen rapide de créer l’index souhaité est d’utiliser la fonction np.ix_
:
>>> a[np.ix_(n1, n2)]
array([[ 0, 1, 2, 3, 4],
[10, 11, 12, 13, 14],
[20, 21, 22, 23, 24],
[30, 31, 32, 33, 34],
[40, 41, 42, 43, 44]])
Cela fournit un moyen pratique de construire un maillage ouvert à partir de séquences d'indices.
Vous pouvez utiliser np.meshgrid
pour donner aux tableaux n1
, n2
la forme appropriée pour effectuer l'indexation souhaitée:
In [104]: a[np.meshgrid(n1,n2, sparse=True, indexing='ij')]
Out[104]:
array([[ 0, 1, 2, 3, 4],
[10, 11, 12, 13, 14],
[20, 21, 22, 23, 24],
[30, 31, 32, 33, 34],
[40, 41, 42, 43, 44]])
Ou, sans maillage:
In [117]: a[np.array(n1)[:,np.newaxis], np.array(n2)[np.newaxis,:]]
Out[117]:
array([[ 0, 1, 2, 3, 4],
[10, 11, 12, 13, 14],
[20, 21, 22, 23, 24],
[30, 31, 32, 33, 34],
[40, 41, 42, 43, 44]])
Il existe un exemple similaire avec une explication de la façon dont ce index de tableau entier fonctionne dans la documentation.
Voir aussi la recette du livre de cuisine Sélection des lignes et des colonnes .
Il semble qu'un cas d'utilisation correspondant à votre question concerne la manipulation d'images. Dans la mesure où vous utilisez votre exemple pour modifier des tableaux numpy issus d'images, vous pouvez utiliser la bibliothèque d'imagerie Python (PIL).
# Import Pillow:
from PIL import Image
# Load the original image:
img = Image.open("flowers.jpg")
# Crop the image
img2 = img.crop((0, 0, 5, 5))
L'objet img2 est un tableau numpy de l'image recadrée obtenue.
Vous pouvez en savoir plus sur la manipulation d'images ici avec le paquet Pillow (une fourche conviviale sur le paquet PIL):