大家是否还记得我写的文章《画个小圈圈》?里面吐槽了ggpubr这个包的存在是制造混乱,徒增学习成本。我们知道ggplot2包里有个qplot函数,而在《ggplot2》的第一版中,hadley先用了一章介绍qplot,再介绍grammar of graphics,然后这个qplot就被吐槽,先入为主,给学习grammar of graphics,学习ggplot2语法制造了不必要的麻烦。然后如果你看《ggplot2》第二版,你就会发现qplot这一章被删掉了。我说ggpubr制造混乱,徒增学习成本并不是为了吐槽某公众号,而是实事求是。

我并不反对封装,我并没有强迫症说大家一定要用ggplot2图形语法,而不能封装函数,我想我在《画个小圈圈》已经说得很清楚了,封装画图的包,按我的观点是要么卖了数据处理的专业知识,要么卖了画图的专业知识。如果一个包做的封装太过于简单,像ggpubr这种,数据必须是用户整理好的data.frame,而画图只是简单的两三个图层的叠加,那么当然可以减少你敲键盘输入,然而影响了初级用户学习ggplot2,复杂的东西,我们当然想要用封装好的函数,等你自己啥都搞懂了,老板都要跟你掀桌子了。简单的东西也可以封装,减少输入嘛,只是特别不适用于ggplot2的图层,简单的设定你写个主题,我都举双手赞成,但两三个图层的简单拼凑我不支持,因为图层是积木,大家学习就是要对积木熟悉,然后才能搭出复杂的东西出来。特别简单的图层叠加正是你熟悉积木的时候,你不多敲点键盘,做为练习,又怎么能做出稍复杂点的东西呢?

我以下面这个ggpubr的例子来讲一下吧。

set.seed(1234)
df <- data.frame( sex=factor(rep(c("f", "M"), each=200)), 
                  weight=c(rnorm(200, 55), rnorm(200, 58)))
head(df)

##   sex   weight
## 1   f 53.79293
## 2   f 55.27743
## 3   f 56.08444
## 4   f 52.65430
## 5   f 55.42912
## 6   f 55.50606

ggdensity(df, x="weight", add = "mean", rug = TRUE, color = "sex", fill = "sex",
          palette = c("#00AFBB", "#E7B800"))

这个图画density,就是geom_density这个图层,下面加个指示位置的地毯线就是用geom_rug

require(ggplot2)
p <- ggplot(df, aes(weight, fill=sex, color=sex)) + 
    geom_density(alpha=.5) + 
    geom_rug()
print(p)

要加上分布的均值之类的统计量,还不容易,随手一算:

require(dplyr)
df2 = group_by(df, sex) %>% summarize(m = mean(weight))
df2

## # A tibble: 2 x 2
##   sex       m
##   <fct> <dbl>
## 1 f      54.9
## 2 M      58.1

然后再用geom_vline加个线条,要搞颜色也是一样。

p + geom_vline(aes(xintercept=m, color=sex), df2, linetype='dashed') + 
    scale_fill_manual(values=c("#00AFBB", "#E7B800")) + 
    scale_color_manual(values=c("#00AFBB", "#E7B800"))

虽然你多输入了很多,但是在不断的输入中,你对各个图层更熟悉了,都是自由组合的组件。而且这还有个前提是ggplot2图层的文档很全,新手学习并不是很费力。所以我也不是说学习是最重要,如果费时费力,你又有产出的压力,比如老板叫你明天把结果放在他桌面上,学个鬼个!黄花菜都凉了。

ggpubr另一方面是组间比较,这个跑个检验还不简单,要标显著嘛,我好多年前的文章《Use ggplot2》已经手把手教你们了,就不重复了。


我知道大家都喜欢少敲点代码,对ggpubr的吐槽一是对学习有负面,二是不中用!今天跟大家一个中用的,比ggpubr强N倍的包。

  • 一键出图,但图不是简单拼图层能够拼出来的
  • 附带做了统计,把统计量都给你在图上标准了

这有什么好处?画图+统计都要做,但一般我们开始只是在探索,而不是最终出要发表文章的图,而这个包,一画图就是图+统计,特别省事/省时间,等于说我毫不费力就可以试不同的图和统计检验。

  • 场景1,你对这些是懂的,那么你不需要花时间去探索,这个包帮你做了。
  • 场景2, 你对这些是不懂的,这是小白福音,你不懂你根本不知道要怎么做,甚至于不知道要做什么,你连学习的方向都没有,但人家帮你做了,你可以有目的性去学习相关的背景。

画图就是在感性(可视化)地探索数据,而统计检验给了你理性(定量)的指标。我们通常需要不断试,各种探索,最后可能都没用,或者有一种是最终要的,这个过程是很花时间的,而这个包,可以让你用很少的时间来探索一下。

ggstatsplot

head(mtcars)

##                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
## Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

比如我想知道汽车的轮子数目和自动波/手波是否有关联,那么二联表走起,然后卡方检验,等你做完一小时过去了。而我用ggstatsplot包,3秒钟搞定:

library(ggstatsplot)
ggpiestats(data = mtcars,
           main = am,                
           condition = cyl) +
  scale_fill_brewer(palette = "Dark2")

实际上你要做多少东西呢:

首先是二联表:

 Contingency Tables             
 ────────────────────────────── 
   cyl      0     1     Total   
 ────────────────────────────── 
   4         3     8       11   
   6         4     3        7   
   8        12     2       14   
   Total    19    13       32   
 ────────────────────────────── 

再都卡方检验:

 χ² Tests                       
 ────────────────────────────── 
         Value    df    p       
 ────────────────────────────── 
   χ²     8.74     2    0.013   
   N        32                  
 ────────────────────────────── 

图上还有一些你可能不懂的东西,比如:

 Nominal                      
 ──────────────────────────── 
                      Value   
 ──────────────────────────── 
   Phi-coefficient      NaN   
   Cramer's V         0.523   
 ──────────────────────────── 

省了多少时间?如果我只是看一看,最终这个结果我不要,那只花了3秒,如果这是我要的,我不懂卡方的情况下,也能做出来,那么我再有的放矢地去学习,甚至可以自己手工来一遍,这比毫无目的在各种手工学习或试错强太多,OK,我也懂用卡方的情况下,它做的东西,里面也可能有我不懂的东西,比如这个Cramer’s V,就打在图上,不懂,又可以去学习了,学习使我快乐,如果不是在图上看到,我不要说看不懂,根本都不知道有这东西的存在!所以不单单好用,也能让我们学到新东西。最重要的是老板说你先试一下,明天给我看个初步的结果,bingo,今晚可以愉快地睡觉!

组间比较

ggbetweenstats(data = iris, 
               x = Species, 
               y = Sepal.Length)

组间比较还有生物狗最喜欢的标注均值,不管是用点和/或数字都行。

相关性

ggscatterstats(data = iris, 
               x = Sepal.Length, 
               y = Petal.Length,
               title = "Dataset: Iris flower data set")

这里展示的只是最简单的,有很多参数,比如marginal.type = "density"那么marginal画出来的就不是histogram,而是density curve。也可以是boxplot哦。

相关性矩阵

ggcorrmat(
   data = subset(iris, Species == "versicolor"),
   cor.vars = c(Sepal.Length, Sepal.Width, Petal.Length, Petal.Width))

分组做图并拼图

像上面的相关性矩阵,我只是切了versicolor一个物种来画,iris数据我们知道有三个物种,我们可以一个一个做,然后用cowplot来拼图。

下面我先定义一个函数来画图:

plot_fun = function(data) {
  ggscatterstats(
  data = data,
  x = Sepal.Length,
  y = Sepal.Width,
  marginal.type = "boxplot",
  title =
    glue::glue("Species: {(data$Species)} (n = {length(data$Sepal.Length)})")
  )
}

然后分组做图:

library(tidyr)
library(purrr)

nested_df <- iris %>% 
  group_by(Species) %>% 
  nest() %>% 
  mutate(p = map(data, plot_fun))   

这样就有三张图,用cowplot当然很容易拼,但我们经常需要拼完再加个title, 再底部加个注释什么的。你当然可以自己再画个tible什么的,然后再去拼。而ggstatsplot直接封装了cowplot,并提供了一些参数,让你加标题和注释什么的。比如上面画的三张图,我用combine_plots拼出来:

combine_plots(
  plotlist = nested_df$p,
  labels = c("(a)", "(b)", "(c)"),
  nrow = 3,
  ncol = 1,
  title.text = "Relationship between sepal length and width for all Iris species",
  title.size = 14,
  title.colour = "blue",
  caption.text = expression(
    paste(
      italic("Note"),
      ": Iris flower dataset was collected by Edgar Anderson."
    ),
    caption.size = 10
  )
)

安装

安装要写到最后,因为我知道大家看到这里,才会急于想要来一波走你。目前此包还没在CRAN上,不过作者已经准备提交到,所以很快就会在CRAN上。给大家安装和升级带来便利。

目前可以通过github安装:

devtools::install_github("IndrajeetPatil/ggstatsplot")