I have a 2D numpy array with elements of the type np.void that are essentially tuples. Is there an efficient way to unpack the values in these tuples to a 3rd dimension without looping through each element of the array?
For example, the 2D array looks something like:
a = np.array([[(1, 2, 3), (1, 2, 3), (1, 2, 3)],
[(1, 2, 3), (1, 2, 3), (1, 2, 3)],
[(1, 2, 3), (1, 2, 3), (1, 2, 3)]],
dtype=[('B4', '<u2'), ('B3', '<u2'), ('B2', '<u2')])
Where,
a.shape = (3,3)
a[0,0] = (1,2,3)
I'd like to unpack each element so the resulting array would be 3D and look something like this:
b.shape = (3,3,3)
b[0,0,0] = 1
b[0,0,1] = 2
b[0,0,2] = 3
in other words,
b[:,:,0] ==
array([[1, 1, 1],
[1, 1, 1],
[1, 1, 1]])
b[:,:,1] ==
array([[2, 2, 2],
[2, 2, 2],
[2, 2, 2]])
b[:,:,2] ==
array([[3, 3, 3],
[3, 3, 3],
[3, 3, 3]])
I know there's gotta be a more efficient way to do this other than looping through each element, but I'm not really familiar with dealing with np.void elements.
Thanks
To clarify, a more efficient solution meaning compared to something like
new_array = np.zeros((a.shape + (3,)))
for i in range(a.shape[0]):
for j in range(a.shape[-1]):
new_array[i, j, 0] = a[i, j][0]
new_array[i, j, 1] = a[i, j][1]
new_array[i, j, 2] = a[i, j][2]
In [601]: a = np.array([[(1, 2, 3), (1, 2, 3), (1, 2, 3)],
...: [(1, 2, 3), (1, 2, 3), (1, 2, 3)],
...: [(1, 2, 3), (1, 2, 3), (1, 2, 3)]],
...: dtype=[('B4', '<u2'), ('B3', '<u2'), ('B2', '<u2')])
In [602]: a.dtype
Out[602]: dtype([('B4', '<u2'), ('B3', '<u2'), ('B2', '<u2')])
In [603]: a.shape
Out[603]: (3, 3)
This is a structured array, with a compound dtype. The tuples display individual elements of the 2d array.
Recent numpy versions have added a function to conveniently convert structured arrays to unstructured:
In [606]: b=rf.structured_to_unstructured(a)
In [607]: b
Out[607]:
array([[[1, 2, 3],
[1, 2, 3],
[1, 2, 3]],
[[1, 2, 3],
[1, 2, 3],
[1, 2, 3]],
[[1, 2, 3],
[1, 2, 3],
[1, 2, 3]]], dtype=uint16)
In [608]: b[:,:,1]
Out[608]:
array([[2, 2, 2],
[2, 2, 2],
[2, 2, 2]], dtype=uint16)
a has 3 fields. Individual fields can be accessed by name:
In [610]: a['B4']
Out[610]:
array([[1, 1, 1],
[1, 1, 1],
[1, 1, 1]], dtype=uint16)
That means you could construct the 3d array by concatenating the 3 individual fields:
np.stack([a['B4'],a['B3'],a['B2']])
This is like your last solution, but without the i,j iteration.
The view approach in the other answer works in this case because all fields have the same dtype, <u2. That means the same underlying data can be viewed as individual <u2 elements, or a groups of 3 of these.
import numpy.lib.recfunctions as rf
The rf.structured_to_unstructured works in more general cases where view does not, such as a mix of dtypes, (e.g. floats and integers).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With