237

In keeping with the "There's only one obvious way to do it", how do you get the magnitude of a vector (1D array) in Numpy?

def mag(x): 
    return math.sqrt(sum(i**2 for i in x))

The above works, but I cannot believe that I must specify such a trivial and core function myself.

Nick T
  • 25,754
  • 12
  • 83
  • 121
  • 1
    I usually use `linalg.norm` as mentioned below. But slightly simpler than your lambda thing, with no imports needed, is just `sum(x*x)**0.5` – wim Feb 07 '12 at 05:07
  • 7
    The link is dead! [Long live the link!](http://legacy.python.org/doc/essays/ppt/regrets/PythonRegrets.ppt) – daviewales Sep 27 '14 at 14:17
  • 6
    The link is PowerPoint! [Long live the PDF!](https://legacy.python.org/doc/essays/ppt/regrets/PythonRegrets.pdf) – squirl Apr 07 '18 at 13:40

8 Answers8

316

The function you're after is numpy.linalg.norm. (I reckon it should be in base numpy as a property of an array -- say x.norm() -- but oh well).

import numpy as np
x = np.array([1,2,3,4,5])
np.linalg.norm(x)

You can also feed in an optional ord for the nth order norm you want. Say you wanted the 1-norm:

np.linalg.norm(x,ord=1)

And so on.

chutsu
  • 13,612
  • 19
  • 65
  • 86
mathematical.coffee
  • 55,977
  • 11
  • 154
  • 194
  • 20
    "Should be a property of an array: x.norm()" I totally agree. Usually when working with numpy I use my own Array and Matrix subclasses that have all functions I commonly use pulled in as methods. `Matrix.randn([5,5])` – mdaoust Feb 07 '12 at 12:10
  • 3
    Also, for matrices comprised of vectors, `np.linalg.norm` now has a new `axis` argument, discussed here: http://stackoverflow.com/a/19794741/1959808 – 0 _ Nov 18 '13 at 09:12
127

If you are worried at all about speed, you should instead use:

mag = np.sqrt(x.dot(x))

Here are some benchmarks:

>>> import timeit
>>> timeit.timeit('np.linalg.norm(x)', setup='import numpy as np; x = np.arange(100)', number=1000)
0.0450878
>>> timeit.timeit('np.sqrt(x.dot(x))', setup='import numpy as np; x = np.arange(100)', number=1000)
0.0181372

EDIT: The real speed improvement comes when you have to take the norm of many vectors. Using pure numpy functions doesn't require any for loops. For example:

In [1]: import numpy as np

In [2]: a = np.arange(1200.0).reshape((-1,3))

In [3]: %timeit [np.linalg.norm(x) for x in a]
100 loops, best of 3: 4.23 ms per loop

In [4]: %timeit np.sqrt((a*a).sum(axis=1))
100000 loops, best of 3: 18.9 us per loop

In [5]: np.allclose([np.linalg.norm(x) for x in a],np.sqrt((a*a).sum(axis=1)))
Out[5]: True
user545424
  • 15,713
  • 11
  • 56
  • 70
  • 2
    I did actually use this slightly-less-explicit method after finding that `np.linalg.norm` was a bottleneck, but then I went one step further and just used `math.sqrt(x[0]**2 + x[1]**2)` which was another significant improvement. – Nick T Sep 13 '13 at 04:02
  • @NickT, see my edit for the real improvement when using pure numpy functions. – user545424 Sep 13 '13 at 17:03
  • 3
    Cool application of the dot product! – squirl Apr 07 '18 at 13:46
  • 3
    `numpy.linalg.norm` contains safeguards against overflow that this implementation skips. For instance, try computing the norm of `[1e200, 1e200]`. There is a reason if it is slower... – Federico Poloni May 09 '18 at 09:52
  • 2
    @FedericoPoloni, at least with numpy version 1.13.3 I get `inf` when computing `np.linalg.norm([1e200,1e200])`. – user545424 Mar 26 '19 at 18:18
  • 1
    @user545424 Interesting, I checked and I confirm your findings with my current version. Must be a regression? With Matlab, I get correctly `norm([1e200, 1e200]) == 1.4142e+200`. – Federico Poloni Mar 26 '19 at 21:24
18

Yet another alternative is to use the einsum function in numpy for either arrays:

In [1]: import numpy as np

In [2]: a = np.arange(1200.0).reshape((-1,3))

In [3]: %timeit [np.linalg.norm(x) for x in a]
100 loops, best of 3: 3.86 ms per loop

In [4]: %timeit np.sqrt((a*a).sum(axis=1))
100000 loops, best of 3: 15.6 µs per loop

In [5]: %timeit np.sqrt(np.einsum('ij,ij->i',a,a))
100000 loops, best of 3: 8.71 µs per loop

or vectors:

In [5]: a = np.arange(100000)

In [6]: %timeit np.sqrt(a.dot(a))
10000 loops, best of 3: 80.8 µs per loop

In [7]: %timeit np.sqrt(np.einsum('i,i', a, a))
10000 loops, best of 3: 60.6 µs per loop

There does, however, seem to be some overhead associated with calling it that may make it slower with small inputs:

In [2]: a = np.arange(100)

In [3]: %timeit np.sqrt(a.dot(a))
100000 loops, best of 3: 3.73 µs per loop

In [4]: %timeit np.sqrt(np.einsum('i,i', a, a))
100000 loops, best of 3: 4.68 µs per loop
n8yoder
  • 9,530
  • 2
  • 16
  • 19
  • `numpy.linalg.norm` contains safeguards against overflow that this implementation skips. For instance, try computing the norm of `[1e200, 1e200]`. There is a reason if it is slower... – Federico Poloni May 09 '18 at 09:53
9

Fastest way I found is via inner1d. Here's how it compares to other numpy methods:

import numpy as np
from numpy.core.umath_tests import inner1d

V = np.random.random_sample((10**6,3,)) # 1 million vectors
A = np.sqrt(np.einsum('...i,...i', V, V))
B = np.linalg.norm(V,axis=1)   
C = np.sqrt((V ** 2).sum(-1))
D = np.sqrt((V*V).sum(axis=1))
E = np.sqrt(inner1d(V,V))

print [np.allclose(E,x) for x in [A,B,C,D]] # [True, True, True, True]

import cProfile
cProfile.run("np.sqrt(np.einsum('...i,...i', V, V))") # 3 function calls in 0.013 seconds
cProfile.run('np.linalg.norm(V,axis=1)')              # 9 function calls in 0.029 seconds
cProfile.run('np.sqrt((V ** 2).sum(-1))')             # 5 function calls in 0.028 seconds
cProfile.run('np.sqrt((V*V).sum(axis=1))')            # 5 function calls in 0.027 seconds
cProfile.run('np.sqrt(inner1d(V,V))')                 # 2 function calls in 0.009 seconds

inner1d is ~3x faster than linalg.norm and a hair faster than einsum

Fnord
  • 5,365
  • 4
  • 31
  • 48
  • Actually from what you write above, `linalg.norm` is the fastest since it does 9 calls in 29ms so 1 call in 3.222ms vs. 1 call in 4.5ms for `inner1d`. – patapouf_ai Jun 01 '16 at 23:25
  • 1
    @bisounours_tronconneuse the timing for total execution time. If you run the code above you'll get a breakdown of timing per function call. If you still have doubts, change the vector count to something very very large, like `((10**8,3,))` and then manually run `np.linalg.norm(V,axis=1)` followed by `np.sqrt(inner1d(V,V))`, you'll notice `linalg.norm` will lag compared to inner1d – Fnord Jun 02 '16 at 01:00
  • 1
    Ok. Thank you for the clarification. – patapouf_ai Jun 02 '16 at 06:38
  • 1
    `numpy.linalg.norm` contains safeguards against overflow that this implementation skips. For instance, try computing the norm of `[1e200, 1e200]`. There is a reason if it is slower... – Federico Poloni May 09 '18 at 09:53
  • Is `numpy.core.umath_tests.inner1d` the same as `numpy.inner` for this purpose? Maybe the latter is new? or slower? – LarsH Sep 30 '21 at 12:24
  • 1
    @LarsH unfortunately it is not. And it looks like `numpy.core.umath_tests` is getting deprecated. If inner1d's existing functionality goes out into the sunset (which would be a shame) then your best bet (for speed) will be to write a parallelized function using `numba`. – Fnord Oct 03 '21 at 23:04
3

use the function norm in scipy.linalg (or numpy.linalg)

>>> from scipy import linalg as LA
>>> a = 10*NP.random.randn(6)
>>> a
  array([  9.62141594,   1.29279592,   4.80091404,  -2.93714318,
          17.06608678, -11.34617065])
>>> LA.norm(a)
    23.36461979210312

>>> # compare with OP's function:
>>> import math
>>> mag = lambda x : math.sqrt(sum(i**2 for i in x))
>>> mag(a)
     23.36461979210312
doug
  • 69,080
  • 24
  • 165
  • 199
2

You can do this concisely using the toolbelt vg. It's a light layer on top of numpy and it supports single values and stacked vectors.

import numpy as np
import vg

x = np.array([1, 2, 3, 4, 5])
mag1 = np.linalg.norm(x)
mag2 = vg.magnitude(x)
print mag1 == mag2
# True

I created the library at my last startup, where it was motivated by uses like this: simple ideas which are far too verbose in NumPy.

paulmelnikow
  • 16,895
  • 8
  • 63
  • 114
1

take square of each index,then sum, then take sqrt.

import numpy as np  

def magnitude(v):
    return np.sqrt(np.sum(np.square(v))) 
print(magnitude([3,4]))
0

Given an example of a single 5D vector:

x = np.array([1,-2,3,-4,5])

Typically you code this:

from scipy import linalg 
mag = linalg.norm(x)

For different types of input (matrices or a stack (batch) of 5D vectors) check the reference documentation which describes the API consistently. https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html

OR

If the virtual environment is fresh and scipy is missing just type

mag = np.sqrt(x.dot(x))