Як застосувати numpy.linalg.norm до кожного рядка матриці?


82

У мене є 2D-матриця, і я хочу взяти норму кожного рядка. Але коли я використовую numpy.linalg.norm(X)безпосередньо, це приймає норму всієї матриці.

Я можу взяти норму кожного рядка, використовуючи цикл for, а потім приймаючи норму кожного X[i], але це займає величезний час, оскільки у мене є 30 тис. Рядків.

Будь-які пропозиції щодо швидшого пошуку шляху? Або можна застосувати np.linalg.normдо кожного рядка матриці?

Відповіді:


87

Зауважте, що, як показує perimosocordiae , станом на NumPy версії 1.9, np.linalg.norm(x, axis=1)це найшвидший спосіб обчислення норми L2.


Якщо ви обчислюєте норму L2, ви можете обчислити її безпосередньо (використовуючи axis=-1аргумент для підсумовування по рядках):

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

Lp-норми, звичайно, можна обчислити подібним чином.

Це значно швидше, ніж np.apply_along_axis, хоча, можливо, і не настільки зручно:

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

Інші ordформи normможна також обчислити безпосередньо (з подібними прискореннями):

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

8
Чому ви робите np.abs (x), якщо все-таки виставите квадрат x?
Патрік

10
@Patrick: Якщо dtype xскладний, то це має значення. Наприклад, якщо x = np.array([(1+1j,2+1j)])тоді np.sum(np.abs(x)**2,axis=-1)**(1./2)є array([ 2.64575131]), тоді як np.sum(x**2,axis=-1)**(1./2)є array([ 2.20320266+1.36165413j]).
unutbu

3
@perimosocordiae опублікував оновлення, яке numpy.linalg.normз його новими axis аргументами на сьогоднішній день є найшвидшим підходом.
Йоанніс Філіппідіс,

Як зробити те саме, якщо я хочу застосувати норму по стовпцях до матриці?
Gunjan naik

@ User3515225: np.linalg.norm(x, axis=0). axisВідноситься до осі будучи підсумовування. Для 2D-масиву 0-вісь відноситься до рядків, тому axis=0викликає normпідсумовування рядків для кожного фіксованого стовпця.
unutbu

54

Відновлення старого питання через оновлення numpy. Станом на випуск 1.9, numpy.linalg.normтепер приймає axisаргумент. [ код , документація ]

Це новий найшвидший метод у місті:

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

І щоб довести, що він обчислює те саме:

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

16

Значно швидше, ніж прийнята відповідь, використовується einsum NumPy ,

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

Зверніть увагу на шкалу журналу:

введіть тут опис зображення


Код для відтворення сюжету:

import numpy
import perfplot


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


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


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


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


perfplot.show(
    setup=lambda n: numpy.random.rand(n, 3),
    kernels=[sum_sqrt, apply_norm_along_axis, norm_axis, einsum_sqrt],
    n_range=[2 ** k for k in range(20)],
    xlabel="len(a)",
)

2
Потрібно пояснити, що це / робить?
jhin

6

Спробуйте наступне:

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

де aваш 2D-масив.

Вище обчислюється норма L2. Для іншої норми ви можете використовувати щось на зразок:

In [22]: numpy.apply_along_axis(lambda row:numpy.linalg.norm(row,ord=1), 1, a)
Out[22]: array([9, 2, 9])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.