[英]How to calculate hamming distance between 1d and 2d array without loop
[英]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)
我的數據類似於以下矩陣:
>>> 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.