Wie wende ich numpy.linalg.norm auf jede Zeile einer Matrix an?

79

Ich habe eine 2D-Matrix und möchte die Norm jeder Zeile übernehmen. Aber wenn ich numpy.linalg.norm(X)direkt benutze , nimmt es die Norm der gesamten Matrix.

Ich kann die Norm jeder Zeile nehmen, indem ich eine for-Schleife verwende und dann die Norm jeder Zeile nehme X[i], aber es dauert sehr lange, da ich 30.000 Zeilen habe.

Irgendwelche Vorschläge, um einen schnelleren Weg zu finden? Oder ist es möglich, np.linalg.normauf jede Zeile einer Matrix anzuwenden ?

nbro
quelle

Antworten:

85

Beachten Sie, dass, wie Perimosocordiae zeigt , ab NumPy Version 1.9 np.linalg.norm(x, axis=1)der schnellste Weg ist, die L2-Norm zu berechnen.


Wenn Sie eine L2-Norm berechnen, können Sie sie direkt berechnen (indem Sie das axis=-1Argument verwenden, um entlang der Zeilen zu summieren):

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

Lp-Normen können natürlich ähnlich berechnet werden.

Es ist erheblich schneller als np.apply_along_axis, wenn auch vielleicht nicht so bequem:

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

Andere ordFormen von normkönnen auch direkt berechnet werden (mit ähnlichen Beschleunigungen):

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
unutbu
quelle
8
Warum machst du np.abs (x), wenn du x trotzdem quadrierst?
Patrick
10
@Patrick: Wenn der dtype von xkomplex ist, macht es einen Unterschied. Zum Beispiel, wenn x = np.array([(1+1j,2+1j)])dann np.sum(np.abs(x)**2,axis=-1)**(1./2)ist array([ 2.64575131]), während np.sum(x**2,axis=-1)**(1./2)ist array([ 2.20320266+1.36165413j]).
Unutbu
3
@perimosocordiae hat ein Update veröffentlicht, das numpy.linalg.normmit seinem neuen axis Argument derzeit der schnellste Ansatz ist.
Ioannis Filippidis
Wie mache ich dasselbe? Wenn ich die Norm spaltenweise auf eine Matrix anwenden möchte?
Gunjan Naik
@ user3515225 : np.linalg.norm(x, axis=0). Das axisbezieht sich auf die Achse, über die summiert wird. Für eine 2D - Anordnung bezieht sich die 0-Achse zu den Reihen, so axis=0bewirkt normdie Zeilen für jede feste Spalte zu summieren unten.
Unutbu
51

Wiederbelebung einer alten Frage aufgrund eines Numpy-Updates. Ab Version 1.9 wird numpy.linalg.normjetzt ein axisArgument akzeptiert . [ Code , Dokumentation ]

Dies ist die neue schnellste Methode in der Stadt:

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

Und um zu beweisen, dass es dasselbe berechnet:

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

Viel schneller als die akzeptierte Antwort ist

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

Beachten Sie die Protokollskala:

Geben Sie hier die Bildbeschreibung ein


Code zur Reproduktion der Handlung:

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)],
    logx=True,
    logy=True,
    xlabel='len(a)'
    )
Nico Schlömer
quelle
2
Möchtest du erklären, was das ist / tut?
Jhin
6

Versuche Folgendes:

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

Wo aist dein 2D-Array?

Das Obige berechnet die L2-Norm. Für eine andere Norm könnten Sie Folgendes verwenden:

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