2019第23周:评《R语言数据可视化之美:专业图表绘制指南》

--- 大师,大师,618买的什么书呀,好奇呢。
··· 一本R语言绘图的书,可花了呢。

"Beautiful Visualization with R" provides a comprehensive introduction to the many types of visualisation that you can create with R. As well as foundational visualisations like bar charts, scatterplots, and histogram, this book will teach you about more complex tools like 3d plots, ternary plots, polar coordinate plots and more! Read this book to learn how to create visually compelling plots that help you understand your data. ——Hadley Wickham RStudio 首席科学家,莱斯大学统计系助理教授,ggplot2等软件包开发者

公元2019年5月底得知出版消息,旋即下单,约莫6月5日拆封,开阅。太贵,开卷有益。于端午假期草过一遍,略有所得,以飨读者。

本书基本上分为三个部分:R语言编程与绘图基础、可视化实战和论文中学术图表的升级技能(tips),其中第二部分是大量的案例,按照绘图类型不同来区分。窃以为这样的安排挺好的,我们学习可视化技能基本上是靠案例来学习的。其实R语言本身也倡导这种case-study的方式,君不见R里面充满了而各种example。

首先,我们为什么要可视化?因为我们想把数据变现:一图胜千言。而且一张图上能表征的信息越多越好,当然是在人类的理解范围内。一张图基本的元素就是点线面以及大小形状颜色,一张好的图就是合理地组合这些元素,做的比较好的当然是ggplot2的图形语法了。本书也是基于ggplot2语法来的,很多图是由ggplot2及其扩展包实现的。读者朋友用R做可视化一定看过《ggplot2:数据分析与图形艺术》一书咯,这是一本圣经级别的书,偏理论一点,例子尽可能的简单。

相比之下,这本书就更加偏向应用了,例子给的详尽也尽量做到了精美。所以叫他指南也是有一定的道理的。这里举几个例子。

#EasyCharts团队出品,
#如需使用与深入学习,请联系微信:EasyCharts

#---------------------------------------图5-1-2核密度估计峰峦图--------------------------------------
library(ggplot2)
library(ggridges)
library(RColorBrewer)

ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = `Month`, fill = ..density..)) + 
  geom_density_ridges_gradient(scale = 3, rel_min_height = 0.00,size = 0.3) + 
  scale_fill_gradientn(colours = colorRampPalette(rev(brewer.pal(11,'Spectral')))(32))

###------------------------------自定义编写代码实现:图5-1-2核密度估计峰峦图-------------------------------
library(reshape2)

colormap <- colorRampPalette(rev(brewer.pal(11,'Spectral')))(32)

dt<-lincoln_weather[,c("Month","Mean Temperature [F]")]
splitdata<-split(dt,dt$Month)
xmax<-max(dt$`Mean Temperature [F]`)*1.1
xmin<-min(dt$`Mean Temperature [F]`)*1.1


N<-length(splitdata)
labels_y<-names(splitdata)

mydata<-data.frame(x=numeric(),y=numeric(),variable=numeric()) #创建空的Data.Frame

for (i in 1:N){
  tempy<-density(splitdata[[i]][2]$`Mean Temperature [F]`,bw = 3.37,from=xmin, to=xmax)
  newdata<-data.frame(x=tempy$x,y=tempy$y)
  newdata$variable<-i
  mydata<-rbind(mydata,newdata)
}

Step<-max(mydata$y)*0.6
mydata$offest<--as.numeric(mydata$variable)*Step
mydata$V1_density_offest<-mydata$y+mydata$offest

p<-ggplot()
for (i in 1:N){
  p<-p+ geom_linerange(data=mydata[mydata$variable==i,],aes(x=x,ymin=offest,ymax=V1_density_offest,group=variable,color=y),size =1, alpha =1) +
    geom_line(data=mydata[mydata$variable==i,],aes(x=x, y=V1_density_offest),color="black",size=0.5)
}

p+scale_color_gradientn(colours=colormap,name="Density")+
  scale_y_continuous(breaks=seq(-Step,-Step*N,-Step),labels=labels_y)+
  xlab("Mean Temperature [F]")+
  ylab("Month")+
  theme_classic()+
  theme(
    panel.background=element_rect(fill="white",colour=NA),
    panel.grid.major.x = element_line(colour = "grey80",size=.25),
    panel.grid.major.y = element_line(colour = "grey60",size=.25),
    axis.line = element_blank(),
    text=element_text(size=15,colour = "black"),
    plot.title=element_text(size=15,hjust=.5),
    legend.position="right"
  )

#EasyCharts团队出品,
#如有问题修正与深入学习,可联系微信:EasyCharts

library(ggplot2)
library(grid)
library(RColorBrewer)
library(dplyr)
install.packages("SuppDists")
library(SuppDists) #提供rJohnson()函数

# somewhat hackish solution to:
# https://twitter.com/EamonCaddigan/status/646759751242620928
# based mostly on copy/pasting from ggplot2 geom_violin source:
# https://github.com/hadley/ggplot2/blob/master/R/geom-violin.r

"%||%" <- function(a, b) {
  if (!is.null(a)) a else b
}

color<-brewer.pal(7,"Set2")[c(1,2,4,5)]


geom_flat_violin <- function(mapping = NULL, data = NULL, stat = "ydensity",
                             position = "dodge", trim = TRUE, scale = "area",
                             show.legend = NA, inherit.aes = TRUE, ...) {
  layer(
    data = data,
    mapping = mapping,
    stat = stat,
    geom = GeomFlatViolin,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params = list(
      trim = trim,
      scale = scale,
      ...
    )
  )
}


GeomFlatViolin <-
  ggproto("GeomFlatViolin", Geom,
          setup_data = function(data, params) {
            data$width <- data$width %||%
              params$width %||% (resolution(data$x, FALSE) * 0.9)
            
            # ymin, ymax, xmin, and xmax define the bounding rectangle for each group
            data %>%
              group_by(group) %>%
              mutate(ymin = min(y),
                     ymax = max(y),
                     xmin = x,
                     xmax = x + width / 2)
            
          },
          
          draw_group = function(data, panel_scales, coord) {
            # Find the points for the line to go all the way around
            data <- transform(data, xminv = x,
                              xmaxv = x + violinwidth * (xmax - x)) #利用transform函数为数据框mydata增加数据
            
            newdata <- rbind(plyr::arrange(transform(data, x = xmaxv), -y),plyr::arrange(transform(data, x = xminv), y))
            newdata_Polygon <- rbind(newdata, newdata[1,])
            newdata_Polygon$colour<-NA
            
            newdata_Path <- plyr::arrange(transform(data, x = xmaxv), -y)
            
            ggplot2:::ggname("geom_flat_violin", grobTree(
              GeomPolygon$draw_panel(newdata_Polygon, panel_scales, coord),
              GeomPath$draw_panel(newdata_Path, panel_scales, coord))
            )
          },
          
          draw_key = draw_key_polygon,
          
          default_aes = aes(weight = 1, colour = "grey20", fill = "white", size = 0.5,
                            alpha = NA, linetype = "solid"),
          
          required_aes = c("x", "y")
  )

# "%||%" <- getFromNamespace("%||%", "ggplot2")
# "%>%"  <- getFromNamespace("%>%", "magrittr")

set.seed(141079)

# Generate sample data -------------------------------------------------------
#findParams函数参考:https://github.com/hadley/boxplots-paper

findParams <- function(mu, sigma, skew, kurt) {
  value <- .C("JohnsonMomentFitR", as.double(mu), as.double(sigma),
              as.double(skew), as.double(kurt - 3), gamma = double(1),
              delta = double(1), xi = double(1), lambda = double(1),
              type = integer(1), PACKAGE = "SuppDists")
  
  list(gamma = value$gamma, delta = value$delta,
       xi = value$xi, lambda = value$lambda,
       type = c("SN", "SL", "SU", "SB")[value$type])
}

# 均值为3,标准差为1的正态分布
n <- rnorm(100,3,1)
# Johnson分布的偏斜度2.2和峰度13
s <- rJohnson(100, findParams(3, 1, 2., 13.1))
# Johnson分布的偏斜度0和峰度20)
k <- rJohnson(100, findParams(3, 1, 2.2, 20))
# 两个峰的均值μ1,μ2分别为1.89和3.79,σ1 = σ2 =0.31
mm <- rnorm(100, rep(c(2, 4), each = 50) * sqrt(0.9), sqrt(0.1))

mydata <- data.frame(
  Class = factor(rep(c("n", "s", "k", "mm"), each = 100),
                 c("n", "s", "k", "mm")),
  Value = c(n, s, k, mm)+3
)
#-------------------------------------------------------------

colnames(mydata)<-c("Class", "Value")

d <- group_by(mydata, Class) %>%
  summarize(mean = mean(Value),
            sd = sd(Value))

plot1<-ggplot(mydata, aes(Class, Value, fill=Class))  +
  geom_flat_violin(position=position_nudge(x=.2)) +
  geom_jitter(aes(color=Class), width=.1) +
  geom_pointrange(aes(y=mean, ymin=mean-sd, ymax=mean+sd),
                  data=d, size=1, position=position_nudge(x=.2)) +
  coord_flip() + 
  theme_bw() +
  theme( axis.text = element_text(size=13),
         axis.title =  element_text(size=15),
         legend.position="none")

plot2<-ggplot(mydata, aes(x=Class, y=Value))  +
  geom_flat_violin(aes(fill=Class),position=position_nudge(x=.25),color="black") +
  geom_jitter(aes(color=Class), width=0.1) +
  geom_boxplot(width=.1,position=position_nudge(x=0.25),fill="white",size=0.5) +
  coord_flip() + 
  theme_bw() +
  theme( axis.text = element_text(size=13),
         axis.title =  element_text(size=15),
         legend.position="none")
library(gridExtra) 
grid.arrange(plot1,plot2,ncol = 2, nrow = 1)
云雨图

作为一本以案例为主的指南书,非常适于我们可视化初学者,因为我们很多时候不知道原来数据还可以这样展示。所以对于资历一般的我们,看到才可能想到,想到才可能做到,做到才可能得到,得到才可能失去,失去才可能知道适不适合自己。

任何一张图都离不开具体的实际问题,有时候理解实际问题要比绘图难的多,数据是连续的还是离散的,离散的用点,连续的用线,把自己的数据分到以下类别中能更好地帮助我们选择相应的绘图类型。

结合案例可以思考哪些类型的数据我是可以用这个图来展示的,要做这张图需要什么样的数据类型呢?可视化的大部分工作是在整理数据,可以感觉到本书的作者也是个tidyverse语法的爱好者。当然这不是一本数据处理的书,不是《R语言数据科学》所以我们主要关心的还是每张图的细节修饰,也许在大量的实践之后,你会觉得作者做的远远不够,也采用了很多默认的参数,但是操千曲而后笑声,观千剑而后识器。通过执行作者的代码。也可以学到不少R编程的知识,当然了如果你已经是大牛了也许就没有兴趣看这本书了。

虽然本书的代码和数据都是公布在github的要运行不必买书的。碰到我是一个喜欢积累的人也喜欢做一些摘抄,有时候遇到惊艳的绘图代码也会记在ggplot2的那本书上,但是由于他偏理论,很多类型的图表没有涉及,以至于有的句子不知道摘抄在哪。有了这本书好了,R语言绘图代码有地方放了。包括但不限于R语言可视化,可以把自己处理数据学术绘图的心得体会写在相应的章节,以后遇到类似的问题就可以参考解决。这不失是一个比较笨的方法,学习要有学生思维嘛。

我还是比较怀念当时拿着铅笔在纸上写代码的年纪呢!

有两点书中没有涉及一个是空间数据,比如地图,另一个关系型数据可视化主题网络图(Network)没有涉及,使我觉得这本书是不完整的,至少不是那么完整。然后是多维数据可视化中heatmap做的有些草草了,就是把pheatmap的example拿过来演示一下,没有之美之说。柱形图、散点图、热图这三种图在学术文章中是最常见的,其中热图可以用来描述类别比较(分组)、数据关系(相关性)、数量关系(丰度),所以他的席位不应该排这么少。

在阅读的过程中我产生一种想法就是:科研绘图亦应该有分级制度:学术文章级别以及学术报告级别。这两个在主题设置、颜色配置要求是不一样的。本书关于学术图表主题设置的讨论我觉得也是有益的。

代码在这||Beautiful-Visualization-with-R

韩愈《答李翊书》:“始者非三代两汉之书不敢观.非圣人之志不敢存,处若忘,行若遗,俨乎其若恩,茫乎其若迷,当其取予心而注于手也,惟陈言之务去,戛戛乎其难哉!”

《樊绍述墓志铭》:“必出于己,不蹈袭前人一言一句,又何其难也。”

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容