字符串余弦相似性算法是通过利用我们初中就学过的三角函数中的余弦定理来计算两个字符串的相似度,它是定义在向量空间模型(Vector Space Model)中的。
余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,这就叫”余弦相似性”。具体算法请看:https://baike.baidu.com/item/%E4%BD%99%E5%BC%A6%E7%9B%B8%E4%BC%BC%E5%BA%A6
我这里的算法并不是完全按照原来的算法实现的,因为经过测试发现如果使用原来的算法来实现的话,对于有SEO背景的人来讲,计算出来的相关性就不那么满意,所以我把算法改进了一下,只计算两个字符串中的名词和动词,如果要计算所有的词,那么也可以通过修改下面的代码实现。
说明:脚本是基于python2.7.x开发的,当然在python3.x下也是可以运行的需要用到jieba分词库,安装方法: pip install jieba如果安装比较慢,那么可以使用pypi豆瓣源:pip install -i https://pypi.douban.com/simple jieba
实现代码:
# -*- coding: utf-8 -*- from jieba import posseg import math import time # 对要进行比较的str1和str2进行计算,并返回相似度 def simicos(str1, str2): # 对两个要计算的字符串进行分词, 使用隐马尔科夫模型(也可不用) # 由于不同的分词算法, 所以分出来的结果可能不一样 # 也会导致相似度会有所误差, 但是一般影响不大 # 如果想把所有的词性都计算,那么把if及其后面的全部删除掉即可 cut_str1 = [w for w, t in posseg.lcut(str1) if 'n' in t or 'v' in t] cut_str2 = [w for w, t in posseg.lcut(str2) if 'n' in t or 'v' in t] # 列出所有词 all_words = set(cut_str1 + cut_str2) # 计算词频 freq_str1 = [cut_str1.count(x) for x in all_words] freq_str2 = [cut_str2.count(x) for x in all_words] # 计算相似度 sum_all = sum(map(lambda z, y: z * y, freq_str1, freq_str2)) sqrt_str1 = math.sqrt(sum(x ** 2 for x in freq_str1)) sqrt_str2 = math.sqrt(sum(x ** 2 for x in freq_str2)) return sum_all / (sqrt_str1 * sqrt_str2) if __name__ == '__main__': case1 = "一车主为防碰瓷,将玛莎拉蒂布满玻璃渣,网友惊呼:绝了!" case2 = "车主为保护玛莎拉蒂将其布满玻璃渣,防“碰瓷”也是绝了!" start = time.time() similarity = simicos(case1, case2) end = time.time() print print "耗时: %.3fs" % (end - start) print "相似度: %.3f" % similarity
运行结果示例:
Building prefix dict from the default dictionary ... Dumping model to file cache /var/folders/h9/5172d1757k90s7p3ngzzgllm0000gn/T/jieba.cache Loading model cost 1.757 seconds. Prefix dict has been built succesfully. 耗时: 1.763s 相似度: 0.632
实际运用场景(主要是SEO中):
更多精彩尽在微信公众号:布鲁的python
如果不是句子与句子之间的比较similarity而是比较一个词与另一个个词之间的相似性的话,不就不用分词了嘛,但是后面就该怎么操作了呢?