tidyrのmulti-gatherの未来

(これはtidyポエム Advent Calendar 2017 - Adventar 6日目の記事です)

こんなIssueがあります。

multi-gatherというのは何かというと、例えば、こういうデータを

testA_score testA_type testB_score testB_type
100 x 90 y
80 y 10 z
44 z 19 z
44 x 77 x

こう変形するようなやつです。

test score type
testA 100 x
testB 90 y
testA 80 y
testB 10 z
testA 44 z
testB 19 z
testA 44 x
testB 77 x

列名にtestAtestB(テストの種類)、値がscoretypeか(テストの種類)という複数の情報が含まれているのをtidyな形に変形しています。

これは今のところ、いい感じにやる方法がありません。だいたい同じことは以下のようにすればできます。

library(tidyr)
library(tibble)
library(dplyr, warn.conflicts=FALSE)

tribble(
  ~testA_score, ~testA_type, ~testB_score, ~testB_type,
           100,         "x",           90,          "y",
            80,         "y",           10,          "z",
            44,         "z",           19,          "z",
            44,         "x",           77,          "x"
) %>%
  # あとでspreadするときに行を一意に識別するキーに使う。これがエラーになる。
  rowid_to_column(var = "rowid") %>%
  # とりあえずいったんすべての列をgather()する
  gather("key", "value", -rowid) %>%
  # "testA_score"を_で切り離して"testA"と"score"にする
  separate(key, into = c("test", "column"), sep = "_") %>%
  # scoreとtypeは列になるべきものなのでspreadする
  spread(column, value) %>%
  # spreadしてしまったらrowidはもう不要なので消す
  select(-rowid)
#> # A tibble: 8 x 3
#>    test score  type
#> * <chr> <chr> <chr>
#> 1 testA   100     x
#> 2 testB    90     y
#> 3 testA    80     y
#> 4 testB    10     z
#> 5 testA    44     z
#> 6 testB    19     z
#> 7 testA    44     x
#> 8 testB    77     x

しかし、気づいたかもしれませんが、scoreがcharacterになってしまっています。これは、このやり方だと値の型が違うscoretypeもいったん一緒の列にgather()しないといけないためです。本来はscoretypeを別々に扱う処理が望ましいです。これが、multi-gatherです。

しかし、上のIssueは止まったままです。これはなぜかというと、ひとつには設計に時間をかけていることですが、もうひとつには、

x %>%
  gather(Race, colnames = multikey("(gender)_(film)"), value = "words")

みたいに書くときに、正規表現のnamed capture groupが使えないと困る、ということのようです(↑自体はnamed capture groupとして使える正規表現じゃないので、もうちょっと仕組みが必要そうですけど)。

I wonder if there's some way to hack in named groups support to stringi.
(https://github.com/tidyverse/tidyr/issues/150#issuecomment-328574220)

named capture groupはstringiの領分で、issueとしては積まれているんですが、止まったままです。

どうもICUのバージョンを上げる必要があるらしいですが、色んなOSをサポートする都合上そんな気軽にも上げられない感じらしいです。

Update to ICU 59 needs C++11. Solaris can stay ICU55. Need both icudt55 and icudt59 + separate sources

おのれSolaris...

ということで未来はなかなか見えないですが、はやく入ってほしい機能です。