0

This question and its answers ...

reshape((-1,)) gets a view whenever the strides of the array allow it even if that means you don't always get a contiguous array.

... raised another question: Assuming that I have am n-dimensional numpy array of any imaginable shape and memory layout, how do I get a guaranteed flattened view of it or else a guaranteed exception if it is not a view?

s-m-e
  • 3,433
  • 2
  • 34
  • 71
  • The most common case where you won't get a view is if youbdo this reshape after a transpose. Or if you are playing with the `order`. Flattening after certain slices may also force a copy. – hpaulj Mar 01 '22 at 00:10
  • @hpaulj Yeah, I know - but I really do not know of a safe way to tell if I got a view of not. – s-m-e Mar 01 '22 at 00:21
  • There's a 'shared memory' function, but for instructionzl purposes I look at the `x.__array_interface__['data']`. – hpaulj Mar 01 '22 at 00:48
  • Doesn't the `reshape` note answer your question? – hpaulj Mar 01 '22 at 00:52
  • @hpaulj The "whenever the strides [...] allow it" part does not make me feel confident. At the end of the day, I'd like to have a clean way to know/check if it's a view or not. So, no, not really :/ – s-m-e Mar 01 '22 at 01:03
  • What are you doing with arrays that makes this so important? – hpaulj Mar 01 '22 at 03:27

1 Answers1

1

I was thinking of this note. It could be cast as a function

It is not always possible to change the shape of an array without copying the data. If you want an error to be raised when the data is copied, you should assign the new shape to the shape attribute of the array:

a = np.zeros((10, 2))

# A transpose makes the array non-contiguous
b = a.T

# Taking a view makes it possible to modify the shape without modifying
# the initial object.
c = b.view()
c.shape = (20)
Traceback (most recent call last):
   ...
AttributeError: Incompatible shape for in-place modification. Use
`.reshape()` to make a copy with the desired shape.

Oops, I see that's quoted in the deleted answer.

edit

Let's explore a bit

In [17]: x = np.arange(24).reshape(2, 3, 4)
In [18]: x
Out[18]: 
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]]])
In [19]: x.ravel()
Out[19]: 
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])
In [20]: x.T
Out[20]: 
array([[[ 0, 12],
        [ 4, 16],
        [ 8, 20]],
   ...
       [[ 3, 15],
        [ 7, 19],
        [11, 23]]])

Default order 'C' ravel for the transpose produces a different sequence from the base aranage. Clearly it's a copy.

In [21]: x.T.ravel()
Out[21]: 
array([ 0, 12,  4, 16,  8, 20,  1, 13,  5, 17,  9, 21,  2, 14,  6, 18, 10,
       22,  3, 15,  7, 19, 11, 23])

But the 'F' order is the same - it's a view

In [22]: x.T.ravel(order='F')
Out[22]: 
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])

Let's try a column slice:

In [23]: x[:, 2:]
Out[23]: 
array([[[ 8,  9, 10, 11]],

       [[20, 21, 22, 23]]])
In [24]: _.strides            # same as x
Out[24]: (96, 32, 8)
In [25]: __.ravel()
Out[25]: array([ 8,  9, 10, 11, 20, 21, 22, 23])
In [26]: x.strides
Out[26]: (96, 32, 8)

The ravel is not "regular" subset of the initial arange. It is a copy.

shares_memory confirms my observations:

In [27]: np.shares_memory(x, Out[22])
Out[27]: True
In [28]: np.shares_memory(x, Out[21])
Out[28]: False
In [29]: np.shares_memory(x, Out[25])
Out[29]: False

Let's try the trick suggested in the quote:

In [30]: c = Out[23].view()
In [31]: c
Out[31]: 
array([[[ 8,  9, 10, 11]],

       [[20, 21, 22, 23]]])
In [32]: c.size
Out[32]: 8
In [34]: c.shape = (8,)
Traceback (most recent call last):
  Input In [34] in <module>
    c.shape = (8,)
AttributeError: Incompatible shape for in-place modification. Use `.reshape()` to make a copy with the desired shape.

Trying set shape as in [34] only works for compatible views. It never makes a copy.

hpaulj
  • 221,503
  • 14
  • 230
  • 353