数据可视化练习——会议论文收录信息可视化与分析
一、问题简介
本文的内容围绕网站http://openaccess.thecvf.com/CVPR2018.py中所列的文献信息进行。该网站中可获取2018年的CVPR 论文的开放版本(由计算机视觉基金会提供)。对于这个网页上的论文标题与作者信息,需要通过爬虫技术将其整理在文本中,并对其进行预处理,包括分词、词频统计等。之后,需要对研究热点词汇、高产作者、论文数量、论文标题长度等信息进行统计分析,并将分析结果进行可视化展示和说明。
在本文中,我使用了八种不同的可视化方案对上述信息进行了不同角度的展示和分析,具体在后文中进行阐述。
二、数据的获取和预处理
2-1使用爬虫获取文本
我使用bs4、lxml和requests库对网页上的信息进行爬取,其中rewquests库负责对网页进行访问,bs4和lxml库对网页进行解析。最后将获得的信息存储到csv文件中,如图2-1、图2-2和图2-3所示。
图2-1 通过爬虫获得的作者信息
图2-2通过爬虫获得的标题信息 图2-3作者数量和标题长度
2-2数据预处理
获取了网页上的原始数据后,接下来对其进行分词和词频统计的处理。对标题词汇进行分词和词频统计的核心代码如下。其中我选取了一些常用的无用词汇进行剔除。
hamletTxt = getText() #得到原始数据
words = hamletTxt.split() #对原始数据按空格分隔
counts = {} #定义存储词频的字典
#创建需要剔除的单词列表
remove_words = ['for', 'and', 'with', 'a', 'of','in',
'from','to','the','using','by']
#进行计数
for word in words:
if word in remove_words:
continue
else:
counts[word] = counts.get(word, 0) + 1
#将字典存储到csv文件中
pd.DataFrame(counts,index=[0]).to_csv('hotword.csv')
得到处理后的词频数据文件如图2-4所示。
图2-4处理后得到的词频
对作者信息进行分词和词频统计的核心代码如下。
hamletTxt = getText2() #得到原始数据
words = hamletTxt.split(',') #根据‘,’将作者名分开
counts = {} #定义存储词频的字典
#进行计数
for word in words:
counts[word] = counts.get(word, 0) + 1
# 将字典存储到csv文件中
pd.DataFrame(counts,index=[0]).to_csv('hotauthor.csv')
得到处理后的作者词频数据文件如图2-5所示。
图2-5处理后得到的作者词频
三、可视化展示和分析
3-1 热门词汇统计分析
对于热门词汇的展示,我采用了matplotlib库和wordcloud库,可视化方案是利用柱状图展示出现频率前十的词,然后使用词云图显示出现次数较多的40个关键词。
实现柱状图和词云的核心代码如下。
items = list(counts.items()) #将词频字典转换为二维数组
items.sort(key=lambda x: x[1], reverse=True) #根据词频进行由高到低排序
#画柱状图
fig, ax = plt.subplots() #创建图形
x=[] #定义x轴的值和y轴的值
height=[]
for i in range(10): #遍历频率前十的词汇
a,b =items[i]
x.append(a)
height.append(b)
ax.bar(x,height,0.5,edgecolor="blue",color="yellow") # 创建柱状图
ax.set_title('majority words') # 设置title和labels
ax.set_xlabel('word')
ax.set_ylabel('count')
plt.xticks(x, rotation=30)
for i in range(10): #标出具体频数
plt.text(i-0.25,height[i]+3,height[i])
plt.show()
#画词云图
wordcloud = WordCloud( #创建词云图
background_color="white", #设置背景为白色
width=1500, #设置图片的宽度
height=960, #设置图片的高度
margin=10 , #设置图片的边缘
min_font_size=10,
max_font_size=220,
max_words=50,
).fit_words(counts)
plt.imshow(wordcloud,interpolation='bilinear') # 绘制图片
plt.axis("off") # 消除坐标轴
plt.show()
程序运行出来的效果如图3-1和图3-2。
图3-1最热门的10个关键词
图3-2出现次数较多的40个关键词
图3-1和3-2都展示了热门词汇,但差别很大。从柱状图当中,我们能够清晰的看出每个热门词汇出现的次数,并且很容易的比较它们之间的差别,效果一目了然。而从词云图中,我们发现它使用了多种视觉通道:面积、颜色、甚至形状(我这里的词云图是简单的矩形)。而且它可以显示的词汇更多,如果在柱状图中一次显示40个词汇,则会导致x轴以及数字标签根本看不清,在词云图中却可以较好的显示。但是,词云图显然不能显示出每个关键词出现的次数,也很难将不同词汇进行比较,如课堂上所说,面积的视觉通道很难直观的进行比较。所以,我个人感觉词云图在科学数据的可视化中没有什么用处,可以放在PPT中起强调作用。
3-2 高产作者统计分析
这里使用了与3-1相同的库。我同样选取了10个出现次数最多的高产作者进行展示。考虑到作者名较长,使用的可视化方案为横向的条形图以及词云图。因为核心代码与热门关键词统计类似,只是将调用的bar函数换成了barh,此处不再赘述。测试效果如图3-3和图3-4所示。
图3-3最热门的十个作者
图3-4出现次数较多的50个关键词
同样,柱状图可以很好的显示作者的出现次数和作者之间的对比,而词云图可以清楚的显示更多的作者,却无法得到具体的数值,也很难进行对比。
3-3 论文标题长度统计分析
对于所有论文的标题,我使用饼图来展示在各个标题长度区间内的文章所占的比例。这里我使用pyecharts库进行实现,pyecharts 是一个用于生成 Echarts 图表的类库。Echarts 是百度开源的一个数据可视化 JS 库。用这个库生成的玫瑰型饼图具有交互功能,其核心代码如下。调用接口时采用链式调用。
columns = ["30以下", "30~50", "51~70",
"71~90", "91~110", "110以上"]
hamletTxt = getText3() #原始数据,所有文章的标题长度
words = hamletTxt.split() #用空格分隔
data = [0 for x in range(0, 6)] #将长度分为六个区间
for word in words:
if int(word)< 30:data[0]+=1
if int(word)>=30 and int(word)<51:data[1]+=1
if int(word) >= 51 and int(word) < 71:data[2] += 1
if int(word) >= 71 and int(word) < 91:data[3] += 1
if int(word) >= 91 and int(word) < 111:data[4] += 1
if int(word)>= 111:data[5]+=1
#生成饼图
c = ( Pie()
.add(
"",
[list(z) for z in zip(columns, data)],
radius=["30%", "75%"],
center=["35%", "50%"],
rosetype="radius",
)
.set_global_opts(title_opts=opts.TitleOpts(title="Statistics of title length"))
)
c.render("Pie.html")
打开生成的html文件,将鼠标放到橙黄色区域上,显示出标题长度在71到90的文章有246个。效果如图3-5所示。
图3-5文章标题长度所占比重
由该图可以清晰的看出不同区间的标题长度所占的比重,饼图的优势就是更方便观察占比情况,而玫瑰型饼图可以更夸张的表现出占比重大的部分。可以看出标题长度在51到70之间的文章最多,占了43.92%,而长度在110以上的文章是最少的。个人感觉饼图在该问题下是最适合表现标题的长度分布情况的,相较柱状图等有一定优势。
3-4 含有热点词汇的标题的平均长度统计分析
在已经统计了热点词汇和标题长度以后,可以探寻这两者之间的关系。我使用折线图,来表现热点词汇的数量及其平均标题长度。画图使用matplotlib,统计平均标题长度的核心代码如下,titlelengh数组和titles数组的元素是一一对应的。
由上文代码可得热点词数组word及其频数数组counts,标题长度数组titlelength
ave=[0 for x in range(0, 10)] #存储每个热点词平均标题长度
for title in titles: #遍历所有标题
for i in range(10): #查找该标题是否含有热点词汇
if word[i] in title: #如果有
j=titles.index(title) #获取该标题的索引
ave[i]+=int(titlelength[j]) #累加上该标题长度
for i in range(10):
ave[i]=round(ave[i]/count[i],2) #算平均长度,保存两位小数
fig, ax = plt.subplots()
ax.plot(word,ave,marker='*', ms=10) #画图
后面的代码为设置标题等,与上文重复,不再赘述
热点词汇的数量已经通过柱状图表现过,这里再用折线图和平均标题长度一起展现出来。效果如图3-6。
图3-6每个热点词汇的次数和平均标题长度
这样我们可以直观的看到有关每个热点词汇的两个属性:出现的次数和平均标题长度。可以发现含有network的92个标题的平均长度最大,其他的相差不多。对于表现形式,原本我是想用三维图来展示,后来发现确实如老师所说,三维图根本不能清楚的观察属性值,如果采用三视图的方法对三维图截图进行展示,则截出来的图片就是三张二维图,其中有一张还没有意义(当两个坐标为分别热点词数量和标题长度时),所以这里采用两个二维图是最好的。可以想象,其实大多数情况下静态的三维图都没有太大意义。对于折线图,原本的优势为可以更好的表现变化,在这里单纯的表示高度也是可以的,可以清晰的看到属性的值,这就是我们想要的。
3-5 四位高产作者所用热点词汇情况的统计分析
我对最高产的四位作者使用热点词汇的情况进行了统计。采用的可视化方案是堆叠图,实现过程中的关键点是作者数组中的元素和标题数组中的元素是相同顺序的。即第n个作者组所写的文章标题就是标题数组中的第n个元素。核心代码如下。
通过上文代码可得最高产的四位作者hotauthor数组、前十热门词汇hotword数组
titles=Txt1.split('\n\n') #从原始数据获得标题数组
authors=Txt2.split('\n') #从原始数据获得作者数组
hotword3author=np.zeros((4,10)) #定义最终结果矩阵
for author in authors: #遍历所有作者
for i in range(4):
if hotauthor[i] in author: #如果发现热门作者
index=authors.index(author) #得到该作者的位置索引
title= titles[index] #得到该文章的标题
for j in range(10):
if hotword[j] in title: #若发现标题中有热门词汇
hotword3author[i][j]+=1 #计数
fig,ax = plt.subplots() #画图
data = hotword3author
color_list = ['b', 'g', 'r','y']
mybar=[]
for i in range(4): #需要绘制四次柱状图
mybar.append(ax.bar(hotword, data[i], #一次绘制矩阵中的一行
width=0.35,
bottom=np.sum(data[:i], axis=0),
color=color_list[i],
alpha=0.5
))
后面的代码为设置标题等,与上文重复,不再赘述
运行程序,效果如图3-7所示。
图3-7四位高产作者使用热点词汇的情况
从这个问题中,想要传达的信息有两个:1.对于四个作者总体而言,每个热点词的使用次数。2.每一个作者使用热点词汇的情况。一开始我计划用系列柱状图来表现,但是后来发现并不能清楚的表现第一个信息,无法直观的对比总体下不同热点词的使用差别。后来经过考虑,发现堆叠图在这里是最好的选择。从图中可以清晰的看出每个词一共使用了多少次、每个作者分别用了多少次,个人感觉效果很好。
3-6 作者关系统计分析
要想统计作者之间的合作关系,最难的地方在于构建一个作者关系矩阵。因为作者总量太多,我这里选取了发表文章超过五篇的“活跃作者”进行统计,共有78位。构建关系矩阵的思想就是对于矩阵的每一种关系,都在原始数据中进行查找,存在则计数加一,核心代码如下。
origindata为从数据文件中读到的原始作者数据
edge = 78
matrix = [['' for j in range(edge)] for i in range(edge)]#定义矩阵
for row in range(1, len(matrix)): #遍历矩阵
for col in range(1, len(matrix)):
if matrix[0][row] == matrix[col][0]: #作者自己和自己没有关系
matrix[col][row] = 0
else:
counter = 0 #定义计数器
for ech in origindata: #遍历原始数据
if matrix[0][row] in ech and matrix[col][0] in ech:
counter += 1 #存在关系,计数
else:
continue
matrix[col][row] = counter
#最终matrix为关系矩阵
得到关系矩阵matrix后,使用pyecharts库绘制网络关系图。主要代码如下。
nodes = []
for i in range(78): #创建78个节点
sum=0 #sum为合作数,决定节点大小
for j in range(78):
sum+=int(result_matrix[i+1][j+1])
nodes.append((opts.GraphNode(name=authornames[i], symbol_size=sum+2,label_opts= {"normal": {"color":"blue"}})))
links = []
for i in range(78): #创建连接
for j in range(78):
if int(result_matrix[i+1][j+1])!=0:
links.append(opts.GraphLink(source=set_key_list[i], target=set_key_list[j],value=result_matrix[i][j]))
c = (Graph() #绘图
.add("", nodes, links, repulsion=100,is_focusnode=False,layout= "force",
linestyle_opts=opts.LineStyleOpts(width=1.1, curve=0),
label_opts=opts.LabelOpts(is_show=True))
.set_global_opts(title_opts=opts.TitleOpts(title="Author network"))
)
c.render("Network.html")
运行程序后得到的关系图如图3-8所示。
图3-8活跃作者的合作网络图
由网络图可以清晰的看到任意两个作者之间的合作关系,也可以容易的看出有哪些合作的小团体。整体看起来简单易懂,而且信息量较多。唯一的问题是无法获取每两个作者之间合作的具体次数,为此,我设计了热力图来对前20的高产作者之间的合作关系进行展示。使用的是seaborn库,下面的data是从关系矩阵中截取的子矩阵。
df2 = pd.DataFrame(data,index = hotauthor, columns = hotauthor)
plt.figure(figsize=(20, 20))
plt.title('Pearson Correlation of Features', y=1, size=12)
sns.heatmap(df2.astype(float), linewidths=0.1,
square=True, linecolor='white', annot=True)
plt.xticks(rotation=90,fontsize=7) # 将字体进行旋转
plt.yticks( rotation=360, fontsize=7)
plt.show()
程序运行的结果如图3-9。
图3-9活跃作者的合作热力图
从图中可以看到,有两位作者合作了9次,所有作者间的合作次数都可以简单的获取。但是它与网络图相比,没有那么简单易懂,并且因为很多作者之间没有过合作,导致大量了0充斥了空间,使得传达的信息量减少。这里只表现了前二十的高产作者,而图3-8的网络图表现了78位作者的关系。因此热力图和网路图在表现人物关系的问题中是各有利弊的。