メモ:%<-%、%>>%

最近ちょっと気になる演算子。使い方をちょっと調べたのでメモ。

%<-%

これはzeallotというパッケージの演算子です。

値のparallel assignmentができます。parallel assignmentというのは、Pythonとかだと、

a, b, c = 1, 2, 3

みたいなことでできるじゃないですか。あれです。

library(zeallot)

c(a, b, c) %<-% 1:3
c(a, b, c)
#> [1] 1 2 3

もちろん値の入れ替えなんかもできます。

x <- 0
y <- 1

c(x, y) %<-% c(y, x)

c(x, y)
#> [1] 1 0

%>>%

これはlumberjackというパッケージの演算子です。

%>>%%>%と同じような演算子で、lumberjackパッケージの他の関数と組み合わせて使うとデータ変形のログを取ることができます。

例えば、こんな感じの処理を、

library(dplyr, warn.conflicts = FALSE)

mtcars %>%
  group_by(cyl) %>%
  summarise(
    disp = mean(disp),
    hp = mean(hp)
  )
#> # A tibble: 3 x 3
#>     cyl     disp        hp
#>   <dbl>    <dbl>     <dbl>
#> 1     4 105.1364  82.63636
#> 2     6 183.3143 122.28571
#> 3     8 353.1000 209.21429

ちょっと(?)変えると

library(lumberjack)

out <- tempfile(fileext = ".csv")

mtcars %>>%
  start_log() %>>%
  group_by(cyl) %>>%
  summarise(
    disp = mean(disp),
    hp = mean(hp)
  ) %>>%
  dump_log(file = out, stop = TRUE)
#> Dumped a log at C:\Users\user1\AppData\Local\Temp\RtmpMZMA0t\file33e8672434bd.csv
#> # A tibble: 3 x 3
#>     cyl     disp        hp
#>   <dbl>    <dbl>     <dbl>
#> 1     4 105.1364  82.63636
#> 2     6 183.3143 122.28571
#> 3     8 353.1000 209.21429

こんな感じのログが記録されます。

readr::read_csv(out)
#> Parsed with column specification:
#> cols(
#>   step = col_integer(),
#>   time = col_datetime(format = ""),
#>   expression = col_character(),
#>   changed = col_logical()
#> )
#> # A tibble: 2 x 4
#>    step                time                                  expression
#>   <int>              <dttm>                                       <chr>
#> 1     1 2017-08-07 21:24:52                               group_by(cyl)
#> 2     2 2017-08-07 21:24:52 summarise(disp = mean(disp), hp = mean(hp))
#> # ... with 1 more variables: changed <lgl>

ということで、あんまり汎用的な演算子ではないんですけど、おもしろいなーと思ったのは%>>%は、通常のオブジェクトに対しては%>%と同じように働いて、LOGNAMEという属性があるオブジェクトが通った時だけログ記録が発動します。

lumberjack::`%>>%`
#> function (lhs, rhs) 
#> {
#>     rhs <- substitute(rhs)
#>     out <- pipe(lhs, rhs, env = parent.frame())
#>     meta <- list(expr = as.expression(rhs), src = as.character(as.expression(rhs)))
#>     if (has_log(lhs)) {
#>         log <- get_log(lhs)
#>         log$add(meta = meta, input = lhs, output = out)
#>     }
#>     out
#> }
#> <environment: namespace:lumberjack>

こういう感じで、%>%(的なもの)にフックを仕掛けるという仕組みはもっと汎用的になれば面白いかも。その意気や良し、という感想でした。