メモ:lubridateパッケージで区間を指定したいときはperiod、ざっくり時刻計算したいときはduration

任意の時刻型について、floor_date()で区切った区間のちょうど真ん中を返す関数を作りたい。こんな感じの。

library(lubridate)

mannaka <- function(x, binwidth_hour) {
  floor_date(x, hours(binwidth_hour)) + hours(binwidth_hour)/2
}

floor_date()とかの区間"minutes"みたいな文字列だけではなくperiodでも指定できるの知ってました? けっこう便利です。 で、これは一見うまく動きます。

mannaka(Sys.time(), 2)
#> [1] "2018-02-25 15:00:00 JST"

しかし、区間の長さを1にするとうまくいかない。

mannaka(Sys.time(), 1)
#> Error in validObject(.Object): invalid class "Period" object: periods must have integer values

これは、Periodは日付上意味のある切れ目を表すもので、常に一定の長さを持っているとは限らないからです。 つまり具体的には、1時間が3600秒とは限らないわけです。うるう秒とかあるし。

しかしここで足し算をしてるのはそんな厳密な話ではなくて、区間のだいたい真ん中を指してくれればいい。 ということで、そんなときはdurationです。これはPeriodをつくる関数の頭にdをつけた関数名になっています。

dhours(1)/2
#> [1] "1800s (~30 minutes)"

ということで、さっきのdhours()hours()に変えてやってみますがうまくいきません。

mannaka2 <- function(x, binwidth_hour) {
  floor_date(x, dhours(binwidth_hour)) + dhours(binwidth_hour)/2
}

mannaka2(Sys.time(), 1)
#> Error: Invalid period name: s

floor_date()は厳密な区間の区切りを求めるので、ここで使うのはPeriodでなければいけません。ということで前者はhours()に戻すのが正解。

mannaka3 <- function(x, binwidth_hour) {
  floor_date(x, hours(binwidth_hour)) + dhours(binwidth_hour)/2
}

mannaka3(Sys.time(), 1)
#> [1] "2018-02-25 15:30:00 JST"

むずい...