文档相似性:有效地比较两个文档

时间:2010-03-13 10:24:55

标签: python mysql performance

我有一个循环来计算两个文档之间的相似性。它收集文档中的所有标记及其分数,并将它们放在字典中。然后它比较字典

这是我到目前为止所做的,但是效果很好:

# Doc A
cursor1.execute("SELECT token, tfidf_norm FROM index WHERE doc_id = %s", (docid[i][0]))
doca = cursor1.fetchall()
#convert tuple to a dictionary
doca_dic = dict((row[0], row[1]) for row in doca)

#Doc B
cursor2.execute("SELECT token, tfidf_norm FROM index WHERE doc_id = %s", (docid[j][0]))
docb = cursor2.fetchall()
#convert tuple to a dictionary
docb_dic = dict((row[0], row[1]) for row in docb)

# loop through each token in doca and see if one matches in docb
for x in doca_dic:
    if docb_dic.has_key(x):
        #calculate the similarity by summing the products of the tf-idf_norm 
        similarity += doca_dic[x] * docb_dic[x]
print "similarity"
print similarity

我对Python很新,因此这个烂摊子。我需要加快速度,任何帮助都会受到赞赏。 感谢。

3 个答案:

答案 0 :(得分:2)

Python点:adict.has_key(k)在Python 2.X中已经过时,在Python 3.X中消失了。自Python 2.2起,k in adict表达式已经可用;用它代替。它会更快(没有方法调用)。

任何语言的实用点:迭代较短的字典。

综合结果:

if len(doca_dic) < len(docb_dict):
    short_dict, long_dict = doca_dic, docb_dic
else:
    short_dict, long_dict = docb_dic, doca_dic
similarity = 0
for x in short_dict:
    if x in long_dict:
        #calculate the similarity by summing the products of the tf-idf_norm 
        similarity += short_dict[x] * long_dict[x]

如果你不需要其他任何两个词典,你可以只创建一个A并在B(key,value)元组中跳出B查询时迭代它们。在docb = cursor2.fetchall()之后,请替换以下所有代码:

similarity = 0
for b_token, b_value in docb:
    if b_token in doca_dic:
        similarity += doca_dic[b_token] * b_value

上述代码的替代方法:这是做更多的工作,但是它在C语言而不是Python中进行更多的迭代,可能更快。

similarity = sum(
    doca_dic[k] * docb_dic[k]
    for k in set(doca_dic) & set(docb_dic)
    )

Python代码的最终版本

# Doc A
cursor1.execute("SELECT token, tfidf_norm FROM index WHERE doc_id = %s", (docid[i][0]))
doca = cursor1.fetchall()
# Doc B
cursor2.execute("SELECT token, tfidf_norm FROM index WHERE doc_id = %s", (docid[j][0]))
docb = cursor2.fetchall()
if len(doca) < len(docb):
    short_doc, long_doc = doca, docb
else:
    short_doc, long_doc = docb, doca
long_dict = dict(long_doc) # yes, it should be that simple
similarity = 0
for key, value in short_doc:
    if key in long_dict:
        similarity += long_dict[key] * value

另一个实际观点:你还没有说过它的哪一部分很慢......正在做决定或做出选择?将time.time()的一些调用放入您的脚本中。

考虑将所有工作推送到数据库。以下示例使用硬连线SQLite查询,但原理是相同的。

C:\junk\so>sqlite3
SQLite version 3.6.14
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table atable(docid text, token text, score float,
    primary key (docid, token));
sqlite> insert into atable values('a', 'apple', 12.2);
sqlite> insert into atable values('a', 'word', 29.67);
sqlite> insert into atable values('a', 'zulu', 78.56);
sqlite> insert into atable values('b', 'apple', 11.0);
sqlite> insert into atable values('b', 'word', 33.21);
sqlite> insert into atable values('b', 'zealot', 11.56);
sqlite> select sum(A.score * B.score) from atable A, atable B
    where A.token = B.token and A.docid = 'a' and B.docid = 'b';
1119.5407
sqlite>

值得检查数据库表是否已被适当地编入索引(例如,token本身就是一个)...没有可用的索引是使SQL查询运行速度非常慢的好方法。

说明:在token上建立索引可能会使您现有的查询或“在数据库中执行所有工作”查询,或者两者都运行得更快,具体取决于数据库软件中查询优化程序的异想天开以及月相。如果您没有可用的索引,DB将读取表中的所有行 - 不好。

创建索引:create index atable_token_idx on atable(token);

删除索引:drop index atable_token_idx;

(但请参阅您的数据库的文档)

答案 1 :(得分:1)

如何推动数据库上的一些工作呢?

通过联接,您可以获得基本上

的结果
    Token    A.tfidf_norm B.tfidf_norm
-----------------------------------------
    Apple      12.2          11.00
       ...
    Word       29.87         33.21
    Zealot      0.00         11.56
    Zulu       78.56          0.00

您只需要扫描光标并进行操作。

如果您不需要知道一个文档中是否有一个单词而另一个文档中是否缺少某个单词,则不需要外部联接,该列表将是两个集合的交集。上面包含的示例自动为两个文档中的一个文档中缺失的单词分配“0”。看看你的“匹配”功能需要什么。

答案 2 :(得分:0)

一个SQL查询可以完成这项工作:

SELECT sum(index1.tfidf_norm*index2.tfidf_norm) FROM index index1, index index2 WHERE index1.token=index2.token AND index1.doc_id=? AND index2.doc_id=?

只需更换'?'分别带有2个文档ID。