寻找快速计算许多字符串的成对距离的方法

时间:2013-01-21 01:33:37

标签: python distance

我有一个大约100万个唯一的16个字符的字符串列表(一个名为VEC的数组),我想计算Python中每个字符串的最小成对汉明距离(一个名为RES的数组)。基本上,我一次计算一对完整的成对距离矩阵,但每行只在RES中存储最小值。

VEC= ['AAAAAAAAAAAAAAAA','AAAAAAAAAAAAAAAT','AAAAGAAAAAATAAAA'...]

使dist(VEC [1],VEC [2])= 1,dist(VEC [1],VEC [3])= 2等......并且RES [1] = 1。使用这些页面中的提示和技巧,我提出了:

#METHOD#1:
import Levenshtein
import numpy
RES=99*numpy.ones(len(VEC))
i=0
for a in VEC:
    dist=numpy.array([Levenshtein.hamming(a,b) for b in VEC] ) #array of distances
    RES[i]=numpy.amin(dist[dist>0])  #pick min distance greater than zero
    i+=1
只有10,000的VEC缩短了大约70秒,但是如果我将其推断到满百万,则需要8天。我的方法似乎很浪费,因为我正在重新计算距离矩阵的对称部分,所以我尝试计算一半矩阵,同时为每行更新RES:

#METHOD #2:
import Levenshtein
import numpy
RES=99*numpy.ones(len(VEC))
for i in range(len(VEC)-1):
    dist=[Levenshtein.hamming(VEC[i],VEC[j]) for j in range(i+1, len(VEC))]
    RES[i]=min(numpy.amin(dist),RES[i])
    #update RES as you go along:
    k=0
    for j in range(i+1,len(VEC)):
        if dist[k]<RES[j]:
             RES[j]=dist[k]
        k+=1

可能不足为奇,第二种方法需要几乎两倍的时间(117秒)所以它不是很好。无论如何,任何人都可以推荐改进/更改以加快速度吗?

2 个答案:

答案 0 :(得分:1)

如果你只需要每个比特的最近邻居(忽略自己),并且你可能只能获得一个近似的最近邻居,你可能会考虑实施&#34 ;比特采样&#34;汉明距离的局部敏感哈希。简而言之,创建三个哈希表。从每个128位输入,采样16位,3次,使用这些16位采样作为键。哈希表的值应该是具有该采样密钥的所有128位输入的列表。将所有数百万个输入放入LSH索引后,只需:

  • 迭代百万分
  • 对于每个输入,执行上述3次采样
  • 在三个列表中找到最近的邻居(距离> 0),保持最佳整体

装载和测试都非常快。我可能会推荐优秀的bitarray库来支持这个。

答案 1 :(得分:0)

我试着用numpy。这是我的代码:

#!/usr/bin/env python

import numpy as np
import time

def gen_data(n):
    arr = np.empty(shape=(n, 16))
    for i in range(n):
        arr[i] = np.random.randint(ord('A'), ord('Z')+1, 16)
    return arr

def distance_from_array(i, arr):
    r = arr[i] != arr
    r[i,:] = True
    min_i = np.argmin(np.sum(r, axis=1))
    return min_i

data = gen_data(1000000)
distances = []
start = time.time()
for i in range(200):
    distances.append(distance_from_array(i, data))
end = time.time()
print end - start

您可以将字符串列表转换为数字数组。然后你可以使用numpy函数来处理数组,例如sum和argmin。我想你不想只找到大于1的距离,如果一个字符串可能会出现两次。

我在计算机上测试了它,处理200个字符串大约需要10秒钟。对于每一个,你必须经历所有1 000 000个其他字符串,因此我们可以计算相当容易处理所有字符串所需的时间。应该是大约13个小时。但是,不要忘记我们目前只使用一个核心。如果您拆分索引并使用http://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.pool,则可以非常快速地获得结果。