まずは蝋の翼から。

学んだことを書きながら確認・整理するためのメモブログ。こういうことなのかな?といったことをふわっと書いたりしていますが、理解が浅いゆえに的はずれなことも多々あると思うのでツッコミ歓迎

mutate_atで引数付き関数を使う

やりたいこと

mutateでは、関数を以下のように適用を適用することができる。

library(tidyverse)

iris %>%
  group_by(Species) %>%
  mutate(Sepal.Length_lag2 = lag(Sepal.Length,2))

# Sepal.Length Sepal.Width Petal.Length Petal.Width Species Sepal.Length_lag2
# <dbl>       <dbl>        <dbl>       <dbl> <fct>               <dbl>
#   1          5.1         3.5          1.4         0.2 setosa               NA  
# 2          4.9         3            1.4         0.2 setosa               NA  
# 3          4.7         3.2          1.3         0.2 setosa                5.1
# 4          4.6         3.1          1.5         0.2 setosa                4.9
# 5          5           3.6          1.4         0.2 setosa                4.7
# 6          5.4         3.9          1.7         0.4 setosa                4.6
# 7          4.6         3.4          1.4         0.3 setosa                5  
# 8          5           3.4          1.5         0.2 setosa                5.4
# 9          4.4         2.9          1.4         0.2 setosa                4.6
# 10          4.9         3.1          1.5         0.1 setosa                5  

また、mutate_atではvarsに指定した列に対して関数を適用できる。funsで作成したlist(fun_list)を関数部分に用いると指定した要素名を末尾に追加した列名を追加することができる。

iris %>%
  group_by(Species) %>%
  mutate_at(vars(Sepal.Length:Petal.Width), funs('lag' = lag))

# 
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species Sepal.Length_lag Sepal.Width_lag Petal.Length_lag Petal.Width_lag
# <dbl>       <dbl>        <dbl>       <dbl> <fct>              <dbl>           <dbl>            <dbl>           <dbl>
#   1          5.1         3.5          1.4         0.2 setosa              NA              NA               NA              NA  
# 2          4.9         3            1.4         0.2 setosa               5.1             3.5              1.4             0.2
# 3          4.7         3.2          1.3         0.2 setosa               4.9             3                1.4             0.2
# 4          4.6         3.1          1.5         0.2 setosa               4.7             3.2              1.3             0.2
# 5          5           3.6          1.4         0.2 setosa               4.6             3.1              1.5             0.2
# 6          5.4         3.9          1.7         0.4 setosa               5               3.6              1.4             0.2
# 7          4.6         3.4          1.4         0.3 setosa               5.4             3.9              1.7             0.4
# 8          5           3.4          1.5         0.2 setosa               4.6             3.4              1.4             0.3
# 9          4.4         2.9          1.4         0.2 setosa               5               3.4              1.5             0.2
# 10          4.9         3.1          1.5         0.1 setosa               4.4             2.9              1.4             0.2

しかし、このときfunsを使った関数で引数を使おうとしても上手く動かない(何故か全てNA)になる。

iris %>%
  group_by(Species) %>%
  mutate_at(vars(Sepal.Length:Petal.Width), funs('lag' = lag(2)))

# Sepal.Length Sepal.Width Petal.Length Petal.Width Species Sepal.Length_lag Sepal.Width_lag Petal.Length_lag Petal.Width_lag
# <dbl>       <dbl>        <dbl>       <dbl> <fct>              <dbl>           <dbl>            <dbl>           <dbl>
#   1          5.1         3.5          1.4         0.2 setosa                NA              NA               NA              NA
# 2          4.9         3            1.4         0.2 setosa                NA              NA               NA              NA
# 3          4.7         3.2          1.3         0.2 setosa                NA              NA               NA              NA
# 4          4.6         3.1          1.5         0.2 setosa                NA              NA               NA              NA
# 5          5           3.6          1.4         0.2 setosa                NA              NA               NA              NA
# 6          5.4         3.9          1.7         0.4 setosa                NA              NA               NA              NA
# 7          4.6         3.4          1.4         0.3 setosa                NA              NA               NA              NA
# 8          5           3.4          1.5         0.2 setosa                NA              NA               NA              NA
# 9          4.4         2.9          1.4         0.2 setosa                NA              NA               NA              NA
# 10          4.9         3.1          1.5         0.1 setosa                NA              NA               NA              NA

対応策

内部的にどういう挙動になっているかよくわからないが、mutate_atの関数部分は関数か、funsのようにlistを指定する。

dplyr.tidyverse.org

そのため、引数付きの関数を事前に作成して関数部分にfunsのようなlistを適用するとよさそう。

partialという関数を用いると既存関数を用いた無名関数を作成することができる。

purrr.tidyverse.org

heavywatal.github.io

そのため、この無名関数をfunsのように作成して関数部分に渡すことで対応ができる。

iris %>%
  group_by(Species) %>%
  mutate_at(vars(Sepal.Length:Petal.Width), list(lag2 = partial(lag, n=2)))

# Sepal.Length Sepal.Width Petal.Length Petal.Width Species Sepal.Length_la… Sepal.Width_lag2 Petal.Length_la…
# <dbl>       <dbl>        <dbl>       <dbl> <fct>              <dbl>            <dbl>            <dbl>
#   1          5.1         3.5          1.4         0.2 setosa              NA               NA               NA  
# 2          4.9         3            1.4         0.2 setosa              NA               NA               NA  
# 3          4.7         3.2          1.3         0.2 setosa               5.1              3.5              1.4
# 4          4.6         3.1          1.5         0.2 setosa               4.9              3                1.4
# 5          5           3.6          1.4         0.2 setosa               4.7              3.2              1.3
# 6          5.4         3.9          1.7         0.4 setosa               4.6              3.1              1.5
# 7          4.6         3.4          1.4         0.3 setosa               5                3.6              1.4
# 8          5           3.4          1.5         0.2 setosa               5.4              3.9              1.7
# 9          4.4         2.9          1.4         0.2 setosa               4.6              3.4              1.4
# 10          4.9         3.1          1.5         0.1 setosa               5                3.4              1.5

なお、mutate_atは前述のようにlistでhoge = 関数で渡すと適用した列の末尾にlistの要素名が_で付けることができるので上記コードもlistで渡しているが、必要ない場合そのままpartial無名関数を渡す。

iris %>%
  group_by(Species) %>%
  mutate_at(vars(Sepal.Length:Petal.Width), partial(lag, n=2))

# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# <dbl>       <dbl>        <dbl>       <dbl> <fct>  
#   1         NA          NA           NA          NA   setosa 
# 2         NA          NA           NA          NA   setosa 
# 3          5.1         3.5          1.4         0.2 setosa 
# 4          4.9         3            1.4         0.2 setosa 
# 5          4.7         3.2          1.3         0.2 setosa 
# 6          4.6         3.1          1.5         0.2 setosa 
# 7          5           3.6          1.4         0.2 setosa 
# 8          5.4         3.9          1.7         0.4 setosa 
# 9          4.6         3.4          1.4         0.3 setosa 
# 10          5           3.4          1.5         0.2 setosa