Python 提高在二维numpy数组中查找最小元素的速度,该数组中有许多项设置为np.inf

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()时,它仍然会遍历整个矩阵。有没有办

我有一个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()
时,它仍然会遍历整个矩阵。有没有办法“忽略”下三角并提高速度

我尝试过很多方法,例如:

  • 使用屏蔽数组
  • 使用
    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分钟才能完成。我刚刚补充了一些我的问题背景,你能抽出一些时间来看看吗?真实的