追記:これは「補間」とは言わないかも…。期待外れだったらすみません。
前にこんな記事を書きました。
で、これはたまたま毎分データがあったからよかったんですが、もっととびとびのデータの時どうするんだろう?と思ってたらtidyrパッケージのcomplete()
とfull_seq()
が便利そうだったのでメモ。
おさらい
complete()
complete()
は、分かりづらいんですが、暗黙の欠損値を明示的な欠損値(NA
とか0
とか)して出現させる関数です。ヘルプにもそう書かれています。
Turns implicit missing values into explicit missing values.
これはどういうことかというと、例えばこういうデータがあったとき、何を思うでしょうか。
library(tidyverse) d <- tribble( ~name, ~lang, ~chottodekiru, "A", "R", TRUE, "A", "Python", TRUE, "B", "R", TRUE, "B", "Python", TRUE, "C", "R", TRUE )
Cさんが果たしてPythonがチョットデキル人なのかどうか、このデータからはわかりません。これをはっきりさせるためにcomplete()
を使うと、こういう感じにchottodekiru
列をNA
で埋めたデータをつくってくれます。
complete(d, name, lang) #> # A tibble: 6 × 3 #> name lang chottodekiru #> <chr> <chr> <lgl> #> 1 A Python TRUE #> 2 A R TRUE #> 3 B Python TRUE #> 4 B R TRUE #> 5 C Python NA #> 6 C R TRUE
NA
以外のもので埋めたいときはfill
引数にリストを指定します。例えば、fill = list(chottodekiru = FALSE)
を指定するとchottodekiru
列ではFALSE
が欠損値を表すものとしてあてがわれます。
full_seq()
full_seq()
は、いい感じに数値列を補完してくれる関数です。
full_seq(c(1, 2, 4, 5, 10), period = 1) #> [1] 1 2 3 4 5 6 7 8 9 10
Date
やPOSIXct
にも使うことができます。ただしhms
とかdifftime
には使えないので注意。period
に指定している数値の単位は秒です。
x <- c(lubridate::now(), lubridate::now() - lubridate::hours(1)) full_seq(x, period = 600) #> [1] "2017-04-04 20:30:02 JST" "2017-04-04 20:40:02 JST" #> [3] "2017-04-04 20:50:02 JST" "2017-04-04 21:00:02 JST" #> [5] "2017-04-04 21:10:02 JST" "2017-04-04 21:20:02 JST" #> [7] "2017-04-04 21:30:02 JST"
組み合わせる。
complete()
は、すでにデータ中に登場している組み合わせしか埋められませんでしたが、full_seq()
をcomplete()
の中で使うとそれをさらに拡張できます。例えば、毎年取られるデータみたいなのがあって、2002年の分だけが欠けているとします。
d <- tribble( ~year, ~value, 1999, 1, 2000, 2, 2001, 3, 2003, 5 )
このとき、full_seq()
をcomplete()
の中で使うとかけている年を補完して欠損値で埋めることができます。
complete(d, year = full_seq(year, 1)) #> # A tibble: 5 × 2 #> year value #> <dbl> <dbl> #> 1 1999 1 #> 2 2000 2 #> 3 2001 3 #> 4 2002 NA #> 5 2003 5
時系列データを埋める
何か毎分行われる処理があって、エラーが起きた時のタイムスタンプだけを記録したデータがあるとします。
set.seed(12) d <- data.frame( timestamps = lubridate::today() + lubridate::minutes(ceiling(runif(30, min = 0, max = 24 * 60))) ) d #> timestamps #> 1 2017-04-04 01:40:00 #> 2 2017-04-04 19:38:00 #> 3 2017-04-04 22:38:00 #> 4 2017-04-04 06:28:00 #> 5 2017-04-04 04:04:00 #> 6 2017-04-04 00:49:00 #> 7 2017-04-04 04:18:00 #> 8 2017-04-04 15:24:00 #> 9 2017-04-04 00:33:00 #> 10 2017-04-04 00:12:00 ...
ggplot(d, aes(timestamps, 1)) + geom_point()
で、これのエラー率みたいなのを計算したい。というとき、とりあえず毎分データを埋めてみてRcppRoll
とかを使う、という手があります。
d %>% # 元のデータにはerror=TRUEを入れておく mutate(error = TRUE) %>% # full_seqで補完されるデータにはerror=FALSEを入れる complete(timestamps = full_seq(timestamps, 60), fill = list(error = FALSE)) %>% # 前後5個づつのデータの平均(前後10分のエラー率)を計算 mutate(error_rate = RcppRoll::roll_mean(error, n = 10, fill = NA)) %>% # プロットする ggplot(aes(timestamps, error_rate)) + geom_line()
感想
とはいえこの例だとあんまり効率的じゃない感じはするので、なんかもっといいやり方ありますよね…。誰かいい感じの方法を教えてください。とりあえずcomplete()
とfull_seq()
の使い方のメモということで。