4

I have a three dimensional array :

 y = np.random.randint(1,5 ,(50,50,3))

I want to compute the max and min across the third axis (3 elements), and then divide by the remaining number/element.

So something like this:

x = (np.max(y, axis =2) - 2*np.min(y, axis =2))/the third number

I don't know how to get the third number. Something to beware of is, the third number has the possibility of being equal to the min or max value:

e.g. (5,5,1)

Moondra
  • 4,399
  • 9
  • 46
  • 104

3 Answers3

5

While usually sorting is overkill when you only need a max and min, in this case I think it's the simplest. It directly puts the numbers we want in places it's easy to access, without any complex arithmetic.

y = np.random.randint(1, 5, (50, 50,3))
y2 = y.copy()
y2.sort(axis=2)
sout = (y2[...,2] - 2 * y2[...,0]) / y2[...,1]

which gives me

In [68]: (sout == divakar_out).all()
Out[68]: True

which is usually a good sign. ;-)

DSM
  • 342,061
  • 65
  • 592
  • 494
5

Another alternative is to use np.median

(y.max(2) - 2 * y.min(2)) / np.median(y, 2)
piRSquared
  • 285,575
  • 57
  • 475
  • 624
  • Okay, I feel bad I didn't think of this one, which is my favourite of all the solutions posted. I think I have a mental block against medians because of the situations in which it can return a value not in the sequence, but that can't apply here.. – DSM Nov 11 '17 at 12:49
  • I only thought of it after I was trying to give the middle sorted layer a name. When I decided on `med_` then it became obvious. However, your solution is much quicker as it parses all three components with one very short sort. – piRSquared Nov 11 '17 at 14:26
4

Approach #1

The trick to finding the third one is subtracting from 3 those max and min indices. The borderline case would be when max and min indices are the same, i.e. all three elements along the last axis are the same, for which the third element index would also be the same.

Thus, we would have one solution like this -

max_idx = y.argmax(2)
min_idx = y.argmin(2)

rem_idx = np.where(max_idx == min_idx, max_idx, 3 - max_idx - min_idx)
out = (y[all_idx(max_idx, 2)] -2*y[all_idx(min_idx, 2)])/y[all_idx(rem_idx, 2)]

Helper function for indexing into y with the indices -

# https://stackoverflow.com/a/46103129/ @Divakar
def all_idx(idx, axis):
    grid = np.ogrid[tuple(map(slice, idx.shape))]
    grid.insert(axis, idx)
    return tuple(grid)

Approach #2

We could get the summation along the axis and subtract min and max values to get the third element and simply plug that into the formula -

maxv = np.max(y, axis =2)
minv = np.min(y, axis =2)
x = (maxv - 2*minv)/(y.sum(2) - maxv - minv)
Divakar
  • 218,885
  • 19
  • 262
  • 358