Nested data

library(tidyr)
library(dplyr)
library(purrr)

基本

ネストされたデータフレームとは、1つ(または複数)の列がデータフレームのリストになっているデータフレームのことです。単純なネストされたデータフレームは手で作ることができます。

df1 <- tibble(
  g = c(1, 2, 3),
  data = list(
    tibble(x = 1, y = 2),
    tibble(x = 4:5, y = 6:7),
    tibble(x = 10)
  )
)

df1
#> # A tibble: 3 × 2
#>       g data            
#>   <dbl> <list>          
#> 1     1 <tibble [1 × 2]>
#> 2     2 <tibble [2 × 2]>
#> 3     3 <tibble [1 × 1]>

基本

ネストされたデータフレームとは、1つ(または複数)の列がデータフレームのリストになっているデータフレームのことです。単純なネストされたデータフレームは手で作ることができます。:

df2 <- tribble(
  ~g, ~x, ~y,
   1,  1,  2,
   2,  4,  6,
   2,  5,  7,
   3, 10,  NA
)
df2 %>% nest(data = c(x, y))
#> # A tibble: 3 × 2
#>       g data            
#>   <dbl> <list>          
#> 1     1 <tibble [1 × 2]>
#> 2     2 <tibble [2 × 2]>
#> 3     3 <tibble [1 × 2]>

nest() では、どの変数を内部に入れ子にするかを指定します。もう一つの方法は、どの変数を外部に置いておくかを記述するために、dplyr::group_by()を使用することです。

df2 %>% group_by(g) %>% nest()
#> # A tibble: 3 × 2
#> # Groups:   g [3]
#>       g data            
#>   <dbl> <list>          
#> 1     1 <tibble [1 × 2]>
#> 2     2 <tibble [2 × 2]>
#> 3     3 <tibble [1 × 2]>

ネスティングは、グループ化されたデータに関連して理解するのが最も簡単だと思います。つまり、出力の各行が入力の1つの group に対応します。出力の各行は入力の1つの group に対応しています。この方法は、グループごとのオブジェクトがある場合に特に便利であることはすぐに分かるでしょう。

nest()の反対はunnest()です。データフレームを含むリスト列の名前を与えると、データフレームを行結合し、外側の列を正しい回数だけ繰り返して並べます。

df1 %>% unnest(data)
#> # A tibble: 4 × 3
#>       g     x     y
#>   <dbl> <dbl> <dbl>
#> 1     1     1     2
#> 2     2     4     6
#> 3     2     5     7
#> 4     3    10    NA

Nested data and models

入れ子になったデータは、各グループに 何か が1つずつあるような問題に適しています。このような問題がよく起こるのは、複数のモデルをフィッティングする場合です。

mtcars_nested <- mtcars %>% 
  group_by(cyl) %>% 
  nest()

mtcars_nested
#> # A tibble: 3 × 2
#> # Groups:   cyl [3]
#>     cyl data              
#>   <dbl> <list>            
#> 1     6 <tibble [7 × 10]> 
#> 2     4 <tibble [11 × 10]>
#> 3     8 <tibble [14 × 10]>

データフレームのリストがあれば、モデルのリストを作るのはとても自然なことです。

mtcars_nested <- mtcars_nested %>% 
  mutate(model = map(data, function(df) lm(mpg ~ wt, data = df)))
mtcars_nested
#> # A tibble: 3 × 3
#> # Groups:   cyl [3]
#>     cyl data               model 
#>   <dbl> <list>             <list>
#> 1     6 <tibble [7 × 10]>  <lm>  
#> 2     4 <tibble [11 × 10]> <lm>  
#> 3     8 <tibble [14 × 10]> <lm>

そして、予測のリストを作成することもできます。

mtcars_nested <- mtcars_nested %>% 
  mutate(model = map(model, predict))
mtcars_nested  
#> # A tibble: 3 × 3
#> # Groups:   cyl [3]
#>     cyl data               model     
#>   <dbl> <list>             <list>    
#> 1     6 <tibble [7 × 10]>  <dbl [7]> 
#> 2     4 <tibble [11 × 10]> <dbl [11]>
#> 3     8 <tibble [14 × 10]> <dbl [14]>

このワークフローは、broomと組み合わせて使うと、モデルを整然としたデータフレームに変換し、それを unnest() してフラットなデータフレームに戻すことが簡単にできます。broom と dplyr の vignetteでは、より大きな例を見ることができます。