Reputation: 1179
Let's say I have a numpy.ndarray
with shape (2,3,2)
as below,
arr = np.array([[[1,3], [2,5], [1,2]],[[3,3], [6,5], [5,2]]])
I want to reshape it in such a way that:
arr.shape == (2,3)
arr == [[(1,3), (2,5), (1,2)],[(3,3), (6,5), (5,2)]]
and
each value of arr
is a size 2 tuple
The reason I want to do this is that I want to take the minimum along axis 0 of the 3dimensional array, but I want to preserve the value that the min of the rows in paired with.
arr = np.array(
[[[1, 4],
[2, 1],
[5, 2]],
[[3, 3],
[6, 5],
[1, 7]]])
print(np.min(arr, axis=0))
>>> [[1,3],
[2,1],
[1,2]]
>>>Should be
[[1,4],
[2,1],
[1,7]]
If the array contained tuples, it would be 2 dimensional, and the comparison operator for minimize would still function correctly, so I would get the correct result. But I haven't found any way to do this besides iterating over the arrays, which is inefficient and obvious in implementation.
Is it possible to perform this conversion efficiently in numpy?
Upvotes: 1
Views: 510
Reputation: 97571
Don't use tuples at all - just view it as a structured array, which supports the lexical comparison you're after:
a = np.array([[[1,3], [2,5], [1,2]],[[3,3], [6,5], [5,2]]])
a_pairs = a.view([('f0', a.dtype), ('f1', a.dtype)]).squeeze(axis=-1)
min_pair = np.partition(a_pairs, 0, axis=0)[0] # min doesn't work on structured types :(
array([(1, 4), (2, 1), (1, 7)],
dtype=[('f0', '<i4'), ('f1', '<i4')])
Upvotes: 3
Reputation: 221564
Here's one way -
# Get each row being fused with scaling based on scale being decided
# based off the max values from the second col. Get argmin indices.
idx = (arr[...,1] + arr[...,0]*(arr[...,1].max()+1)).argmin(0)
# Finally use advanced-indexing to get those rows off array
out = arr[idx, np.arange(arr.shape[1])]
Sample run -
In [692]: arr
Out[692]:
array([[[3, 4],
[2, 1],
[5, 2]],
[[3, 3],
[6, 5],
[5, 1]]])
In [693]: out
Out[693]:
array([[3, 3],
[2, 1],
[5, 1]])
Upvotes: 1
Reputation: 249153
First, let's find out which pairs to take:
first_eq = arr[0,:,0] == arr[1,:,0]
which_compare = np.where(first_eq, 1, 0)[0]
winner = arr[:,:,which_compare].argmin(axis=0)
Here, first_eq
is True where the first elements match, so we would need to compare the second elements. It's [False, False, False]
in your example. which_compare
then is [0, 0, 0]
(because the first element of each pair is what we will compare). Finally, winner
tells us which of the two pairs to choose along the second axis. It is [0, 0, 1]
.
The last step is to extract the winners:
arr[winner, np.arange(arr.shape[1])]
That is, take the winner (0 or 1) at each point along the second axis.
Upvotes: 1