[英]is numpy testing assert_array_less working correctly with 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 。 有沒有辦法“忽略”下三角形並提高速度?
我嘗試了很多方法,例如:
triu_indices()
提取上三角形,然后找到最小值None
而不是np.inf
,然后使用np.nanargmin()
找到最小值但是,我嘗試的所有方法都比直接使用np.argmin()
慢。
感謝您的寶貴時間,如果您能幫助我,我將不勝感激。
更新1:我的問題的一些背景
事實上,我正在從頭開始實施凝聚聚類的修改版本。 原始數據集是16000*64(我有16000個點,每個都是64維的)。 起初,我構建了 16000 個集群,每個集群都包含一個點。 在每次迭代中,我找到最近的 2 個集群並將它們合並,直到滿足終止條件。
為了避免重復計算距離,我將距離存儲在一個 16000*16000 的距離矩陣中。 我將對角線和下三角形設置為np.inf
。 在每次迭代中,我會在距離矩陣中找到最小的條目,並且該條目的索引對應於最近的 2 個簇,例如c_i
和c_j
。 之后,在距離矩陣中,我將c_i
和c_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
盡管如此,我還是要說,現在生成距離矩陣不是算法的瓶頸,因為我只生成一次,然后我只是更新距離矩陣(如上所述)。
如果我們后退一步,假設距離矩陣是對稱的並且基於具有n
維i
點的(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.