很多情况下,管道操作符可以很大程度的简化代码,并且使其更加直观、易读、易懂,下面就简单说明了useR2014上颇受R用户喜爱的magrittr包。
the pipe operator is one (if not THE) most important innovation introduced, this year, to the R ecosystem
Intro
类似于linux中的
|
,可以把前一个表达式(函数)的输出(而不用定义中间变量来表示)
直接传递给后面的函数或者表达式,省去不必要中间变量或者括号,代码更加清晰易读。
magrittr提供
了一系列的管道操作符,如
%>%
,
x %>% f
等价于
f(x)
。
2014年useR会议介绍magrittr之后,有人评价:
the pipe operator is one (if
not THE) most important innovation introduced, this year, to the R ecosystem
Basics
Produce values
通过使用管道操作符,可以从语义上改变我们写代码的方式,使代码更加简洁易读,管道操作
符的功能为
- 默认情况下,左边的结果默认作为右边函数的第一个参数,可省略,如下面的
transform
as.Data
和format
.
%>%
可以以嵌套的方式使用,例如它可以作用于表达式来计算函数的参数,如
Date = paste(1973, Month, Day, sep = "-") %>% as.Date
- 如果不是传递作为第一个参数用
.
表示,如aggregate(. ~ Date %>% format("%W"), ., mean)
- 公式中的
.
并不影响管道操作符,如aggregate(. ~ Date %>% format("%W"), ., mean)
- 如果传递的右边函数只需要一个参数,那么可以省略参数和括号,
head
,当然head(.)
和head()
也可以
- 管道操作符左边的函数(包含从上一步传递过来的
.
)为一元函数如aggregate(. ~ Date %>% format("%W"), ., mean)
library(magrittr)
weekly <-
airquality %>%
transform(Date = paste(1973, Month, Day, sep = "-") %>% as.Date)
%>% aggregate(. ~ Date %>% format("%W"), ., mean) %>%
head
上面代码包括三个部分,输入(airquality),一系列的数据转换(
transform
,
aggregate
)和输出(weekly),类似函数的定义,所以它可以看成是一个函数的
定义和调用的过程,容易读写和理解。
当然你也可以不用
%>%
weekly <- aggregate(. ~ format(Date,"%W"),
transform(airquality, Date = as.Date(paste(1973, Month,
Day, sep = "-"))),
mean)
显然这种写法可读性较差,难于理解,含有多对圆括号,更不利于别人的阅读;或者使用
中间变量来避免圆括号的使用,也不如利用管道操作符容易理解。此外,如果想在代码中间
添加新的计算,使用管道操作显然特别方便。
Produce functions
此外,利用
%>%
也可以构造简单的函数,与其基本用法其实是一样的(仅仅是基本用法定义函数的时候
即时调用返回结果),在构造函数的时候没有输入变量,用
.
替代输入变量即成功构造了一个函数
mae <- . %>% abs %>% mean(na.rm = TRUE)
mae(rnorm(10))
## [1] 0.949347
##等价于
mae <- function(x) {
mean(abs(x), na.rm = TRUE)
}
匿名函数和lambda表达式
# 标准的函数定义
mtcars %>%
(function(x) {
if (nrow(x) > 2)
rbind(head(x, 1), tail(x, 1))
else x
})
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.62 16.46 0 1 4 4
## Volvo 142E 21.4 4 121 109 4.11 2.78 18.60 1 1 4 2
# lambda表达式,一元函数的参数用<code>.</code>表示
mtcars %>%
{
if (nrow(.) > 0)
rbind(head(., 1), tail(., 1))
else .
}
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.62 16.46 0 1 4 4
## Volvo 142E 21.4 4 121 109 4.11 2.78 18.60 1 1 4 2
右边的匿名函数用括号包装起来,括号在管道操作符产生作用前优先计算右边括起来的表达式或者函数,
这时候默认情况下第一个参数应该为省略的
.
就不起作用了。
1:5 %>%
{paste(letters[.])}
## [1] "a" "b" "c" "d" "e"
此外匿名函数最有用的就是可以用于
*pply
系列函数
list(1,2,3) %>%
sapply(. %>% length)
## [1] 1 1 1
嵌套的函数调用
.
在管道操作符的右边可以嵌套调用
1:5 %>%
paste(letters[.])
## [1] "1 a" "2 b" "3 c" "4 d" "5 e"
# 等价于
1:5 %>%
paste(.,letters[.])
## [1] "1 a" "2 b" "3 c" "4 d" "5 e"
Advances
magrittr提供了三个其他的,辅助的管道操作符,在某些特定情况下,它们使我们更加
方便的实现各种操作
1.
%T>%
和
%>%
类似,只是它返回管道操作符左侧的值,通常用于流程中产生其他
副作用的步骤(临时的,不改变左侧的结果继续传递给下一个步骤),如(print,
plot, logging, etc)
rnorm(200) %>%
matrix(ncol = 2) %T>%
plot %>% # plot usually does not return anything.
colSums
## [1] -9.25270 -7.98688
## 其实是下面代码的简写形式
rnorm(200) %>%
matrix(ncol = 2) %>%
{plot(.);.} %>% # plot usually does not return anything.
colSums
## [1] 11.197969 7.683612
2.
%$%
使左侧数据中变量的名称加载到流程的环境中方便我们直接提取,显然他等价于
函数
with
mtcars %$%
plot(mpg, wt)
3.
%<>% 必须是流程中的第一个管道操作符,最后的计算结果赋值给其左边变量,它主要作用是 把形如
foo
\<- foo %\>% bar %\>% baz
的计算流程简化为
foo %\<\>% bar %\>% baz\`
此外为了使R中的基本操作符与管道操作更友好的结合起来,为这些操作符提供了别名:
<blockquote> extract
[
extract2
[[
use_series
$
add
+
subtract
-
multiply_by
*
raise_to_power
^
multiply_by_matrix
%*%
divide_by
/
divide_by_int
%/%
mod
%%
and
&
or
|
equals
==
is_greater_than
>
is_weakly_greater_than
>=
is_less_than
<
is_weakly_less_than
<=
not
!
set_colnames
colnames<-
set_rownames
rownames<-
set_names
names<-
</blockquote>
rnorm(100) %>% <code>*</code>(5) %>% <code>+</code>(5) %>%
{
cat("Mean:", mean(.), "Variance:", var(.), "\n")
head(.)
}
## Mean: 4.785676 Variance: 23.8642
## [1] 7.806526 -4.416972 -1.273823 2.976003 7.568857 12.097451
# 可以写为
rnorm(1000) %>%
multiply_by(5) %>%
add(5) %>%
{
cat("Mean:", mean(.),
"Variance:", var(.), "\n")
head(.)
}
## Mean: 4.982161 Variance: 26.18578
## [1] 0.06189078 8.67467189 6.37717763 6.09563550 -0.65720166 4.04583594
Ref
1.
magrittr: Simplifying R code with pipes
2.
magrittr 1.5
3.
magrittr vignette