2016-10-29 11 views
4

gegeben, zwei 3-D-Arrays von Dimensionen (2,2,2) finden:Numpy: den euklidischen Abstand zwischen zwei 3-D-Arrays

A = [[[ 0, 0], 
    [92, 92]], 

    [[ 0, 92], 
    [ 0, 92]]] 

B = [[[ 0, 0], 
    [92, 0]], 

    [[ 0, 92], 
    [92, 92]]] 

Wie Sie den euklidischen Abstand für jeden Vektor finden Sie in A und B effizient?

Ich habe for-Schleifen versucht, aber diese sind langsam, und ich arbeite mit 3-D-Arrays in der Reihenfolge (>> 2, >> 2, 2).

Schließlich möchte ich eine Matrix der Form:

C = [[d1, d2], 
    [d3, d4]] 

Edit:

ich die folgende Schleife versucht haben, aber das größte Problem mit ihm ist, dass verliert die Dimensionen ich behalten möchten. Aber die Entfernungen sind korrekt.

[numpy.sqrt((A[row, col][0] - B[row, col][0])**2 + (B[row, col][1] -A[row, col][1])**2) for row in range(2) for col in range(2)] 

Antwort

3

Denken in einer NumPy vektorisiert Weise, die elementweise Differenzierung, quadriert und Summieren entlang der letzten Achse und schließlich immer Quadratwurzel durchführen würde. Also, die Straight-Forward-Implementierung wäre -

np.sqrt(((A - B)**2).sum(-1)) 

Wir konnten die Quadrierung durchführen und mit np.einsum entlang der letzten Achse in einem Rutsch Summieren und machen es somit effizienter zu gestalten, wie so -

subs = A - B 
out = np.sqrt(np.einsum('ijk,ijk->ij',subs,subs)) 

eine weitere Alternative mit numexpr module -

import numexpr as ne 
np.sqrt(ne.evaluate('sum((A-B)**2,2)')) 

Da wir mit einer Länge von 2 entlang der letzten Achse arbeiten, konnten wir nur sli ce diese und füttern Sie es evaluate Methode. Bitte beachten Sie, dass das Slicen innerhalb der Evaluierungszeichenfolge nicht möglich ist. So würde die modifizierte Implementierung sein -

a0 = A[...,0] 
a1 = A[...,1] 
b0 = B[...,0] 
b1 = B[...,1] 
out = ne.evaluate('sqrt((a0-b0)**2 + (a1-b1)**2)') 

Runtime Test

Funktionsdefinitionen -

def sqrt_sum_sq_based(A,B): 
    return np.sqrt(((A - B)**2).sum(-1)) 

def einsum_based(A,B): 
    subs = A - B 
    return np.sqrt(np.einsum('ijk,ijk->ij',subs,subs)) 

def numexpr_based(A,B): 
    return np.sqrt(ne.evaluate('sum((A-B)**2,2)')) 

def numexpr_based_with_slicing(A,B): 
    a0 = A[...,0] 
    a1 = A[...,1] 
    b0 = B[...,0] 
    b1 = B[...,1] 
    return ne.evaluate('sqrt((a0-b0)**2 + (a1-b1)**2)') 

Timings -

In [288]: # Setup input arrays 
    ...: dim = 2 
    ...: N = 1000 
    ...: A = np.random.rand(N,N,dim) 
    ...: B = np.random.rand(N,N,dim) 
    ...: 

In [289]: %timeit sqrt_sum_sq_based(A,B) 
10 loops, best of 3: 40.9 ms per loop 

In [290]: %timeit einsum_based(A,B) 
10 loops, best of 3: 22.9 ms per loop 

In [291]: %timeit numexpr_based(A,B) 
10 loops, best of 3: 18.7 ms per loop 

In [292]: %timeit numexpr_based_with_slicing(A,B) 
100 loops, best of 3: 8.23 ms per loop 

In [293]: %timeit np.linalg.norm(A-B, axis=-1) #@dnalow's soln 
10 loops, best of 3: 45 ms per loop 
+0

Hätten Sie Links oder Ressourcen, die 'np.einsum' im Vergleich zu normaler numpy Effizienz vergleichen? – jadsq

+1

@jadsq Ich denke [dieses] (http://stackoverflow.com/questions/18365073/why-is-numpys-einsum-faster-than-numpys-built-in-functions), wenn wir über einen allgemeinen Fall sprechen . In diesem speziellen Fall machen wir zwei Dinge auf einmal mit'Einsum' und das macht es hier wirklich nützlich. – Divakar

+1

@Divakar, danke! Ich habe heute wieder etwas Neues von dir gelernt. – MaxU

2

der Vollständigkeit halber:

np.linalg.norm(A-B, axis=-1) 
+0

Schön! Dies wurde zur Liste der Ansätze für den Laufzeittest hinzugefügt. – Divakar

Verwandte Themen