蜀汉英雄传部分人物可视化

  蜀汉英雄传是一款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())

  举此一例,其他的就看各位看官的功夫了。

 

posted on 2020-06-15 11:28  纵横二剑  阅读(792)  评论(0)    收藏  举报