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 ?

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

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

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)'
    )

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])
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.