基本实现思路:每次输入两个论文的名字,然后通过查询node.list找到相应的id,或者直接输入论文id进行查询,然后在link.json中找到他引用的所有论文集合,然后计算jaccard相似度。模型中会遇到大量的查询两个论文引用的jaccard相似度,这样查询速度比较慢。由于样本数据量较大,后续使用时,每次读取所有的数据(一对一的引用关系),然后计算相似度速度比较慢,所以把数据集进行了整理,以每篇论文的id为key,以引用的论文的id列表为value进行保存,保存到link.json没有把所有的论文对的相似度计算完保存起来,是因为维度比较高,且数据比较稀疏。(注:对称且稀疏矩阵,可以采用压缩存储。)
算法:十分简单的杰卡德系数(Jaccard Index),也称Jaccard相似系数(Jaccard similarity coefficient),用于比较有限样本集之间的相似性与差异性。如集合间的相似性、字符串相似性、目标检测的相似性、文档查重等
Jaccard系数的计算方式为:交集个数和并集个数的比值
数据来源:计算所使用的数据来自已经给出的ACM数据集,其中的inlinks.list和outlinks.list的数据是两列论文id,都是前面的id所对应的论文引用后面的id所对应的论文。
有待改进的地方:
1.所给数据集只是在一定范围内的引用关系,并不全面,不能较为准确的反应论文对引用的相似度,可以爬取所有的引用关系,然后再计算。
2.可以把所有的论文对引用相似度计算出来,自己尝试进行计算时,还是使用了大量的时间,若是直接带入模型,进行计算,会耗费大量的时间。至于占用空间比较大。可以采用矩阵的压缩存储。
3.在论文所引用的集合中,不同的论文所占有权重也应该是不同的。有的论文对相似度贡献比较大,有的比较小。可以加入惩罚因子。但是究竟是引用量高的被引论文对相似度的贡献更大,还是较少出现的偏僻的被引论文对相似度的贡献更大呢?
直接计算:
def node_id(name):
fp=open("C:\\Users\\asus\\Desktop\\实训\\资料整理\\code+data\\ACM数据集\\nodes.list",'r')
while(fp.readline()):
id,node=fp.readline().rstrip().split("\t")
if node==name:
return id
#若是引用关系不在变化,需不需建立一个表直接把信息存起来
def jaccard2id(a,b):
links1=set()
links2=set()
fp=open("C:\\Users\\asus\\Desktop\\实训\\资料整理\\code+data\\ACM数据集\\inlinks.list",'r')
while(fp.readline()):
links_from,links_to=fp.readline().rstrip().split("\t")
if links_from==a:
links1.add(links_to)
if links_from==b:
links2.add(links_to)
if links1.intersection(links2)==None or links1.intersection_update(links2)==None:
return 0
else:
common=len(links1.intersection(links2))
all2=len(links1.intersection_update(links2))
jaccard=common/(all2+1)
return jaccard
if __name__ == '__main__':
name1=input("请输入第一篇论文的名字:")
name2=input("请输入第二篇论文的名字:")
id1=node_id(name1)
id2=node_id(name2)
samilarity=jaccard2id(id1,id2)
print(samilarity)
保存到文件中:
import json
def essay_link():
link=dict()
fp=open("C:\\Users\\asus\\Desktop\\实训\\资料整理\\code+data\\ACM数据集\\inlinks.list",'r')
while(fp.readline()):
link_from,link_to=fp.readline().rstrip().split("\t")
if link_from in link:
link[link_from].append(link_to)#link['link_from']那么key就是link_from
else:
link[link_from]=[link_to]
#value用list保存,用set()不能保存为json
json_str = json.dumps(link, indent=4)
with open('link.json', 'w') as json_file:
json_file.write(json_str)
def jaccard(id1,id2):
with open('link.json', 'r') as json_file:
link=json.load(json_file)
if id1 in link and id2 in link:
links1=set(link[id1])
links2=set(link[id2])
if links1.intersection(links2)==None or links1.intersection_update(links2)==None:
return 0
else:
# common=len(links1.intersection(links2))
# all2=len(links1.intersection_update(links2))
# jaccard=common/(all2+1) #已经不为0了,感觉不加1平滑也可以了
jaccard=len(links1.intersection(links2))/(len(links1.intersection_update(links2))+1)#借鉴推荐系统上惩罚热门的做法???
return jaccard
else:
return -1
def name_query():
name1=input("请输入第一篇论文的名字:")
name2=input("请输入第二篇论文的名字:")
fp=open("C:\\Users\\asus\\Desktop\\实训\\资料整理\\code+data\\ACM数据集\\nodes.list",'r')
while(fp.readline()):
id,node=fp.readline().rstrip().split("\t")
if node==name1:
id1=id
if node==name2:
id2=id
return jaccard(id1,id2)
def id_query():
id1=input("请输入第一篇论文的编号:")
id2=input("请输入第二篇论文的编号:")
return jaccard(id1,id2)
#计算所有论文对的jaccard相似度
def jall():
fp=open("C:\\Users\\asus\\Desktop\\实训\\资料整理\\code+data\\ACM数据集\\nodes.list",'r')
id=[]
while(fp.readline()):
iid,name=fp.readline().rstrip().split("\t")
id.append(iid)
#print(id)
fp.close()
f=open('jaccard.txt', 'w')
for i in range(len(id)):
for j in range(i+1,len(id)):
jac=jaccard(id[i], id[j])
f.writelines([str(id[i])+'\t'+str(id[j])+'\t'+str(jac)+'\n'])
f.close()
if __name__ == '__main__':
#essay_link()
#name_query()
#print(id_query())
jall()
运行结果:
下图为link.json里面的内容,很多仅有两三篇引用文献,所以大部分的引用关系不是特别全面。
下图是计算出的相似度,-1表示没有引用关系(没有引用其他文献和被其他文献引用),直接保存相似度还是比较稀疏和占用空间的。