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.