How to apply numpy.linalg.norm to each row of a matrix?

I have a 2D matrix and I want to take norm of each row. But when I use numpy.linalg.norm(X) directly, it takes the norm of the whole matrix.

I can take norm of each row by using a for loop and then taking norm of each X[i], but it takes a huge time since I have 30k rows.

Any suggestions to find a quicker way? Or is it possible to apply np.linalg.norm to each row of a matrix?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

For numpy 1.9+

Note that, as perimosocordiae shows, as of NumPy version 1.9, np.linalg.norm(x, axis=1) is the fastest way to compute the L2-norm.

For numpy < 1.9

If you are computing an L2-norm, you could compute it directly (using the axis=-1 argument to sum along rows):

np.sum(np.abs(x)**2,axis=-1)**(1./2)

Lp-norms can be computed similarly of course.

It is considerably faster than np.apply_along_axis, though perhaps not as convenient:

In [48]: %timeit np.apply_along_axis(np.linalg.norm, 1, x)
1000 loops, best of 3: 208 us per loop

In [49]: %timeit np.sum(np.abs(x)**2,axis=-1)**(1./2)
100000 loops, best of 3: 18.3 us per loop

Other ord forms of norm can be computed directly too (with similar speedups):

In [55]: %timeit np.apply_along_axis(lambda row:np.linalg.norm(row,ord=1), 1, x)
1000 loops, best of 3: 203 us per loop

In [54]: %timeit np.sum(abs(x), axis=-1)
100000 loops, best of 3: 10.9 us per loop

Method 2

Resurrecting an old question due to a numpy update. As of the 1.9 release, numpy.linalg.norm now accepts an axis argument. [code, documentation]

This is the new fastest method in town:

In [10]: x = np.random.random((500,500))

In [11]: %timeit np.apply_along_axis(np.linalg.norm, 1, x)
10 loops, best of 3: 21 ms per loop

In [12]: %timeit np.sum(np.abs(x)**2,axis=-1)**(1./2)
100 loops, best of 3: 2.6 ms per loop

In [13]: %timeit np.linalg.norm(x, axis=1)
1000 loops, best of 3: 1.4 ms per loop

And to prove it’s calculating the same thing:

In [14]: np.allclose(np.linalg.norm(x, axis=1), np.sum(np.abs(x)**2,axis=-1)**(1./2))
Out[14]: True

Method 3

Much faster than the accepted answer is using NumPy’s einsum,

numpy.sqrt(numpy.einsum('ij,ij->i', a, a))

And even faster than that is arranging the data such that the norms are computed across all columns,

numpy.sqrt(numpy.einsum('ij,ij->j', aT, aT))

Note the log-scale:

How to apply numpy.linalg.norm to each row of a matrix?


Code to reproduce the plot:

import numpy as np
import perfplot

rng = np.random.default_rng(0)


def setup(n):
    x = rng.random((n, 3))
    xt = np.ascontiguousarray(x.T)
    return x, xt


def sum_sqrt(a, _):
    return np.sqrt(np.sum(np.abs(a) ** 2, axis=-1))


def apply_norm_along_axis(a, _):
    return np.apply_along_axis(np.linalg.norm, 1, a)


def norm_axis(a, _):
    return np.linalg.norm(a, axis=1)


def einsum_sqrt(a, _):
    return np.sqrt(np.einsum("ij,ij->i", a, a))


def einsum_sqrt_columns(_, aT):
    return np.sqrt(np.einsum("ij,ij->j", aT, aT))


b = perfplot.bench(
    setup=setup,
    kernels=[
        sum_sqrt,
        apply_norm_along_axis,
        norm_axis,
        einsum_sqrt,
        einsum_sqrt_columns,
    ],
    n_range=[2**k for k in range(20)],
    xlabel="len(a)",
)
b.show()
b.save("out.png")

Method 4

Try the following:

In [16]: numpy.apply_along_axis(numpy.linalg.norm, 1, a)
Out[16]: array([ 5.38516481,  1.41421356,  5.38516481])

where a is your 2D array.

The above computes the L2 norm. For a different norm, you could use something like:

In [22]: numpy.apply_along_axis(lambda row:numpy.linalg.norm(row,ord=1), 1, a)
Out[22]: array([9, 2, 9])


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x