web-dev-qa-db-fra.com

Que signifie "trois points" dans Python lors de l'indexation de ce qui ressemble à un nombre?

Quelle est la signification de x [...] ci-dessous?

a = np.arange(6).reshape(2,3)
for x in np.nditer(a, op_flags=['readwrite']):
    x[...] = 2 * x
52
Nan Hua

Alors que le doublon proposé Que fait l'objet Python Ellipsis? répond à la question dans un contexte général python, son utilisation dans une boucle nditer nécessite, je pense, des informations supplémentaires.

https://docs.scipy.org/doc/numpy/reference/arrays.nditer.html#modifying-array-values

Une affectation régulière dans Python change simplement une référence dans le dictionnaire de variables local ou global au lieu de modifier une variable existante à la place. Cela signifie que la simple affectation à x ne placera pas la valeur dans l'élément du tableau, mais fera en sorte que x ne soit plus une référence d'élément de tableau mais une référence à la valeur que vous avez attribuée. Pour réellement modifier l’élément du tableau, x doit être indexé avec l’Ellipsis.

Cette section comprend votre exemple de code.

Donc, dans mes mots, le x[...] = ... modifie x sur place; x = ... aurait cassé le lien vers la variable nditer sans le changer. C'est comme x[:] = ... mais ça fonctionne avec des tableaux de n'importe quelle dimension (y compris 0d). Dans ce contexte, x n'est pas simplement un nombre, c'est un tableau.

Peut-être que la chose la plus proche de cette itération nditer, sans nditer, est:

In [667]: for i, x in np.ndenumerate(a):
     ...:     print(i, x)
     ...:     a[i] = 2 * x
     ...:     
(0, 0) 0
(0, 1) 1
...
(1, 2) 5
In [668]: a
Out[668]: 
array([[ 0,  2,  4],
       [ 6,  8, 10]])

Notez que je devais indexer et modifier a[i] directement. Je n'aurais pas pu utiliser, x = 2*x. Dans cette itération, x est un scalaire, et ne peut donc pas être modifié

In [669]: for i,x in np.ndenumerate(a):
     ...:     x[...] = 2 * x
  ...
TypeError: 'numpy.int32' object does not support item assignment

Mais dans le cas nditer, le cas x est un tableau 0d, et mutable.

In [671]: for x in np.nditer(a, op_flags=['readwrite']):
     ...:     print(x, type(x), x.shape)
     ...:     x[...] = 2 * x
     ...:     
0 <class 'numpy.ndarray'> ()
4 <class 'numpy.ndarray'> ()
...

Et comme il est 0d, x[:] ne peut pas être utilisé à la place de x[...]

----> 3     x[:] = 2 * x
IndexError: too many indices for array

Une itération de tableau plus simple pourrait également donner un aperçu:

In [675]: for x in a:
     ...:     print(x, x.shape)
     ...:     x[:] = 2 * x
     ...:     
[ 0  8 16] (3,)
[24 32 40] (3,)

cela itère sur les lignes (1ère dim) de a. x est alors un tableau 1d et peut être modifié avec x[:]=... ou x[...]=....

Et si j'ajoute le drapeau external_loop du prochain section , x est maintenant un tableau 1d et x[:] = fonctionnerait. Mais x[...] = fonctionne toujours et est plus général. x[...] est utilisé par tous les autres exemples nditer.

In [677]: for x in np.nditer(a, op_flags=['readwrite'], flags=['external_loop']):
     ...:     print(x, type(x), x.shape)
     ...:     x[...] = 2 * x
[ 0 16 32 48 64 80] <class 'numpy.ndarray'> (6,)

Comparez cette simple itération de ligne (sur un tableau 2d):

In [675]: for x in a:
     ...:     print(x, x.shape)
     ...:     x[:] = 2 * x
     ...:     
[ 0  8 16] (3,)
[24 32 40] (3,)

cela itère sur les lignes (1ère dim) de a. x est alors un tableau 1d et peut être modifié avec x[:] = ... ou x[...] = ....

Lisez et testez cette page nditer jusqu’à la fin. En soi, nditer n'est pas très utile dans python. Cela n'accélère pas l'itération - vous ne portez pas votre code dans cython .np.ndindex est l'une des rares fonctions numpy non compilées qui utilise nditer.

44
hpaulj