R tips:使用{}自由的使用管道

在R语言中可以使用magrittr包的%>%进行管道操作。

管道操作可以减少中间变量的产生,并且使得多层嵌套的函数逻辑更加清晰。

一个管道操作的示意如下:

library("magrittr")
library("tidyverse")
# 使用管道:
rnorm(10) %>% sort() %>% plot

# 如果不使用管道,函数嵌套变多,难以阅读
plot(sort(rnorm(10)))
# 或者产生很多中间变量
x <- rnorm(10)
y <- sort(x)
plot(y)

magrittr的管道操作符里面有多个:%>%, %$%, %T>%, %<>%,最常用的就是%>%操作符,它将操作符之前的运算结果传递给操作符之后的函数的第一个参数。

由于管道操作传递结果的特殊性——传递给第一个参数(在管道操作符后一个函数中使用.来表示此结果),因此并不是所有的函数都可以使用管道操作,比如base R中的一系列字符串函数:grep、sub等等,其第一个参数往往不是前一步的运算结果,这个时候就无法直接使用%>%操作符进行管道操作了。

管道操作也不适用同一个步骤需要同步进行多个操作的情况。

幸好这些可以通过{}来解决。

base R函数也可以使用管道

并不是所有的base R函数都不支持管道,那些第一个参数往往不是前一步的运算结果的函数才不支持。

此外,还有一些函数是不支持管道的:使用当前环境的函数,因为管道操作符会新建一个环境,因此操作当前环境的函数实际上在操作管道符创建的新环境,这很可能不是你想要的;使用惰性求值的函数:如try、tryCatch等等。

这些函数使用{}就可以正常进行管道操作了,示例如下:

mtcars %>% head %>% { colnames(.) <- 1:ncol(.); . }
#                     1 2   3   4    5     6     7 8 9 10 11
#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

{}代表创建了一个新环境,操作符上一步的运算结果在此环境中使用.来表示,上例是对传进来的数据框的列名进行修改,修改为列号。

colnames函数就是一种典型的无法直接使用管道操作符的函数。

需要注意的是,由于colnames函数运算完成后不会返回其操作的数据框,所以需要最后使用.显式的将结果传出{}环境。

一旦熟悉了{}和.的配合,那么这种操作方式就可以让管道变得非常灵活,相比较而言虽然其不如普通管道显得整洁,但是只要多用几次,就可以很容易的理解适应这种用法。

mtcars %>% head %>% { 
  colnames(.) <- 1:ncol(.)
  rownames(.) <- paste0("row"1:nrow(.))
  .
}
#        1 2   3   4    5     6     7 8 9 10 11
#row1 21.0 6 160 110 3.90 2.620 16.46 0 1  4  4
#row2 21.0 6 160 110 3.90 2.875 17.02 0 1  4  4
#row3 22.8 4 108  93 3.85 2.320 18.61 1 1  4  1
#row4 21.4 6 258 110 3.08 3.215 19.44 1 0  3  1
#row5 18.7 8 360 175 3.15 3.440 17.02 0 0  3  2
#row6 18.1 6 225 105 2.76 3.460 20.22 1 0  3  1

{}后面可以继续进行管道操作:

mtcars %>% head %>% { colnames(.) <- 1:ncol(.); . } %>% slice(1)
#   1 2   3   4   5    6     7 8 9 10 11
#1 21 6 160 110 3.9 2.62 16.46 0 1  4  4

更好用的ggplot2

ggplot2只能支持一般的管道操作,只支持传入,不支持传出。但是使用了{}就可以不受此限制。

这里其实是以ggplot2举例,其实并不局限于ggplot2,{}在需要单步执行多个命令时是很有效的。下面是一个示例:

iris %>% {
  # 正常绘图
  ggplot(., aes(x = Species, y = Sepal.Length, fill = Species)) +
    geom_boxplot() -> p1

  # 添加部分文字图层的图
  # geom_text的data参数可以对.进行运算
  ggplot(., aes(x = Sepal.Length, y = Sepal.Width)) +
    geom_point(aes(color = Species)) +
    geom_text(data = top_frac(., n = 0.1, wt = Sepal.Width), 
              aes(label = Sepal.Width)) -> p2

  # 绘制第三张图用于拼图
  ggplot(., aes(x = Petal.Length, fill = Species)) +
    geom_histogram() -> p3

  require(patchwork)
  (p1|p2)/p3


由于{}内是新建环境,所以临时变量p1、p2、p3并不会影响.GlobalEnv。如果所需绘制的图很多,那么在{}内可以随意创建p1、p2、p3,不会影响下一批的图片绘制。

其后还可以继续进行管道操作,继续使用%>%即可。

上述命令的结果图如下:

生物信息学

ggplot2绘图学习 控制坐标轴的范围

2020-8-11 18:58:35

生物信息学

38款测序仪性能指标统计(横向比较)

2020-8-12 18:11:30

声明 本网站部分文章源于互联网,出于传递更多信息和学习之目的转载,并不保证内容正确或赞同其观点。
如转载稿涉及失效、版权等问题,请立即联系管理员;我们会予以修改、删除相关文章,请留言反馈
Notice: When your legal rights are being violated, please send an email to: [email protected]
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索