簡體   English   中英

提高在二維 numpy 數組中查找最小元素的速度,該數組有許多條目設置為 np.inf

[英]Increase speed of finding minimum element in a 2-D numpy array which has many entries set to np.inf

我有一個 16000*16000 矩陣,想找到最小的條目。 這個矩陣是一個距離矩陣,所以它是關於對角線對稱的。 為了每次都得到一個最小值,我將下三角形和對角線設置為np.inf 下面是一個 5*5 矩陣示例:

inf a0  a1  a2  a3
inf inf a4  a5  a6
inf inf inf a7  a8
inf inf inf inf a9
inf inf inf inf inf

我只想在上三角形中找到最小條目的索引。 但是,當我使用np.argmin()時,它仍然會通過整個矩陣 go 。 有沒有辦法“忽略”下三角形並提高速度?

我嘗試了很多方法,例如:

  1. 使用掩碼數組
  2. 使用triu_indices()提取上三角形,然后找到最小值
  3. 將下三角形和對角線中的條目設置為None而不是np.inf ,然后使用np.nanargmin()找到最小值

但是,我嘗試的所有方法都比直接使用np.argmin()慢。

感謝您的寶貴時間,如果您能幫助我,我將不勝感激。

更新1:我的問題的一些背景

事實上,我正在從頭開始實施凝聚聚類的修改版本。 原始數據集是16000*64(我有16000個點,每個都是64維的)。 起初,我構建了 16000 個集群,每個集群都包含一個點。 在每次迭代中,我找到最近的 2 個集群並將它們合並,直到滿足終止條件。

為了避免重復計算距離,我將距離存儲在一個 16000*16000 的距離矩陣中。 我將對角線和下三角形設置為np.inf 在每次迭代中,我會在距離矩陣中找到最小的條目,並且該條目的索引對應於最近的 2 個簇,例如c_ic_j 之后,在距離矩陣中,我將c_ic_j對應的2行2列填充到np.inf中,也就是說這2個簇合並了,不存在了。 然后我將計算新集群與所有其他集群之間的距離數組,然后將該數組放在與c_i對應的 1 行 1 列中。

讓我說清楚:在整個過程中,距離矩陣的大小永遠不會改變。 在每次迭代中,對於 2 行和 2 列對應於我找到的 2 個最近的集群,我用np.inf填充 1 行和 1 列,並將新集群的距離數組放在其他 1 行和 1 列中。

現在性能的瓶頸是在距離矩陣中找到最小的條目,這需要 0.008 秒。 整個算法的運行時間約為 40 分鍾。

更新 2:我如何計算距離矩陣

下面是我用來生成距離矩陣的代碼:

from sklearn.metrics import pairwise_distances

dis_matrix = pairwise_distances(dataset)

for i in range(num_dim):
    for j in range(num_dim):
        if i >= j or (cluster_list[i].contain_reference_point and cluster_list[j].contain_reference_point):
            dis_matrix[i][j] = np.inf

盡管如此,我還是要說,現在生成距離矩陣不是算法的瓶頸,因為我只生成一次,然后我只是更新距離矩陣(如上所述)。

如果我們后退一步,假設距離矩陣是對稱的並且基於具有ni點的(i, n)形狀的數組,並且距離度量是笛卡爾坐標,這可以使用KDTree數據結構非常有效地完成:

i = 16000
n = 3
points = np.random.rand(i, n) * 100

from scipy.spatial import cKDTree
tree = cKDTree(points)
close = tree.sparse_distance_matrix(tree, 
                                    max_distance = 1, #can tune for your application
                                    output_type  = "coo_matrix") 
close.eliminate_zeros()
ix = close.data.argmin()
i, j = (close.row[ix], close.col[ix])

這非常快,但它是否對您有用取決於您的應用程序和距離度量。

如果您根本不需要距離矩陣(並且只需要索引),您可以執行以下操作:

d, ix = tree.query(points, 2)
j, i = ix[d[:, 1].argmin()]

編輯:這不適用於高維數據。 由於您正面臨維度的詛咒,您可能需要蠻力。 我為此推薦scipy.spatial.distance.pdist

from scipy.spatial.distance import pdist
D = pdist(points, metric = 'seuclidean')  # this only returns the upper diagonal
ix = np.argmin(D)

def ix_to_ij(ix, n):
    sorter = np.arange(n-1)[::-1].cumsum()
    j = np.searchsorted(sorter, ix)
    i = ix - sorter[j]
    return i, j

ix_to_ij(ix, 16000)

沒有完全測試,但我認為應該可以。

我能想到的一件事可能會給您帶來提振,那就是使用numba.njit

@njit
def upper_min(m):
    x = np.inf
    for r in range(0, m.shape[0] - 1):
        for c in range(r + 1, m.shape[1] + 1):
            if x < m[r, c]:
                x = m[r, c]

確保第一次運行時不要計時。 編譯很慢。

另一種方法可能是以某種方式使用稀疏矩陣。

可以通過掩碼對數組的select上三角進行屏蔽,簡單示例:

import numpy as np
arr = np.array([[0, 1], [2, 3]])
# Mask of upper triangle
mask = np.array([[True, True],[False, True]])
# Masking returns only upper triangle as 1D array
min_val = np.min(arr[mask]) # Equal to np.min([0, 1, 3])

因此,不要將下三角形設為inf ,您必須生成一個掩碼,其中下三角為False且上三角為True並應用掩碼arr[mask]返回上三角的一維數組,然后應用 min

暫無
暫無

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

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