如何在Python中编码向量到矩阵的汉明距离?

如何在Python中编码向量到矩阵的汉明距离?,python,matrix,information-retrieval,hamming-distance,Python,Matrix,Information Retrieval,Hamming Distance,我喜欢开发一个查询系统,根据从数据中提取的二进制签名查找与给定项最相似的项。我探索最有效的方法,因为我有运行时约束。我试着使用短距离,但速度太慢了。你知道其他有用的库或技巧,使它以更快的方式 作为示例场景 我有一个长度为68的二进制值的查询向量,还有一个矩阵大小为3000Kx68的数据集。我喜欢使用汉明距离在这个矩阵中找到与给定查询最相似的项 感谢您的评论如果有比这更快的方法,我会感到惊讶:将您的数据放入pandas数据框(M),每个向量按列排列,目标向量放入pandas系列(x) 然后执行以下

我喜欢开发一个查询系统,根据从数据中提取的二进制签名查找与给定项最相似的项。我探索最有效的方法,因为我有运行时约束。我试着使用短距离,但速度太慢了。你知道其他有用的库或技巧,使它以更快的方式

作为示例场景

我有一个长度为68的二进制值的查询向量,还有一个矩阵大小为3000Kx68的数据集。我喜欢使用汉明距离在这个矩阵中找到与给定查询最相似的项


感谢您的评论

如果有比这更快的方法,我会感到惊讶:将您的数据放入pandas数据框(
M
),每个向量按列排列,目标向量放入pandas系列(
x

然后执行以下操作

%timeit M.apply(lambda y: x==y).astype(int).sum().idxmax()

1 loop, best of 3: 746 ms per loop
编辑:事实上,我很惊讶这是一种更快的方法

%timeit M.eq(x, axis=0).astype(int).sum().idxmax()

100 loops, best of 3: 2.68 ms per loop

使用SciPy中的
cdist

from scipy.spatial.distance import cdist
Y = cdist(XA, XB, 'hamming')
计算归一化的汉明距离,或两个不一致的n向量u和v之间的向量元素的比例。为了节省内存,矩阵X可以是布尔类型


参考资料:

问题很好,我喜欢Alex和Piotr的答案。我第一次天真的尝试也导致了大约800毫秒的解决时间(在我的系统上)。我的第二次尝试是使用numpy的
(un)packbits
,速度提高了4倍

import numpy as np

LENGTH = 68
K = 1024
DATASIZE = 3000 * K
DATA = np.random.randint(0, 2, (DATASIZE, LENGTH)).astype(np.bool)

def RandomVect():
    return np.random.randint(0, 2, (LENGTH)).astype(np.bool)

def HammingDist(vec1, vec2):
    return np.sum(np.logical_xor(vec1, vec2))

def SmallestHamming(vec):
    XorData = np.logical_xor(DATA, vec[np.newaxis, :])
    Lengths = np.sum(XorData, axis=1)
    return DATA[np.argmin(Lengths)]  # returns first smallest

def main():
    v1 = RandomVect()
    v2 = SmallestHamming(v1)
    print(HammingDist(v1, v2))

# oke, lets try make it faster... (using numpy.(un)packbits)

DATA2 = np.packbits(DATA, axis=1)
NBYTES = DATA2.shape[-1]

BYTE2ONES = np.zeros((256), dtype=np.uint8)
for i in range(0,256):
    BYTE2ONES[i] = np.sum(np.unpackbits(np.uint8(i)))

def RandomVect2():
    return np.packbits(RandomVect())

def HammingDist2(vec1, vec2):
    v1 = np.unpackbits(vec1)
    v2 = np.unpackbits(vec2)
    return np.sum(np.logical_xor(v1, v2))

def SmallestHamming2(vec):
    XorData = DATA2 ^ vec[np.newaxis, :]
    Lengths = np.sum(BYTE2ONES[XorData], axis=1)
    return DATA2[np.argmin(Lengths)]  # returns first smallest

def main2():
    v1 = RandomVect2()
    v2 = SmallestHamming2(v1)
    print(HammingDist2(v1, v2))

一种快速的方法是将68位存储在字节中。然后对向量行和矩阵行的字节进行异或运算,并将结果用作查找表的索引(2^8个条目)。查找表(简单列表)存储每个字节中的位数。即。表[0b111]=3。您尝试使用SciPy的哪个距离函数并不明显。如果是这样的话,恐怕你需要借助一些启发式方法(如果你想要3Mx3M矩阵,它是数百TB的,所以你需要用Spark或类似的东西来并行处理)。这样平均每次调用823ms,我最多需要823ms20ms@Erogol如果您将其并行化,请按批执行,而不是按每个向量对执行。(我不知道,每1000个?)对于小型阵列,转换的开销很大。不过,每次调用823ms似乎还是很大(我不知道为什么要花这么长时间;就像每次运行都要加载所有库一样。)哦,Erogol提到的是3000K列,而不是3000列。我的第二个方法是3000K列,每个循环的时间为2.7s,是1K的一个因数。这意味着不同列的函数DataFrame.eq()的计算不会并行化。也许有办法迫使它。有什么见解吗?
import numpy as np

LENGTH = 68
K = 1024
DATASIZE = 3000 * K
DATA = np.random.randint(0, 2, (DATASIZE, LENGTH)).astype(np.bool)

def RandomVect():
    return np.random.randint(0, 2, (LENGTH)).astype(np.bool)

def HammingDist(vec1, vec2):
    return np.sum(np.logical_xor(vec1, vec2))

def SmallestHamming(vec):
    XorData = np.logical_xor(DATA, vec[np.newaxis, :])
    Lengths = np.sum(XorData, axis=1)
    return DATA[np.argmin(Lengths)]  # returns first smallest

def main():
    v1 = RandomVect()
    v2 = SmallestHamming(v1)
    print(HammingDist(v1, v2))

# oke, lets try make it faster... (using numpy.(un)packbits)

DATA2 = np.packbits(DATA, axis=1)
NBYTES = DATA2.shape[-1]

BYTE2ONES = np.zeros((256), dtype=np.uint8)
for i in range(0,256):
    BYTE2ONES[i] = np.sum(np.unpackbits(np.uint8(i)))

def RandomVect2():
    return np.packbits(RandomVect())

def HammingDist2(vec1, vec2):
    v1 = np.unpackbits(vec1)
    v2 = np.unpackbits(vec2)
    return np.sum(np.logical_xor(v1, v2))

def SmallestHamming2(vec):
    XorData = DATA2 ^ vec[np.newaxis, :]
    Lengths = np.sum(BYTE2ONES[XorData], axis=1)
    return DATA2[np.argmin(Lengths)]  # returns first smallest

def main2():
    v1 = RandomVect2()
    v2 = SmallestHamming2(v1)
    print(HammingDist2(v1, v2))