Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.5k views
in Technique[技术] by (71.8m points)

numpy - What does "three dots" in Python mean when indexing what looks like a number?

What is the meaning of x[...] below?

a = np.arange(6).reshape(2,3)
for x in np.nditer(a, op_flags=['readwrite']):
    x[...] = 2 * x
Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

While the proposed duplicate What does the Python Ellipsis object do? answers the question in a general python context, its use in an nditer loop requires, I think, added information.

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

Regular assignment in Python simply changes a reference in the local or global variable dictionary instead of modifying an existing variable in place. This means that simply assigning to x will not place the value into the element of the array, but rather switch x from being an array element reference to being a reference to the value you assigned. To actually modify the element of the array, x should be indexed with the ellipsis.

That section includes your code example.

So in my words, the x[...] = ... modifies x in-place; x = ... would have broken the link to the nditer variable, and not changed it. It's like x[:] = ... but works with arrays of any dimension (including 0d). In this context x isn't just a number, it's an array.

Perhaps the closest thing to this nditer iteration, without nditer is:

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

Notice that I had to index and modify a[i] directly. I could not have used, x = 2*x. In this iteration x is a scalar, and thus not mutable

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

But in the nditer case x is a 0d array, and 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'> ()
...

And because it is 0d, x[:] cannot be used instead of x[...]

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

A simpler array iteration might also give insight:

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

this iterates on the rows (1st dim) of a. x is then a 1d array, and can be modified with either x[:]=... or x[...]=....

And if I add the external_loop flag from the next section, x is now a 1d array, and x[:] = would work. But x[...] = still works and is more general. x[...] is used all the other nditer examples.

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

Compare this simple row iteration (on a 2d array):

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

this iterates on the rows (1st dim) of a. x is then a 1d array, and can be modified with either x[:] = ... or x[...] = ....

Read and experiment with this nditer page all the way through to the end. By itself, nditer is not that useful in python. It does not speed up iteration - not until you port your code to cython.np.ndindex is one of the few non-compiled numpy functions that uses nditer.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...