21

What i need is a way to get "fancy indexing" (y = x[[0, 5, 21]]) to return a view instead of a copy.

I have an array, but i want to be able to work with a subset of this array (specified by a list of indices) in such a way that the changes in this subset is also put into the right places in the large array. If i just want to do something with the first 10 elements, i can just use regular slicing y = x[0:10]. That works great, because regular slicing returns a view. The problem is if i don't want 0:10, but an arbitrary set of indices.

Is there a way to do this?

Eskil
  • 3,385
  • 5
  • 28
  • 32

4 Answers4

16

I don't think there is a way around this. My understanding is that 'fancy indexing' will always return a copy. The best solution I can think of is to manipulate y and then use the same fancy indexes to change the values of x afterwards:

ii = [0, 5, 21]
y = x[ii]
<manipulate y>
x[ii] = y
JoshAdel
  • 66,734
  • 27
  • 141
  • 140
  • 4
    You beat me to it. The only thing I was going to add was this: http://projects.scipy.org/numpy/ticket/224 Indicated that this is not likely to change. – Paul Feb 26 '11 at 16:20
  • 1
    Oh if this kind of assignment to indexed array (x[ii] = y) works then that does what i need i guess. – Eskil Feb 26 '11 at 16:22
  • @Eskil That is because `x[ii] = y` invokes `x.__setitem__(ii, y)` so there is no copying involved although you used an index array. It is true that `x[ii]` returns a copy but this invokes `x.__getitem__(ii)` and is a different story. – a_guest Jun 29 '17 at 18:22
2

Here is a possible way to simulate having a view (some syntactic sugar), avoiding the explicit copy statements at the end by using a 'fancy view context'. You'll need to take care that your code doesn't modify the index array within the context

import contextlib

@contextlib.contextmanager
def fancy_index_view(arr, inds):
    # create copy from fancy inds
    arr_copy = arr[inds]

    # yield 'view' (copy)
    yield arr_copy

    # after context, save modified data
    arr[inds] = arr_copy

now, the snippet

import numpy as np
foo = np.random.random((22,2))
row_inds = [0,5,21]
barview = foo[row_inds]
barview[::] = 1
foo[row_inds] = barview

could be replaced by

import numpy as np
foo = np.random.random((22,2))
row_inds = [0,5,21]
with fancy_index_view(foo, row_inds) as barview:
    barview[::] = 1
eqzx
  • 5,323
  • 4
  • 37
  • 54
2

You can just do:

y = x[[0,1,4]]
func(y)
x[[0,1,4]] = y

I don't think you can get views with fancy indexing. You might not want to, as I think fancy indexing is pretty slow, it should be faster to just copy the data once.

wisty
  • 6,981
  • 1
  • 30
  • 29
0

You could theoretically create an object that performs the role of a 'fancy view' into another array, and I can think of plenty of use cases for it. The problem is, that such an object would not be compatible with the standard numpy machinery. All compiled numpy C code relies on data being accessible as an inner product of strides and indices. Generalizing this code to fundamentally different data layout formats would be a gargantuan undertaking. For a project that is trying to take on a challenge along these lines, check out continuum's Blaze.

Eelco Hoogendoorn
  • 10,459
  • 1
  • 44
  • 42