R | 这些奇怪的符号是什么?(管道符)

最近学习到Linux的管道符 | 可以将左侧的输入传递给右侧的函数处理,这种从左到右的函数处理方式看着就让人很舒服,想起R中其实也有这种类似的管道符 —— %>% 。因此,就在这篇文章谈谈%>%的基本特点和包含它的包magrittr

记得第一次看到这样的代码的时候就忍不住头大,这个%>%到底是个啥?

rnorm(10) %>% as.character %>% sort %>% unique

在翻阅了多个网站的资料后,才知道%>%的功能就是把左侧的变量传递给右边。而上面的函数也就是相当于把嵌套的函数unique(sort(as.character(rnorm(10))))%>%写出来。

当时对%>%的第一感受就是可以简化函数嵌套的表达模式,用一种更简洁且优美的方式敲代码。但%>%就止步于此了吗?

magrittr - Ceci n'est pas un pipe.

Ceci n'est pas un pipe 是一句法语,翻译过来的意思是 “这不是管道符”。

在了解%>%之前,我们要先弄明白 Stefan Milton Bache写这个包想要解决的问题是什么?

magrittr的R Documents上了解到,其提供了一系列操作符,可以解决以下问题

  • 以一种从左到右的方式构建数据操作的顺序
  • 避免函数嵌套式调用
  • 减少局部变量的创建和部分函数的定义
  • 简单地在代码序列中添加步骤

一句话总结magrittr管道符的优点就是大大地提高了代码的可读性(readable),减少到几乎不需要局部变量的创建,并降低程序对内存的占用。

Installation

install.packages("magrittr")

Usage

%>% : 管道操作符,将左侧变量传递到右侧并作为输入变量.

1. %>%的基本用法
先从最基本的%>%开始,其用法也十分简单,基础的用法就是 x %>% f == f(x)

x %>% f 默认将x传递到函数的第一个参数位,无论后面有无参数,即 x %>% f(y) == f(x,y)

像一开始的例子一样,多级函数传递也是可以的,x %>% f %>% g %>% h == h(g(f(x)))

iris数据做个例子

# 第一种用法
> iris %>% head
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

# 第二种用法
> iris %>% head(3)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa

# 第三种用法
> iris[,1] %>% as.character %>% sort %>% unique 
 [1] "4.3" "4.4" "4.5" "4.6" "4.7" "4.8" "4.9" "5"   "5.1" "5.2" "5.3" "5.4"
[13] "5.5" "5.6" "5.7" "5.8" "5.9" "6"   "6.1" "6.2" "6.3" "6.4" "6.5" "6.6"
[25] "6.7" "6.8" "6.9" "7"   "7.1" "7.2" "7.3" "7.4" "7.6" "7.7" "7.9"

2. Placeholder

x %>% f(y, .) == f(y, x)
.可作为placeholder,指明左侧输入传递到的位置。如果没有的话,默认左侧输入传递到右侧函数的第一个参数。

> "Ceci n'est pas une pipe" %>% gsub("une", "un", .)
[1] "Ceci n'est pas un pipe"

# '[]'也可以
> iris[,5] %>% grep('setosa', .)  %>% iris[.,1:5] %>% head
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

3. Placeholder的其他用法 —— ‘{}’
在嵌套函数中使用.方法时需要格外注意以下情况的发生:

x %>% f(y=nrow(.), z=ncol(.)) == f(x, y=nrow(x), z=ncol(x))

虽然使用.后,变量x会传递到.处,但是x也传递到函数f的第一个参数位。

想要避免这种情况的发生可以使用{}将函数包起来,即x %>% {f(y=nrow(.), z=ncol(.))} == f(y=nrow(x), z=ncol(x))

> set.seed(501)
> rnorm(100) %>% {c(min(.), mean(.), max(.))} %>% floor
[1] -3  0  2

4. 构建函数
利用管道符构建函数也是十分简单的,我们可以使用.作为管道传递的起始,它将会返回一个函数,例如:
f <- . %>% cos %>% sin == f <- function(.) sin(cos(.))

其他管道符

magrittr包还提高了其他几种管道符,以在特殊的情景进行利用,下面会简单介绍这几种符号。

  1. %<>% :相当于<- %>%的组合, 同样是将左侧变量传递到右侧并作为输入变量,但会将最后的结果赋值到最左侧的变量。
a <- rep(c(2,3), each =10)
a %<>% as.character %>% unique
a
## [1] "2" "3"
  1. %T>% : 将左侧传递到右侧,右侧执行后返回左边的原始输入,而非执行后结果。

以下的代码从标准正态分布中随机取样200个数,并构建出100 x 2的矩阵,最后对列求和。唯一不同在于第二条代码使用 %T>% plot 对矩阵画了个散点图,再将矩阵传递给colSums

> rnorm(200) %>% matrix(ncol = 2) %>% colSums
[1]   9.040591 -10.754680

> rnorm(200) %>% matrix(ncol = 2) %T>% plot %>% colSums
[1]   9.040591 -10.754680
rnorm(200) plot

所以%T>%的一种应用环境就是当操作序列中间有像plot这种不返回结果的函数时,既将左侧变量传递给这种函数,又不会影响后边函数的处理。

上述代码流程图示

  1. %$% : 可以将左侧变量中的变量名传递到右侧应用。有些函数,如ggplot,在输入数据后可以用数据框中的变量名来调用数据框内的变量(例如:ggplot(data,aes(x=x1,y=x2)))。%$%就是发挥这样的功能,我们可以用下面这个例子来理解
iris %>%
  subset(Sepal.Length > mean(Sepal.Length)) %$%
  cor(Sepal.Length, Sepal.Width)
## [1] 0.3361992

上面的代码就是先截取iris数据中Sepal.Length列大于平均值的部分,再在截取后的iris数据中使用cor计算Sepal.LengthSepal.Width的相关性。与下面的代码是等价的

a <- iris %>% subset(Sepal.Length > mean(Sepal.Length)) 
cor(a$Sepal.Length,a$Sepal.Width)
## [1] 0.3361992

通过这个例子,我们可以看到使用%$%这一管道符,其实相当于在符号的右侧创建了一个局部环境,在该环境中可以直接以名字的方式调用左侧传递过来的变量,而省略$

以上就是对R中管道符%>%magrittr包的简单介绍。

完。

参考资料:
https://www.rdocumentation.org/packages/magrittr/versions/1.5

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

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,746评论 2 9
  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,380评论 0 5
  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,340评论 0 2
  • 1.面向对象的程序设计思想是什么? 答:把数据结构和对数据结构进行操作的方法封装形成一个个的对象。 2.什么是类?...
    少帅yangjie阅读 4,997评论 0 14
  • 第 2 章 SHELL 基础知识2.1 shell脚本我们在上面简单介绍了一下什么是shell脚本,现在我们来进一...
    LiWei_9e4b阅读 1,567评论 0 0