簡體   English   中英

二維數組的 Python Earth Mover 距離

[英]Python Earth Mover Distance of 2D arrays

我想計算兩個二維陣列之間的地球移動距離(這些不是圖像)。

現在我瀏覽了兩個庫: scipy ( https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.wasserstein_distance.html ) 和pyemd ( https://pypi.org/project/pyemd / )。

#define a sampeling method
def sampeling2D(n, mu1, std1, mu2, std2):
   #sample from N(0, 1) in the 2D hyperspace
   x = np.random.randn(n, 2)

   #scale N(0, 1) -> N(mu, std)
   x[:,0] = (x[:,0]*std1) + mu1
   x[:,1] = (x[:,1]*std2) + mu2

   return x

#generate two sets
Y1 = sampeling2D(1000, 0, 1, 0, 1)
Y2 = sampeling2D(1000, -1, 1, -1, 1)

#compute the distance
distance = pyemd.emd_samples(Y1, Y2)

雖然scipy版本不接受二維數組並返回錯誤,但pyemd方法返回一個值。 如果你從文檔中看到,它說它只接受一維數組,所以我認為輸出是錯誤的。 在這種情況下如何計算這個距離?

因此,如果我理解正確,您正在嘗試傳輸采樣分布,即計算所有集群權重為 1 的設置的距離。通常,您可以將 EMD 的計算視為最小成本流的一個實例,在您的情況下,這歸結為線性分配問題:您的兩個數組是二部圖中的分區,兩個頂點之間的權重是您選擇的距離。 假設您想使用歐幾里得范數作為度量,可以使用scipy.spatial.distance.cdist獲得邊的權重,即地面距離,實際上 SciPy 為線性和分配問題提供了一個求解器,如在scipy.optimize.linear_sum_assignment最近看到了 SciPy 1.4 中可用的巨大性能改進。如果您遇到性能問題,這可能對您感興趣;1.3 實現對於 1000x1000 輸入有點慢)。

換句話說,你想做的事情歸結為

from scipy.spatial.distance import cdist
from scipy.optimize import linear_sum_assignment

d = cdist(Y1, Y2)
assignment = linear_sum_assignment(d)
print(d[assignment].sum() / n)

也可以使用scipy.sparse.csgraph.min_weight_bipartite_full_matching作為linear_sum_assignment替代linear_sum_assignment 雖然是為稀疏輸入(你的肯定不是)而設計的,但它可能會在某些情況下提供性能改進。

驗證此計算的結果是否與您從最小成本流求解器中得到的結果相匹配可能會有所幫助; NetworkX 中提供了一種這樣的求解器,我們可以在其中手動構建圖形:

import networkx as nx

G = nx.DiGraph()

# Represent elements in Y1 by 0, ..., 999, and elements in
# Y2 by 1000, ..., 1999.
for i in range(n):
    G.add_node(i, demand=-1)
    G.add_node(n + i, demand=1)

for i in range(n):
    for j in range(n):
        G.add_edge(i, n + j, capacity=1, weight=d[i, j])

此時,我們可以驗證上述方法是否符合最小成本流:

In [16]: d[assignment].sum() == nx.algorithms.min_cost_flow_cost(G)
Out[16]: True

同樣,對於一維輸入,看到結果與scipy.stats.wasserstein_distance一致是scipy.stats.wasserstein_distance的:

from scipy.stats import wasserstein_distance

np.random.seed(0)
n = 100

Y1 = np.random.randn(n)
Y2 = np.random.randn(n) - 2
d =  np.abs(Y1 - Y2.reshape((n, 1)))

assignment = linear_sum_assignment(d)
print(d[assignment].sum() / n)       # 1.9777950447866477
print(wasserstein_distance(Y1, Y2))  # 1.977795044786648

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM