我有一个大约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秒)所以它不是很好。无论如何,任何人都可以推荐改进/更改以加快速度吗?
答案 0 :(得分:1)
如果你只需要每个比特的最近邻居(忽略自己),并且你可能只能获得一个近似的最近邻居,你可能会考虑实施&#34 ;比特采样&#34;汉明距离的局部敏感哈希。简而言之,创建三个哈希表。从每个128位输入,采样16位,3次,使用这些16位采样作为键。哈希表的值应该是具有该采样密钥的所有128位输入的列表。将所有数百万个输入放入LSH索引后,只需:
装载和测试都非常快。我可能会推荐优秀的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,则可以非常快速地获得结果。