簡體   English   中英

將1D numpy陣列壓縮為2D漢明距離矩陣

[英]Condensed 1D numpy array to 2D Hamming distance matrix

我正在尋找一種可靠的方法將使用scipy.spatial.distance.pdist函數生成的壓縮漢明距離數組轉換為相應的2D漢明距離矩陣。 我知道scipy.spatial.distance.squareform函數。 但是,我正在計算高達100,000 x 100,000矩陣的漢明距離,這導致Python中的MemoryError

我正在尋找一種方法將壓縮矩陣逐行轉換為方形。 有沒有人知道使用NumPy和/或相關軟件包的可靠(並且可能是快速)實現?

我需要在每一行上執行numpy.sum計算,但不能將完整的N x N矩陣存儲在內存中。

目前,我使用嵌套循環迭代輸入矩陣並“手動”計算距離。

identity = 0.7
hamming_sum = numpy.zeros(msa_mat.shape[0], dtype=numpy.float64)
hamming_dist = numpy.zeros(msa_mat.shape[0], dtype=numpy.float64)
for i, row1 in enumerate(msa_mat):
    hamming_dist.fill(0)
    for j, row2 in enumerate(msa_mat):
        if i != j:
            hamming_dist[j] = scipy.spatial.distance.hamming(row1, row2)
    hamming_sum[i] = numpy.sum(numpy.where(hamming_dist < (1 - identity), 1, 0), axis=0)

編輯1

我的數據類似於以下矩陣:

>>> a = numpy.array([1, 2, 3, 4, 5, 4, 5, 4, 2, 7, 9, 4, 1, 5, 6, 2, 3, 6], dtype=float).reshape(3, 6)
>>> a
array([[ 1.,  2.,  3.,  4.,  5.,  4.],
       [ 5.,  4.,  2.,  7.,  9.,  4.],
       [ 1.,  5.,  6.,  2.,  3.,  6.]])

我想計算這個矩陣的漢明距離。 對於小型矩陣,可以使用SciPy中的cdist命令輕松完成此操作,如下所示:

>>> cdist(a, a, 'hamming')
array([[ 0.        ,  0.83333333,  0.83333333],
       [ 0.83333333,  0.        ,  1.        ],
       [ 0.83333333,  1.        ,  0.        ]])

但是,在具有更大矩陣的情況下,這會在Python中引發MemoryError。

我知道在這種情況下我可以使用pdist命令計算漢明距離。 這將返回1D數組中上三角形的距離。

>>> pdist(a, 'hamming')
array([ 0.83333333,  0.83333333,  1.        ])

我的問題涉及這樣一個事實:我不知道如何從每行pdist結果重建cdist矩陣。

我知道squareform函數,但它再次引發大矩陣的MemoryErrors。

這是一種使用基於ID的np.bincount求和np.bincount -

def getdists_v1(a):
    n = a.shape[0]
    r,c = np.triu_indices(n,1)
    vals = pdist(a, 'hamming') < (1 - identity)
    return np.bincount(r,vals,minlength=n) + np.bincount(c,vals,minlength=n) + 1

這是另一個bin-based ,使用np.add.reduceat關注內存效率 -

def getdists_v2(a):
    n = a.shape[0]
    nr = (n*(n-1))//2
    vals = pdist(a, 'hamming') < (1 - identity)

    sfidx = n*np.arange(0,n-1) - np.arange(n-1).cumsum()
    id_arr = np.ones(nr,dtype=int)
    id_arr[sfidx[1:]] = -np.arange(n-3,-1,-1)
    c = id_arr.cumsum()

    out = np.bincount(c,vals)+1
    out[:n-1] += np.add.reduceat(vals,sfidx)
    return out

這是另一個循環計算下三角區域行方式求和 -

def getdists_v3(a):
    n = a.shape[0]
    r_arr = np.arange(n-1)
    cr_arr = r_arr.cumsum()
    sfidx_c = (n-1)*r_arr - cr_arr
    vals = pdist(a, 'hamming') < (1 - identity)
    out = np.zeros(n)
    for i in range(n-1):
        out[i+1] = np.count_nonzero(vals[sfidx_c[:i+1] + i])
    out[:n-1] += np.add.reduceat(vals, n*r_arr - cr_arr)
    out[:] += 1
    return out

避免內存問題的一種方法是批量使用cdist

import numpy as np
from scipy.spatial.distance import cdist


def count_hamming_neighbors(points, radius, batch_size=None):
    n = len(points)

    if batch_size is None:
        batch_size = min(n, 1000)

    hamming_sum = np.zeros(n, dtype=int)

    num_full_batches, last_batch = divmod(n, batch_size)
    batches = [batch_size]*num_full_batches
    if  last_batch != 0:
        batches.append(last_batch)
    for k, batch in enumerate(batches):
        i = batch_size*k
        dists = cdist(points[i:i+batch], points, metric='hamming')
        hamming_sum[i:i+batch] = (dists < radius).sum(axis=1)

    return hamming_sum

這是Divakar的getdists_v3(a) ,以確保我們得到相同的結果:

In [102]: np.random.seed(12345)

In [103]: a = np.random.randint(0, 4, size=(16, 4))

In [104]: count_hamming_neighbors(a, 0.3)
Out[104]: array([1, 1, 3, 2, 2, 1, 2, 1, 3, 2, 3, 2, 2, 1, 2, 2])

In [105]: identity = 0.7

In [106]: getdists_v3(a)
Out[106]: 
array([ 1.,  1.,  3.,  2.,  2.,  1.,  2.,  1.,  3.,  2.,  3.,  2.,  2.,
        1.,  2.,  2.])

比較更大陣列的時間:

In [113]: np.random.seed(12345)

In [114]: a = np.random.randint(0, 4, size=(10000, 4))

In [115]: %timeit hamming_sum = count_hamming_neighbors(a, 0.3)
1 loop, best of 3: 714 ms per loop

In [116]: %timeit v3result = getdists_v3(a)
1 loop, best of 3: 1.05 s per loop

所以它快一點。 更改批量大小會影響性能,有時會出現令人驚訝的方式:

In [117]: %timeit hamming_sum = count_hamming_neighbors(a, 0.3, batch_size=250)
1 loop, best of 3: 643 ms per loop

In [118]: %timeit hamming_sum = count_hamming_neighbors(a, 0.3, batch_size=2000)
1 loop, best of 3: 875 ms per loop

In [119]: %timeit hamming_sum = count_hamming_neighbors(a, 0.3, batch_size=125)
1 loop, best of 3: 664 ms per loop

暫無
暫無

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

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