Co oznaczają „trzy kropki” w Pythonie podczas indeksowania czegoś, co wygląda jak liczba?

Odpowiedzi:

69

Podczas gdy proponowany duplikat Co robi obiekt Python Ellipsis? odpowiada na pytanie w ogólnym pythonkontekście, jego użycie w nditerpętli wymaga, jak sądzę, dodatkowych informacji.

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

Zwykłe przypisanie w Pythonie po prostu zmienia odniesienie w lokalnym lub globalnym słowniku zmiennych zamiast modyfikować istniejącą zmienną w miejscu. Oznacza to, że zwykłe przypisanie do x nie spowoduje umieszczenia wartości w elemencie tablicy, ale raczej przełączy x z odniesienia do elementu tablicy na odniesienie do przypisanej wartości. Aby faktycznie zmodyfikować element tablicy, x powinno być indeksowane za pomocą wielokropka.

Ta sekcja zawiera przykładowy kod.

Więc, moim zdaniem, x[...] = ...modyfikacje xw miejscu; x = ...złamałby link do nditerzmiennej i nie zmieniłby jej. To tak, x[:] = ...ale działa z tablicami o dowolnym wymiarze (w tym 0d). W tym kontekście xto nie tylko liczba, to tablica.

Być może najbliższą rzeczą do tej nditeriteracji, bez której nditerjest:

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]])

Zauważ, że musiałem a[i]bezpośrednio indeksować i modyfikować . Nie mogłem użyć x = 2*x,. W tej iteracji xjest skalarem, a zatem nie podlega zmianom

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

Ale w tym nditerprzypadku xjest to tablica 0d i mutowalna.

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'> ()
...

A ponieważ jest 0d, x[:]nie można go użyć zamiastx[...]

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

Prostsza iteracja tablicy może również dać wgląd:

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

to iteruje w rzędach (1 wymiar) a. xjest wtedy tablicą 1d i można ją modyfikować za pomocą x[:]=...lub x[...]=....

A jeśli dodam external_loopflagę z następnej sekcji , xjest teraz tablicą 1d i x[:] =zadziała. Ale x[...] =nadal działa i jest bardziej ogólny. x[...]używane są wszystkie inne nditerprzykłady.

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,)

Porównaj tę prostą iterację wiersza (na tablicy 2d):

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

to iteruje w rzędach (1 wymiar) a. xjest wtedy tablicą 1d i można ją modyfikować za pomocą x[:] = ...lub x[...] = ....

Przeczytaj i eksperymentuj z tą nditerstroną do końca. Sam w sobie nditernie jest tak przydatny w python. Nie przyspiesza iteracji - dopóki nie przeniesiesz kodu do cython. np.ndindexjest jedną z niewielu niekompilowanych numpyfunkcji, które używają nditer.

hpaulj
źródło
Zauważ, że rzeczy takie jak x [1,:, ...] są również dozwoloną składnią. Pozostawiony do wykorzystania w przyszłości.
borgr