依据CCS分类建树(简单方案)
CCS分类
ACM Computing Classification System(CCS)
2012 ACM计算分类系统已开发为一种多层次的本体,可以在语义Web应用程序中使用。它替代了1998年的ACM计算分类系统(CCS)的传统版本,该版本已成为计算领域的事实上的标准分类系统。
它已集成到ACM数字图书馆的搜索功能和可视主题显示中。它依赖于语义词汇作为类别和概念的唯一来源,它反映了计算机科学的最新水平,并且随着未来的发展而接受结构性变化。 ACM在可视显示格式内提供了一种工具,以促进将2012 CCS类别应用到即将发表的论文中,并提供确保CCS与时俱进的过程。新的分类系统将在ACM数字图书馆的人员搜索界面的开发中起到关键作用,以补充其当前的传统书目搜索。
完整的CCS分类树可以HTML格式免费用于教育和研究目的。在ACM数字图书馆中,CCS以可视化显示格式显示,有助于导航和反馈。完整的CCS分类树也可以在数字图书馆中以平面文件形式查看。
来源:https://dl.acm.org/ccs
即,CCS分类是一种树结构的分类标准。接下来看一下给出的CCS分类文件:
由这个文件可以很显然的看出每篇论文的所属的类别。
例如:
- h. information systems h.3 information storage and retrieval h.3.3 information search and retrieval subjects: search process
- H.信息系统 h.3信息存储和检索 h.3.3信息搜索和检索主题:搜索过程
由此可见,该文件属于h.3.3类。
文件的每一行对应一个类,空行是空数据,作特殊处理。
在查询的过程中也发现了中国标准文献分类法(CCS),在这里也可以记录一下。
建树求距离
方案一:借助文件求
观察文件IndexTerms.txt,我们可以知道一篇论文可能会有三种情况:
- 论文属于a(假设)
- 论文属于a.1
- 论文属于a.1.1
(以后都以a,a.1,a.1.1为例)
因此,树结构最多有三层。那么,我们可以通过分类讨论列举所有情况并进行处理的方式来解决这个问题。
数据预处理
对于文件IndexTerms.txt来说,我们需要的是能够表达论文所属类型的关键词(a,a.1,a.1.1等),而不需要冗长的介绍,因此我们需要对文件进行预处理。
对于这种,我们选择提取k.3.1
但是并不是每个论文都能精确的分类到第三级,因此会有:
这种情况出现,对于这样的我们选择提取j.5。
而有的论文只能划分到大类,例如
对于这种情况,我们选择提取a。
对于这种提取我们有一个很好用的工具:正则表达式。能想到这个问题就很简单了,代码如下
s=""
for i in range(len(lis)):
if(len(lis[i])==0):
#print(i)
s+=str(i)
s+=','
s+='\n'
else:
s+=str(i)
s+=','
ret = re.findall(r'\w.\d.\d',lis[i])
if(len(ret)==0):
print(i)
ret = re.findall(r'\w.\d',lis[i])
if(len(ret)==0):
ret = re.findall(r'\w',lis[i])
s+=ret[0]
else:
s+=ret[0]
else:
for j in range(len(ret)):
s+=ret[j]
if(j<len(ret)-1):
s+=","
s+='\n'
print(s)
需要注意的是,还有一种特殊情况需要单独考虑:
图中展示的论文分类表示,该论文可以被分到多个类中,这种情况我们需要把它所属的类都统计下来,因此在匹配的时候选择的函数是
r
e
.
f
i
n
d
a
l
l
(
)
re.findall()
re.findall()
为了方便之后的计算,我们把处理后的数据存到一个文件中。存储的形式为“论文id,分类”,因此在构造字符串的时候也进行了相应的处理。写入文件:
fh = open('E:\\创新实训\\资料整理\\code+data\\treenode.txt', 'w', encoding='utf-8')
fh.write(s)
fh.close()
文件如下:
计算距离
因为树的结构是规则的,因此距离也是有规律可循的,接下来我们对每种情况进行分类讨论。
假设论文为X和Y,并且,我们把 “* . * . *” 部分记为该论文的分类属性。
- 其中有论文没有所属类:
- 相似度为0
- 两篇论文不属于同一个大类:
- 相似度为0(该相似度仍有待商榷)
- 两篇论文的分类属性字符串完全相同
- 相似度为1
- 两篇论文的分类属性字符串不同
- 两篇论文分类属性长度
l
l
l相同(字符串长度)
- l = = 3 l==3 l==3:同为二级节点,没有多种情况,边数为2,设边权为2(*),相似度记为距离的倒数,因此相似度为1/4。
-
l
=
=
5
l==5
l==5:
- 两篇论文属于同一个二级结点:边数为2,距离为4,相似度为1/4。
- 两篇论文不同属于一个二级结点,那么它们的最近公共父节点就是根节点,因此边数为4,距离为8,相似度为1/8.
- 注: l ! = 1 l!=1 l!=1,因为当属性分类字符串长度为1时,说明是“a”这种情况,这种情况已经由上一步的“分类属性字符串完全相同”处理了,因此这里没有。
- 两篇论文分类属性长度
l
l
l不同(字符串长度)
- 声明: 我们保证 l x < l y l_x<l_y lx<ly,如正好相反,则交换 x , y x,y x,y。
- l x = = 1 l_x==1 lx==1 and l y = = 3 l_y==3 ly==3:边长为1,距离为2,相似度为1/2。
- l x = = 1 l_x==1 lx==1 and l y = = 5 l_y==5 ly==5:边长为2,距离为4,相似度为1/4。
-
l
x
=
=
3
l_x==3
lx==3 and
l
y
=
=
5
l_y==5
ly==5:
- 两篇论文是父子关系: 边长为1,距离为2,相似度为1/2。
- 两篇论文不是父子关系:边长为3,距离为6,相似度为1/6。
- 两篇论文分类属性长度
l
l
l相同(字符串长度)
至此,所以的情况我们已经都讨论完了,只要按照这个编写代码即可完成。
def fun():
X,Y=input().split()
X=int(X)
Y=int(Y)##ID i
tx=listid[X].split(",")
ty=listid[Y].split(",")
if(tx[1]==ty[1]):
return 1
XT=tx[1][0]
YT=ty[1][0]
#print(XT,YT)
if(XT!=YT):
return 0##直接返回相似度,不是距离,距离算无穷大
else :
lx=len(tx[1])
ly=len(ty[1])
if(lx>ly):##保证lx<ly
tt=lx
lx=ly
ly=tt
tt=tx
tx=ty
ty=tt##数值和字符串都换回来
if(lx==ly):
if(lx==1):
return 1/1
elif(lx==3):
return 1/4
else:
#print(lx)
xx=tx[1]
yy=ty[1]
mx=xx.split(".")[1]
my=yy.split(".")[1]##找出第二位
if(mx==my):
return 1/4
else:
return 1/8
else:##lx<ly
if(lx==1)&(ly==3):
return 1/2
elif(lx==1)&(ly==5):
return 1/4
else:
xx=tx[1]
yy=ty[1]
mx=xx.split(".")[1]
my=yy.split(".")[1]##找出第二位
if(mx==my):
return 1/4
else:
return 1/6
调用该函数,输入需要计算的论文id,即可得到结果。
全部代码如下:
# -*- coding: utf-8 -*-
"""
Created on Tue Jun 23 10:33:22 2020
@author: nyy
"""
#import re
def Load():
listid = []
for line in open("treenode.txt","r"): #设置文件对象并读取每一行文件
listid.append(line.replace("\n",""))
#print(listid)
return listid
def fun(listid):
print("请输入论文id:")
X,Y=input().split()
X=int(X)
Y=int(Y)##ID i
tx=listid[X].split(",")
ty=listid[Y].split(",")
if(len(tx[1])==0) or (len(ty[1])==0):
return 0
if(tx[1]==ty[1]):
return 1
XT=tx[1][0]
YT=ty[1][0]
#print(XT,YT)
if(XT!=YT):
return 0##直接返回相似度,不是距离,距离算无穷大
else :
lx=len(tx[1])
ly=len(ty[1])
if(lx>ly):##保证lx<ly
tt=lx
lx=ly
ly=tt
tt=tx
tx=ty
ty=tt##数值和字符串都换回来
if(lx==ly):
if(lx==1):
return 1/1
elif(lx==3):
return 1/4
else:
#print(lx)
xx=tx[1]
yy=ty[1]
mx=xx.split(".")[1]
my=yy.split(".")[1]##找出第二位
if(mx==my):
return 1/4
else:
return 1/8
else:##lx<ly
if(lx==1)&(ly==3):
return 1/2
elif(lx==1)&(ly==5):
return 1/4
else:
xx=tx[1]
yy=ty[1]
mx=xx.split(".")[1]
my=yy.split(".")[1]##找出第二位
if(mx==my):
return 1/4
else:
return 1/6
def main():
lis=Load()
#print(lis)
rere=fun(lis)
print(rere)
main()