蜀汉英雄传部分人物可视化
蜀汉英雄传是一款gba游戏,打游戏的过程中搜集了其中的人物数据,但是没有给出人物归属,所以得处理一下。数据见百度网盘:
https://pan.baidu.com/s/1TMclLvFsnIRdytuZQPBVxA 提取码:cdrw
接下来,数据处理交给Python,可视化交给R!
- Python处理数据
import pandas as pd
import numpy as np
pd.options.display.max_rows=10
heros=pd.read_excel('C:/Users/Administrator/Desktop/University/蜀汉英雄传人物.xlsx')
colnames=['人物','武力','智力','速度','武器','天赋1']+['天赋'+str(i) for i in range(2,10)]
heros.columns=colnames
heros.head()
游戏数据长这样子:

下面就是导入各人物的归属国数据了,这份数据不全,只涵盖了大部分人物归属信息。
country=pd.read_csv('C:/Users/Administrator/Desktop/University/人物归属.txt',header=None,sep='、')
country
这份数据是不规整的,因此需要进一步处理。首先是以第一列为键,进行宽转长;第二是把包含缺失值的行去掉;最后是挑选出含有国家和人物名称信息的列并进行重命名。这里把得到的数据记做belong。
own=pd.melt(country,[0]).dropna().iloc[:,[0,2]]
belong=own.rename({0:'国家','value':'人物'},axis='columns')
belong.head()

最后就是对heros和belong以人物这一列进行内连接,并保存下来,方便R读取作图。
tidy=pd.merge(heros,belong,how='inner',left_on=['人物'],right_on=['人物'])
tidy=tidy.drop_duplicates()
tidy.to_csv('C:/Users/Administrator/Desktop/University/最终数据.csv',index=False)
tidy.head()

- R读取数据并进行可视化
这里自己动手写一个图层geom_sword(),专门用来画剑。
图层的具体画法可以参见ggplot2的官方说明文档:extending-ggplot2,详情见网址:
https://cran.r-project.org/web/packages/ggplot2/vignettes/extending-ggplot2.html
写图层还是需要一些grid包的绘图知识的,这里便用grid包绘制剑身和剑柄。剑身和剑柄都是多边形,因此选用polygonGrob()函数创建图形对象。
首先是绘制剑身,剑身的填充颜色由gpar()函数里的fill参数控制,这里简单选择灰色。
library(grid)
grid.draw(
polygonGrob(
gp=grid::gpar(fill='grey'),
x=c(1/2,1/3,5/12,7/12,2/3),
y=c(8/9,2/3,1/3,1/3,2/3)))
然后是绘制剑柄,grid包的绘图逻辑和base plot一样,都是遵循“画布”模式。即如果不新建画图页面,后画的图形会叠加在先画的图形上面。
grid.draw(
polygonGrob(
gp=grid::gpar(fill='forestgreen'),
x=c(1/3,1/3,11/24,11/24,
13/24,13/24,2/3,2/3),
y=c(1/3,1/3-1/12,1/3-1/12,1/18,
1/18,1/3-1/12,1/3-1/12,1/3))
)

这样,把剑柄和剑身叠加在一起就得到了一把剑。
基本的图形得到了,接下来做的事情还是比较简单的,因为这几乎都有模板。创建这个图层需要用ggproto()函数定义一个GeomSword和用layer()函数定义一个geom_sword()。对于前者,required_aes、draw_key、default_aes几乎是必须指定的,体现个性化的地方是draw_panel。在这里需要把画出的剑身和剑柄在同一视图画出,最后放到gTree里。对于后者,体现个性化的地方是layer()函数里的geom参数,这里只需要指定前面定义好的GeomSword就行,也就是"geom=GeomSword"。
值得一提的是,由于grid包里的视图可以旋转,于是可在default_aes参数里添加参数angle,通过改变angle的值来实现视图的旋转,从而得到摆放位置不同的剑。
下面是想法的落地实现。
library(grid)
library(ggplot2)
GeomSword<-ggproto('GeomSword',Geom,
required_aes=c('x','y'),
draw_key=draw_key_polygon,
default_aes=aes(fill='white',handle='grey',size=0.1,angle=0),
draw_panel=function(data,panel_params,coord){
coords<-coord$transform(data,panel_params)
#画剑身
Body<-lapply(1:nrow(coords),function(i){
vp=viewport(x=coords$x[i],y=coords$y[i],
width=coords$size[i],height=coords$size[i]*(1+sqrt(5))/2,
angle=coords$angle[i],
just=c('center','center'),
default.units='native')
polygonGrob(vp=vp,
gp=grid::gpar(fill=coords$fill[i]),
x=c(1/2,1/3,5/12,7/12,2/3),
y=c(8/9,2/3,1/3,1/3,2/3))
})
class(Body)<-"gList"
#画剑柄
Handle<-lapply(1:nrow(coords),function(i){
vp=viewport(x=coords$x[i],y=coords$y[i],
width=coords$size[i],height=coords$size[i]*(1+sqrt(5))/2,
angle=coords$angle[i],
just=c('center','center'),
default.units='native')
polygonGrob(vp=vp,
gp=grid::gpar(fill=coords$handle[i]),
x=c(1/3,1/3,11/24,11/24,
13/24,13/24,2/3,2/3),
y=c(1/3,1/3-1/12,1/3-1/12,1/18,
1/18,1/3-1/12,1/3-1/12,1/3))
})
class(Handle)<-"gList"
ggplot2:::ggname('geom_sword',gTree(children=gList(Body,Handle)))
})
geom_sword<-function(mapping=NULL,data=NULL,
stat='identity',position='identity',
na.rm=FALSE,show.legend=NA,
inherit.aes=TRUE,...){
ggplot2::layer(
geom=GeomSword,
data=data,mapping=mapping,
stat=stat,position=position,
show.legend=show.legend,
inherit.aes=inherit.aes,
params=list(na.rm=na.rm,...)
)
}
图层写完后,R读入数据,画图
heros<-read.csv('C:/Users/Administrator/Desktop/University/最终数据.csv',
header=TRUE,sep=',',fileEncoding='utf-8')
ggplot(heros,aes(武力,智力))+
geom_sword(angle=seq(0,360,length=nrow(heros)),aes(fill=速度))+
facet_wrap(vars(国家))
效果如下:

这个图层也有不足之处,比如无法像ggplot2包里的原生图层一样生成相应的图例。尽管如此,这个图层可以分别控制剑身、剑柄,画出一些挺好看的图形。
theta<-seq(0,360,45)[-1]/180*pi
r<-5
x<-r*cos(theta)
y<-r*sin(theta)
test<-data.frame(x=x,y=y,z=theta)
rainbows<-c('#FF0000','#FF7F00','#FFFF00','#00FF00',
'#00FFFF','#0000FF','#8B00FF')
ggplot(test,aes(x,y))+
lims(x=c(-7,7),y=c(-7,7))+
geom_sword(aes(fill=as.character(z)),
angle=seq(0,360,45)[-1]+90,
size=0.2,colour='#625b57')+
scale_fill_manual(values=c(NA,rainbows))+
theme(legend.position = 'none',
axis.text = element_blank(),
axis.ticks = element_blank(),
axis.title=element_blank())

举此一例,其他的就看各位看官的功夫了。
浙公网安备 33010602011771号