倒不意外,油管哪个视频好像是什么会议上就有人说了马上管道就来了,还演示了示例代码但是确实没想到这么快。鼓掌👏

✓ [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.

试了一下 |>, 目前来说还是会使用 magrittr 包, 主要有三点:

  1. 我经常使用 %<>%, 它可以让代码更简洁, 让写代码的过程更流畅. 比如 x$y$z %<>% c(a) 这样的代码, 要是不用 %<>%, 写出来会一长串, 尤其是变量名比较长的时候.

  2. 如果值不是传给右边函数的第一个变量, |> 用起来会比较笨拙. 比如 x$y %>% {ifelse(is.null(.), ., 1)} 这样的代码, 要使用 |> 必须配合 lambda 函数的写法, 行文上就更不流畅.

  3. 另外, R 4.1.0 提供了 => 所谓的 pipe bind operator 来解决值传递给非首位变量的问题, 但是这让 R 的 pipe 设计在整体上非常不统一, 非常不奥卡姆剃刀.

除非对速度有很高的要求, 不然代码流畅性和代码对人的可读性一定是我首先考虑的. 所以从以上几个角度来说, 我更倾向使用 magrittr.

yihui
看到个支持占位符的做法

Sys.setenv("_R_USE_PIPEBIND_" = "true")
  • ryo 回复了此帖
  • ryo 觉得很赞

    yihui yihui ,你好
    看到这个更新,确实觉得不错。
    但在官网也没说明。新的管道操作有没有快捷键啊。是不是和{magrittr}一样的?
    我认为管道操作一定要有快捷键,这样写起代码来才顺畅

      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) 以后和原来的代码完全不一样。虽然这对大部分用户来说并不是什么问题,但我本人不太喜欢。

      个人观点。

        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)

        这么写对我个人来说还有三个不爽的点:

        1. new_data 这个变量是不重复使用的, 有点废话.
        2. new_data 会阻断思维过程, 本来一句话能说清楚的, 变得冗长了. 我下次看的时候, 加工时间会更长.
        3. 如果你采用中间变量是为了更清晰, 更显性, 那么这背后需要你花心思去取直观简洁的变量名, 这个过程对我来说很痛苦.

        但就算我支持管道, 我也不会刻意这样写:

        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(.)
            5 天 后

            barnett874

            设置了还是... @@“

            x 3.3 GiB [master*]> devtools::session_info()$platform |> 
                 unlist() |> 
                 data.frame(Category = names(.), session_info = .)
            Error in data.frame(unlist(devtools::session_info()$platform), Category = names(.),  : 
              arguments imply differing number of rows: 9, 0
            
            x 3.3 GiB [master*]> devtools::session_info()$platform %>% 
                 unlist() %>% 
                 data.frame(Category = names(.), session_info = .)
                     Category                 session_info
            version   version R version 4.1.0 (2021-05-18)
            os             os           Ubuntu 20.04.2 LTS
            system     system            x86_64, linux-gnu
            ui             ui                      RStudio
            language language                           en
            collate   collate                  en_US.UTF-8
            ctype       ctype                  en_US.UTF-8
            tz             tz                   Asia/Tokyo
            date         date                   2021-05-30

            好了~

            3.3 GiB [master*]> devtools::session_info()$platform %>% 
                 unlist() %>% 
                 (function(df) data.frame(Category = names(df), session_info = df))()
                     Category                 session_info
            version   version R version 4.1.0 (2021-05-18)
            os             os           Ubuntu 20.04.2 LTS
            system     system            x86_64, linux-gnu
            ui             ui                      RStudio
            language language                           en
            collate   collate                  en_US.UTF-8
            ctype       ctype                  en_US.UTF-8
            tz             tz                   Asia/Tokyo
            date         date                   2021-05-30

              ryo 可以用 R-perl 这么写:

              devtools::session_info()$platform |> 
                   unlist() |> 
                   {\(.) data.frame(Category = names(.), session_info = .)}()

              😂

              11 天 后
              1 年 后