Python 提高在二维numpy数组中查找最小元素的速度,该数组中有许多项设置为np.inf
我有一个16000*16000的矩阵,我想找到最小的条目。这个矩阵是一个距离矩阵,所以它是对角对称的。为了每次只得到一个最小值,我将下三角形和对角线设置为Python 提高在二维numpy数组中查找最小元素的速度,该数组中有许多项设置为np.inf,python,performance,numpy,matrix,minimum,Python,Performance,Numpy,Matrix,Minimum,我有一个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()时,它仍然会遍历整个矩阵。有没有办
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()
时,它仍然会遍历整个矩阵。有没有办法“忽略”下三角并提高速度
我尝试过很多方法,例如:
triu_index()
提取上部三角形,然后找到最小值None
而不是np.inf
,然后使用np.nanargmin()
查找最小值np.argmin()
慢
谢谢你抽出时间,如果你能帮助我,我将不胜感激
更新1:我的问题的一些背景信息
事实上,我正在从头开始实现一个经过修改的凝聚集群版本。原始数据集是16000*64(我有16000个点,每个点都是64维的)。首先,我构建了16000个集群,每个集群只包含一个点。在每次迭代中,我找到最近的两个集群并合并它们,直到满足终止条件
为了避免重复计算距离,我将距离存储在16000*16000距离矩阵中。我将对角线和下三角形设置为np.inf
。在每次迭代中,我将在距离矩阵中找到最小的条目,该条目的索引对应于两个最近的聚类,例如c_I
和c_j
。然后,在距离矩阵中,我将对应于np.inf的c_I
和c_j
的2行和2列填充到np.inf中,这意味着这两个集群被合并,不再存在。然后,我将计算新集群和所有其他集群之间的距离数组,然后将该数组放在与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
尽管如此,我需要说的是,生成距离矩阵并不是现在算法中的瓶颈,因为我只生成了一次,然后我只更新了距离矩阵(如上所述)。您可以通过掩蔽选择数组的上三角形,简单示例:
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])
因此,您必须生成一个掩码,其中下三角形为
False
,上三角形为True
,而不是将下三角形设为inf
,并应用掩码arr[mask]
,返回上三角形的1D数组,然后你应用min我能想到的一件事是使用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]
@njit
def上_最小值(m):
x=np.inf
对于范围内的r(0,m.形状[0]-1):
对于范围内的c(r+1,m形[1]+1):
如果x
确保不要在第一次运行时计时。编译速度很慢
另一种方法是以某种方式使用稀疏矩阵。如果我们后退一步,假设距离矩阵是对称的,并且基于
(i,n)
形状的数组,其中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.space.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)
未完全测试,但我认为应该可以。如何创建距离矩阵?如果它是对称的,它很可能是自指的,对吗?你能用它来代替你现在所做的吗?仅输出(且仅计算)上部三角形的。然后,您可以使用
argmin
的结果与triu_索引
进行对比,或者找到直接计算的方法(因为所有这些索引都非常庞大)。演示如何计算距离。我想我可以帮你很多,重新写下这一步。蒂里欧指数做得更有效,但速度很慢。首先,非常感谢你的时间!整个算法仍然需要40分钟才能完成。我刚刚补充了一些我的问题背景,你能抽出一些时间来看看吗?非常感谢你的帮助!啊,是的。要使KDTree高效,您需要i>2**n
。有了64个维度,你就陷入了暴力强迫。是否可以在聚类之前对数据进行PCA以降低维数?我将尝试PCA并检查准确性是否降低。我希望准确度不会有太大变化…首先非常感谢您的时间!我尝试了numba
,它让我的速度提高了28%。然而,整个算法仍然需要40分钟才能完成。我刚刚补充了一些我的问题背景,你能抽出一些时间来看看吗?真实的