qed
qed

Reputation: 23134

How to get the highest element in absolute value in a numpy matrix?

Here is what I am currently doing, it works but it's a little cumbersome:

x = np.matrix([[1, 1], [2, -3]])
xmax = x.flat[abs(x).argmax()]

Upvotes: 35

Views: 67970

Answers (11)

Dong Justin
Dong Justin

Reputation: 137

A rather standard way using np.argmax and np.take, supporting an arbitrary axis argument

def absmax(a, axis=None):
  max_abs_indices = np.argmax(np.abs(a), axis=axis, keepdims=True)
  if axis is None:
    return np.take(a, max_abs_indices).squeeze()
  return np.take_along_axis(a, max_abs_indices, axis=axis).squeeze()

A similar implementation for finding the minimum in the original array in terms of the absolute value.

def absmin(a, axis=None):
  min_abs_indices = np.argmin(np.abs(a), axis=axis, keepdims=True)
  if axis is None:
    return np.take(a, min_abs_indices).squeeze()
  return np.take_along_axis(a, min_abs_indices, axis=axis).squeeze()

Upvotes: 0

JoeCondron
JoeCondron

Reputation: 8906

The value you're looking for has to be either x.max() or x.min() so you could do

max(x.min(), x.max(), key=abs)

which is similar to aestrivex's solution but perhaps more readable? Note this will return the minimum in the case where x.min() and x.max() have the same absolute value e.g. -5 and 5. If you have a preference just order the inputs to max accordingly.

EDIT: Just to clarify, the idea is to leverage the numpy methods to reduce down to just two values and then use the key feature of the builtin max to arbitrate, since key is not supported by the numpy methods.

Upvotes: 49

LegNaiB
LegNaiB

Reputation: 131

If you want to get this value for a 2D numpy array, you can also do the following:

x[np.arange(len(x)), np.abs(x).argmax(axis=1)]

For example if your x looks like this:

x = np.array([[  -1, -10],
              [ -40,  -5],
              [ -10,  15],
              [  11,  35]])

and you want to get the maximum value for each row disregarding the sign, the result would be np.array([-10, -40, 15, 35]).

Upvotes: 0

Léonard
Léonard

Reputation: 2630

The most compact way would probably be:

x_max = x.flat[np.abs(x).argmax()]  

By default, the .argmax() method operates directly on the flattened array (taken from the NumPy documentation). So the operation looks for the maximum absolute value of the n-dimensional array np.abs(x).

Upvotes: 10

Thomas Pfeifer
Thomas Pfeifer

Reputation: 31

pardon the necro, but i can't seem to comment on JoeCondron's answer.

I like:

max(x.min(), x.max(), key=abs) 

but i believe it can be simplified further:

max(x, key=abs)

seems to work for me (or for non-1D) :

max(x.flat, key=abs)

Upvotes: 2

Demetry Pascal
Demetry Pascal

Reputation: 554

I use it

dt = np.random.rand(50000,500)-0.5
# ur
xmax = dt.flat[abs(dt).argmax()] #230 ms

# new
newdt = np.array([dt.min(),dt.max()]) # 56ms
xmax = newdt.flat[abs(newdt).argmax()] # 4ms

it is almost 4 times faster (60 ms against 230)!!

Upvotes: 0

kxr
kxr

Reputation: 5539

This one computes the absolute max'es fast - respecting an arbitrary axis argument in the same way as np.max and np.argmax themselves do.

def absmaxND(a, axis=None):
    amax = a.max(axis)
    amin = a.min(axis)
    return np.where(-amin > amax, amin, amax)

For long arrays its about 2.5x faster than a.flat[abs(a).argmax()] even for the simple case axis=None - because it doesn't render abs() of the original big array.

Upvotes: 10

Martin Spacek
Martin Spacek

Reputation: 3127

I was looking for a way to get the signed values of the maximum absolute values of an N-dimensional array along a specified axis, which none of these answers handle. So, I put together a function to do it. No promises, but it works as far as I've tested it:

def maxabs(a, axis=None):
    """Return slice of a, keeping only those values that are furthest away
    from 0 along axis"""
    maxa = a.max(axis=axis)
    mina = a.min(axis=axis)
    p = abs(maxa) > abs(mina) # bool, or indices where +ve values win
    n = abs(mina) > abs(maxa) # bool, or indices where -ve values win
    if axis == None:
        if p: return maxa
        else: return mina
    shape = list(a.shape)
    shape.pop(axis)
    out = np.zeros(shape, dtype=a.dtype)
    out[p] = maxa[p]
    out[n] = mina[n]
    return out

Upvotes: 6

aestrivex
aestrivex

Reputation: 5280

I think this is a pretty straightforward way, which might be slightly better if code readability is your primary concern. But really, your way is just as elegant.

np.min(x) if np.max(abs(x)) == abs(np.min(x)) else np.max(x)

Upvotes: -1

Daniel
Daniel

Reputation: 19547

The only thing that I could think of, which looks even worse, is:

xmax=x[np.unravel_index(abs(x).argmax(), x.shape)]

Upvotes: 1

A.Wan
A.Wan

Reputation: 2058

EDIT: My answer is off-topic, sorry. As Ophion pointed out this would return the index, not the value - you have to use flat with my "xmax" (which is really "xmaxInd") to get the proper value. Ergo I think your solution is best.


After experimenting a bit I realized you can just do this:

x = np.matrix([[1,1], [2,-3]])
absX = abs(x)
xmax = argmax(absX)

It seems that numpy allows you to take the abs as well as the argmax of a matrix. How convenient!

timeit checks:

def meth1():
    x = np.matrix([[1,1],[2,-3]])
    xmax = x.flat[abs(x).argmax()]

def meth2():
    x = np.matrix([[1,1],[2,-3]])
    xmax = argmax(abs(x))

t1 = timeit.Timer("meth1()","from __main__ import meth1")
t2 = timeit.Timer("meth2()","from __main__ import meth2")

mean(t1.repeat(1,100000)) gives Out[99]: 7.854323148727417 mean(t2.repeat(1,100000)) gives Out[98]: 7.7788529396057129

So meth2() is slightly faster. Probably because it doesn't involve calling flat.

Upvotes: 3

Related Questions