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.