読者です 読者をやめる 読者になる 読者になる

tidyr 0.3を使ってみる

R tidyr

tidyrのバージョンが上がってました。

リリースノート

Release tidyr 0.3.0 · hadley/tidyr · GitHub

主な変更点

complete()

New complete() provides a wrapper around expand(), left_join() and replace_na() for a common task: completing a data frame with missing combinations of variables.

これはちょっと何を言ってるかわからない...。

出所はこのSOの質問みたいです。

r - adding default values to item x group pairs that don't have a value (df %>% spread %>% gather seems strange) - Stack Overflow

以下のようなデータがあったときに、groupiditemidの全組み合わせが見たいとします。具体的には、「groupidoneitemid1value3」「groupidoneitemid2valueは存在しない」...という感じです。

df1 <- data.frame(groupid = c("one","one","one","two","two","two", "one"),
                  value = c(3,2,1,2,3,1,22),
                  itemid = c(1:6, 6))
df1
#>   groupid value itemid
#> 1     one     3      1
#> 2     one     2      2
#> 3     one     1      3
#> 4     two     2      4
#> 5     two     3      5
#> 6     two     1      6
#> 7     one    22      6

すべての値の組み合わせをつくるのは、expand()という関数が用意されています。以下のような感じです。よさそうです。

expand(df1, itemid, groupid)
#> Source: local data frame [12 x 2]
#> 
#>    itemid groupid
#>     (dbl)  (fctr)
#> 1       1     one
#> 2       1     two
#> 3       2     one
#> 4       2     two
#> 5       3     one
#> 6       3     two
...

ただ、ここにvalueを紐づけるには、元のdf1dplyr::left_join()する必要があります。ちょっと冗長な感じがします。

expand(df1, itemid, groupid) %>% 
    left_join(df1, by = c("itemid", "groupid"))
#> Source: local data frame [12 x 3]
#> 
#>    itemid groupid value
#>     (dbl)  (fctr) (dbl)
#> 1       1     one     3
#> 2       1     two    NA
#> 3       2     one     2
...

これをcomplete()を使えば一発でできます。

complete(df1, itemid, groupid)
#> Source: local data frame [12 x 3]
#> 
#>    itemid groupid value
#>     (dbl)  (fctr) (dbl)
#> 1       1     one     3
#> 2       1     two    NA
#> 3       2     one     2
...

NAを値で置き換えるには、fillという引数を使います。

complete(df1, itemid, groupid, fill = list(value = 0))
#> Source: local data frame [12 x 3]
#> 
#>    itemid groupid value
#>     (dbl)  (fctr) (dbl)
#> 1       1     one     3
#> 2       1     two     0
#> 3       2     one     2
...

使いどころがぱっと思いつきませんが、まあ便利そうな関数ではあります。

fill()

fill() fills in missing values in a column with the last non-missing value (#4).

NAを、一番直前の値で埋めてくれます。

df <- data.frame(Month = 1:12, Year = c(2000, rep(NA, 11)))
df
#>    Month Year
#> 1      1 2000
#> 2      2   NA
#> 3      3   NA
#> 4      4   NA
...

df %>% fill(Year)
#>    Month Year
#> 1      1 2000
#> 2      2 2000
#> 3      3 2000
#> 4      4 2000
...

これはおなじみdplyr::select()と同じ記法が使えます。

df <- data.frame(x = c(1, rep(NA, 10)), y = c(1:2, rep(NA, 9)), z = c(1:3, rep(NA, 8)))
df
#>     x  y  z
#> 1   1  1  1
#> 2  NA  2  2
#> 3  NA NA  3
#> 4  NA NA NA
...

fill(df, -x)
#>     x y z
#> 1   1 1 1
#> 2  NA 2 2
#> 3  NA 2 3
#> 4  NA 2 3
...

replace_na()

New replace_na() makes it easy to replace missing values with something meaningful for your data.

これほしかった! その名の通りNAを置き換えてくれます。

第一引数にdata.frameを渡し、第二引数に置き換える値をlistで渡します。

df <- data_frame(x = c(1, 2, NA), y = c("a", NA, "b"))
df %>% replace_na(list(x = 0, y = "unknown"))
#> Source: local data frame [3 x 2]
#> 
#>       x       y
#>   (dbl)   (chr)
#> 1     1       a
#> 2     2 unknown
#> 3     0       b

なんとなく忘れてmutate()の中で使ってエラーになったりしそう...。

nest()

nest() is the complement of unnest() (#3).

unnest()は前からあった関数で、data.frameの中にネストして入っているlistやdata.frameを展開します。

df <- data.frame(
  x = 1:2,
  y = list(data.frame(a = 1:10), data.frame(a = 11:20))
)
df
#> Source: local data frame [2 x 2]
#> 
#>       x        y
#>   (int)    (chr)
#> 1     1 <int[5]>
#> 2     2 <int[5]>

unnest(df, y)
#> Source: local data frame [10 x 2]
#> 
#>        x     y
#>    (int) (int)
#> 1      1     1
#> 2      1     2
#> 3      1     3
#> 4      1     4
...

nest()は逆に、data.frame()の中にネストをつくります。baseのsplit()とかdplyr::group_by()がイメージに近いです。たぶん。

iris %>% nest(-Species)
#> Source: local data frame [3 x 5]
#> Groups: <by row>
#> 
#>      Species Sepal.Length Sepal.Width Petal.Length Petal.Width
#>       (fctr)        (chr)       (chr)        (chr)       (chr)
#> 1     setosa    <dbl[50]>   <dbl[50]>    <dbl[50]>   <dbl[50]>
#> 2 versicolor    <dbl[50]>   <dbl[50]>    <dbl[50]>   <dbl[50]>
#> 3  virginica    <dbl[50]>   <dbl[50]>    <dbl[50]>   <dbl[50]>

感想

細かいけど便利なのが増えてきた気がします。

問題は、必要なときにtidyrの関数を思い出せるかということ。。いつもどれがどの関数か忘れてしまいます。