如果考虑到向下兼容问题,或许在用到{magrittr}
依赖的包的开发中,目前来说还是不太适合完全使用 |>
进行替代?
R 4.1.0 官方支持管道 |>,爷青结
- 已编辑
R 为什么不吸取第三方包的精华呢,直接把 %>%
转正不就完了嘛
It's all about politics!
- 已编辑
chuxinyuan 测试了下 %>%
和 |>
速度一样,都比 4.0.5 快 16% 的样子。
barnett874 速度一样是不可能的,这辈子都不会一样的。|>
是从语法解析器层面实现的,把 x |> f()
直接解析为 f(x)
,也就是 x |> f()
的速度几乎与直接运行 f(x)
一样;而 %>%
则没这么直接,它还要做一箩筐其它的事情。
chuxinyuan 我的理解是 %>%
的实现太复杂了,规则太多;而 |>
的实现干净利落,有且仅有一条规则,就是我上面说的,比如它要求管道右边必须是一个函数调用,而不能是裸函数,而 %>%
则二者皆可。
1:10 %>% head
1:10 %>% head()
1:10 |> head # 这不可以
1:10 |> head()
|>
也不支持 %>%
发明的占位符 .
。要用占位符的话,只能显式地写一个函数,不过可以是简写版的匿名函数,如 \(.) head(., 8)
,也就是一条反斜杠代替了 function
关键字。不过说实话我到现在仍然不习惯这个反斜杠,因为它在我脑子里根深蒂固成了转义符的代表,视觉上我很难把 \(x)
与 function(x)
等价起来。
最后,不管是哪种管道,我个人对它们都并不是很热衷。%>%
的排错(debug)之麻烦,让我觉得简直难以忍受;若真能一气呵成写出一条管道,那皆大欢喜,而万一要是写不对,要查出是哪里错了,则麻烦得要死,所以我宁愿用结硬寨、打呆仗的办法写代码,哪怕这种呆办法被管道党一次次嘲弄。
x = 1:10
x = head(x)
因为 |>
的实现原理简单,所以出错后排错要容易得多,可以使用常见的排错手段,如 debug()
函数。
- 已编辑
倒不意外,油管哪个视频好像是什么会议上就有人说了马上管道就来了,还演示了示例代码但是确实没想到这么快。鼓掌。
✓ [main*]> empfreq %>%
tibble(no = row.names(.))
# A tibble: 12 x 2
. no
<table> <chr>
1 0.235 0
2 0.275 1
3 0.230 2
4 0.105 3
5 0.050 4
6 0.045 5
7 0.015 6
8 0.020 7
9 0.005 8
10 0.005 9
11 0.010 10
12 0.005 11
✓ [main*]> empfreq |>
tibble(no = row.names(.))
Error: Internal error in `df_slice()`: Columns must match the data frame size.
Run `rlang::last_error()` to see where the error occurred.
- 已编辑
我想问一下|>
有快捷键吗?
maths_hjxk RStudio 1.4.1714(预览版)可以设置默认管道符,快捷键不变。
- 已编辑
试了一下 |>
, 目前来说还是会使用 magrittr 包, 主要有三点:
我经常使用
%<>%
, 它可以让代码更简洁, 让写代码的过程更流畅. 比如x$y$z %<>% c(a)
这样的代码, 要是不用%<>%
, 写出来会一长串, 尤其是变量名比较长的时候.如果值不是传给右边函数的第一个变量,
|>
用起来会比较笨拙. 比如x$y %>% {ifelse(is.null(.), ., 1)}
这样的代码, 要使用|>
必须配合 lambda 函数的写法, 行文上就更不流畅.另外, R 4.1.0 提供了
=>
所谓的 pipe bind operator 来解决值传递给非首位变量的问题, 但是这让 R 的 pipe 设计在整体上非常不统一, 非常不奥卡姆剃刀.
除非对速度有很高的要求, 不然代码流畅性和代码对人的可读性一定是我首先考虑的. 所以从以上几个角度来说, 我更倾向使用 magrittr.
yihui
看到个支持占位符的做法
Sys.setenv("_R_USE_PIPEBIND_" = "true")
yihui yihui ,你好
看到这个更新,确实觉得不错。
但在官网也没说明。新的管道操作有没有快捷键啊。是不是和{magrittr}
一样的?
我认为管道操作一定要有快捷键,这样写起代码来才顺畅
lianghao118 RStudio 吗? Tools -> Global Options -> Code -> Use native pipe operator
- 已编辑
x <- c(1:10, NA)
a_fun <- function(x) {
. <- x
. <- ifelse(is.na(.), 0, .)
. <- (. + 1) * 3
. <- sort(.)
head(., 5)
}
b_fun <- function(x) {
x |>
{\(.) ifelse(is.na(.), 0, .)}() |>
{\(.) (. + 1) * 3}() |>
sort() |>
head(5)
}
a_fun(x)
#> [1] 3 6 9 12 15
b_fun(x)
#> [1] 3 6 9 12 15
dput(a_fun)
#> function (x)
#> {
#> . <- x
#> . <- ifelse(is.na(.), 0, .)
#> . <- (. + 1) * 3
#> . <- sort(.)
#> head(., 5)
#> }
dput(b_fun)
#> function (x)
#> {
#> head(sort({
#> function(.) (. + 1) * 3
#> }({
#> function(.) ifelse(is.na(.), 0, .)
#> }(x))), 5)
#> }
<sup>Created on 2021-05-24 by the reprex package (v2.0.0)</sup>
想了好久,感觉我还是更喜欢传统的写法。也可以在Rstudio设置 ". <- " 快捷键。写出来的代码也可以很简练。如函数a_fun。这本身就符合奥卡姆剃刀原则。这是其一。
其二, 使用管道符号以后,写出来函数的格式不稳定。比如,dput(b_fun) 以后和原来的代码完全不一样。虽然这对大部分用户来说并不是什么问题,但我本人不太喜欢。
个人观点。
wangbinzjcc 这个匿名函数写法简直是天书,有种看 perl 代码的赶脚
JackieMe 你不是第一个这么说的人。一堆特殊字符(大括号、反斜杠、小括号、点、竖线、小于号)堆在一起,确实是太 Perl 了。
所以我还是喜欢 a_fun
里的写法,一步一赋值,简单明了,排错方便。管道的写法太怂恿用户追求形式上的美感,而这种外表美掩盖了潜在的代价。每次看到这种讨论,总让我联想起古诗与现代诗。形式美是一种美,但特别容易病态和狂热,舍本逐末(比如我认为诗的平仄差不多就行,完全不必成为规则;管道也不必管天管地,不是所有代码都适合管道;管道本是 Unix 的发明,也没见到 Shell 脚本到处都是管道吧,通常也都是普通的 if
判断或 for
循环;我一直都不是很理解为啥 R 社区这么狂热于管道)。
但 a_fun
这种一步一赋值的写法最容易被人诟病的是,当变量名比较长时,写起来就很啰嗦了。这里用 .
简写看起来还行。我通常绕过这个问题的办法是写新函数:如果需要重复操作一个变量,那么就把这部分代码抽出去写一个新函数,新函数的参数用短变量名。如:
c_fun <- function(x) {
x_squared <- x^2
x <- (x_squared + 1) * (x_squared -2)
head(x, 5)
}
为了避免重复敲 x_squared
这个长名字,可以新写一个函数,参数用短名:
d_fun <- function(x) {
(x + 1) * (x -2)
}
然后调用新函数:
c_fun <- function(x) {
x <- d_fun(x^2)
head(x, 5)
}
抽离局部代码到新函数这种做法也便于单元测试。不过我这都是从开发者角度说的。数据分析的代码风格会不一样,可能不会频繁抽离新函数。
- 已编辑
我自己刚开始接触管道的时候, 有过什么都想上管道的倾向 ... 现在处于一种混杂的状态. 至于什么时候用什么时候不用, 我的标准是认知上的流畅和简洁. 但这个 "简洁" 并不是完全说输入的代码越少越好. 我下面举一些例子:
my_data[, c("var_1", "var_2", "var_3")] %>% my_fun()
上面这个例子我觉得就是一个简单的, 但是显示管道优点的例子. 因为它的形式是贴合你的思维过程的. 如果你用传统的方式来写, 就会有点拗口:
my_fun(my_data[, c("var_1", "var_2", "var_3")])
因为 my_fun()
这个操作是后发生的.
如果用中间的变量, 我个人会觉得有点笨拙:
new_data <- my_data[, c("var_1", "var_2", "var_3")]
my_fun(new_data)
这么写对我个人来说还有三个不爽的点:
new_data
这个变量是不重复使用的, 有点废话.new_data
会阻断思维过程, 本来一句话能说清楚的, 变得冗长了. 我下次看的时候, 加工时间会更长.- 如果你采用中间变量是为了更清晰, 更显性, 那么这背后需要你花心思去取直观简洁的变量名, 这个过程对我来说很痛苦.
但就算我支持管道, 我也不会刻意这样写:
c("var_1", "var_2", "var_3") %>%
{my_data[, .]} %>%
my_fun()
因为我思考的时候, my_data[, c("var_1", "var_2", "var_3")]
是整体出现的. 字面上的, 刻意的简洁反而是损害思维上的简洁的. 除非 c("var_1", "var_2", "var_3")]
是我之前处理的结果.
这些例子看起来很无足轻重, 但是是我实际开发时的体验的缩影.
- 已编辑
开辟第三战场
# Example - 1
1:10 ->.; head(.) # 如果换行分号就可去掉了。
# Example - 2
1:10 ->.
head(.)
# Example - 3
library(dplyr)
mtcars ->.
group_by(., cyl) ->.
summarise(., mpg_avg = mean(mpg)) -> df
- 已编辑
chuxinyuan
可以设置个 " ->.; "的快捷键。 想换行就换行,不想换行就接着写。哈哈~~
10:1 |> sort() |> head(5) ->.; ifelse(. < 5, 0, .) ->.; plot(.)
这样是不是有点梦幻了。
等同于这样:
. <- head(sort(10:1), 5)
. <- ifelse(. < 5, 0, .)
plot(.)
wangbinzjcc 有分号普适性好点,就是太丑。纯娱乐,呵呵