[Numpy-discussion] ndarray shape permutation

2022-05-17 Thread Paul Korir
Hellos,
I would like to propose `numpy.ndarray.permute_shape()` method 
to predictably permute the shape of an ndarray. In my opinion, the current 
alternatives (`swapaxes`, `transform`, `moveaxes` and friends) are 
counterintuitive and rely on referring to the axis indices. It would be 
abundantly helpful to have something like reshape but which moves the data 
around (unlike reshape, which doesn't). 
Scenario: structural biology uses MRC files, which define a number of fields 
that describe a 3D volume. There is a field which describes the dimensions of 
the 3D image and another which associates each image axis with a physical axis. 
There are six such assignments between the image shape and the axes assignments 
(if we keep the shape fixed we can assign XYZ, XZY, YXZ, YZX, ZXY, ZYX to the 
columns, rows and stacks) and working out how to correctly transform the data 
is generally non-trivial. I've written a package 
(https://github.com/emdb-empiar/maptools) which does this but I use swapaxes to 
reorient the 3D image. It would be so much easier to use 
`numpy.ndarray.permute_shape()` instead.
Any thoughts? Also, any helpful hints on how to get started with such a 
contribution would be helpful.
Paul
___
NumPy-Discussion mailing list -- numpy-discussion@python.org
To unsubscribe send an email to numpy-discussion-le...@python.org
https://mail.python.org/mailman3/lists/numpy-discussion.python.org/
Member address: arch...@mail-archive.com


[Numpy-discussion] Re: ndarray shape permutation

2022-05-17 Thread Paul Korir
Thanks for your replies. 

In retrospect, I realise that using the shape will not be helpful for a cubic 
array i.e. the permutations of (10, 10, 10) are all (10, 10, 10)! However, the 
problem remains. Let me try to explain.

Short version
The problem boils down to the meaning of axis indices as arguments to swapaxes 
and transpose (and any related functions). Swapping axes or transposing an 
array gives new meanings to the indices. For example, suppose I have a volume 
of shape C, R, S. Then 0 will refer to C, 1 will refer to R and 2 will refer to 
S. After I transpose it, say using (1, 2, 0) so that the shape becomes R, S, C 
then now 0 will refer to R, 1 will refer to S and 2 will refer to C. I can no 
longer reverse the transposition or transpose it predictably to achieve a 
certain shape, which is an important operation in some applications where the 
meaning of the axes is significant.

Long version
Suppose I have a volume of shape (C, R, S) and I have a corresponding 
assignment of physical axes so that C=X, R=Y and S=Z. This is equivalent to 
placing the volume with C along the X axis, R along Y axis and S along the Z 
axis. Now, suppose I would like to permute the axes by only making reference to 
the axis names: what is the shape corresponding to the orientation (Z, Y, X)? 

This is a simple example because we only swap two axes and the resulting shape 
is the same as performing the same swap in the shape: (S, R, C). If we knew the 
indices of the axis names then we can infer these and pass them to swapaxes or 
transpose:

vol = numpy.random.rand(C, R, S) # of shape (C, R, S) -> (X, Y, Z)
# now (Z, Y, X)
new_vol = numpy.swapaxes(vol, 0, 2)
new_vol.shape # (S, R, C)

The same applies to a double swap e.g. (Y, Z, X), though it is less 
straightforward using swapaxes. swapaxes only takes two indices (obviously) so 
we would need to call it twice reflecting the two swaps required. So we have to 
somehow figure which axes to swap successively: ((0, 2) then (0, 1)). We can do 
this in one step with numpy.transpose simply using indices (1, 2, 0). 

However, (and this is the big 'however'), how would we reverse this? The array 
has no memory of the original axes and 0, 1, and 2 now refer to the current 
axes. This is where the axes names (e.g. X, Y and Z) would come in handy. 

Axis names will allow permutations to happen predictably since the array will 
'remember' what the original references were. 

Here is what I propose: an API to numpy.ndarray with some identity e.g. 

vol = numpy.random.rand(C, R, S)
vol.shape # C, R, S
vol.axes = ('X', 'Y', 'Z') # C=X, R=Y, S=Z
new_vol = vol.permute_axes(('Z', 'Y', 'X')) 
# either
new_vol.axes # ('X', 'Y', 'Z') # preserve orientation but change shape
new_vol.shape # S, R, C
# or
new_vol.axes # ('Z', 'Y', 'X') # preserve shape but change orientation
new_vol.shape # C, R, S
# we can now reverse the permutation
old_vol = new_vol.permute_axes(('X', 'Y', 'Z'))
numpy.array_equal(vol, old_vol) # True

I've checked the numpy API documentation and there is no attribute .axes 
present so this is the best candidate. Additionally, this will require the 
.permute_axes() method/function. 

Thanks for your consideration.

Paul
___
NumPy-Discussion mailing list -- numpy-discussion@python.org
To unsubscribe send an email to numpy-discussion-le...@python.org
https://mail.python.org/mailman3/lists/numpy-discussion.python.org/
Member address: arch...@mail-archive.com


[Numpy-discussion] Re: ndarray shape permutation

2022-05-18 Thread Paul Korir
Thank you very much for your replies. xarray is perfect though I'm not sure 
what overhead I'm paying to get the following:

import numpy as np
import xarray as xr
data = xr.DataArray(np.random.randn(2, 3, 4), dims=("x", "y", "z"))
data.transpose('z', 'y', 'x').shape
data.transpose('y', 'z', 'x').shape

Just what I was hoping for.

Paul
___
NumPy-Discussion mailing list -- numpy-discussion@python.org
To unsubscribe send an email to numpy-discussion-le...@python.org
https://mail.python.org/mailman3/lists/numpy-discussion.python.org/
Member address: arch...@mail-archive.com