csvなどのデータを読み込んで "2017-09-01 13:15:23" のような時間の表記を含んでいるものをさあ集計しようとすると、秒まで入っているので、
- 時間単位でまとめて集計したい とか
- 月単位でまとめたい集計したい とか
の時に困ったりします。
「何かちゃんとしたやりかたあるんだろうなあ」と思いつつ文字列にして半ば強引に文字列操作でやってました。
# サンプルデータ
test <- as.POSIXct(c(
"2017-09-01 14:31:21",
"2017-09-01 15:40:43",
"2017-09-01 16:20:26",
"2017-09-01 17:22:16",
"2017-09-01 18:30:37"
))
文字列で半ば力技
> # 時間で丸める
> paste(substr(test,1,14),"00",sep="")
"2017-09-01 15:00" "2017-09-01 15:00" "2017-09-01 16:00" "2017-09-01 16:00" "2017-09-01 16:00"
> # 月単位で丸める
> paste(substr(test,1,8),"01",sep="")
"2017-09-01" "2017-09-01" "2017-09-01" "2017-09-01" "2017-09-01"
これで集計はできなくはないけど、POSIXctせずに文字列のままいけるけど、
なんだかなあと思う日々。
それに5分とか15分とかでまとめたい時に逆に面倒になる。
丸めるならroundじゃない?
と思って調べてみると、できそう。
> # 分で丸める
> round(test,"mins")
[1] "2017-09-01 15:31:00 JST" "2017-09-01 15:41:00 JST" "2017-09-01 16:20:00 JST" "2017-09-01 16:22:00 JST" "2017-09-01 16:31:00 JST"
> # 時間で丸める
> round(test,"hours")
[1] "2017-09-01 16:00:00 JST" "2017-09-01 16:00:00 JST" "2017-09-01 16:00:00 JST" "2017-09-01 16:00:00 JST" "2017-09-01 17:00:00 JST"
> # 日で丸める
> round(test,"days")
[1] "2017-09-02 JST" "2017-09-02 JST" "2017-09-02 JST" "2017-09-02 JST" "2017-09-02 JST"
おお、いい感じ。
でも調子に乗ると、、
> # 月で丸めたい!
> round(test,"month")
match.arg(units) でエラー:
'arg' は “secs”, “mins”, “hours”, “days” の一つでなければなりません:
> # 15分で丸めたい!
> round(test,"15 mins")
match.arg(units) でエラー:
'arg' は “secs”, “mins”, “hours”, “days” の一つでなければなりません:
oh.. 使えるのは 秒、分、時間、日 の単位のみ。
あと一息、帯に短い。。
まさかcutが使えるとは。
数字のベクトルをよしなに切り分けてくれるcut。
この子が時間でも切り分けてくれるらしい。灯台下暗し。
> # 15分で丸める
> cut(test,"15 mins")
[1] 2017-09-01 14:31:00 2017-09-01 15:31:00 2017-09-01 16:16:00 2017-09-01 17:16:00 2017-09-01 18:16:00
16 Levels: 2017-09-01 14:31:00 2017-09-01 14:46:00 2017-09-01 15:01:00 2017-09-01 15:16:00 2017-09-01 15:31:00 ... 2017-09-01 18:16:00
> # 2時間で丸める
> cut(test,"2 hours")
[1] 2017-09-01 14:00:00 2017-09-01 14:00:00 2017-09-01 16:00:00 2017-09-01 16:00:00 2017-09-01 18:00:00
Levels: 2017-09-01 14:00:00 2017-09-01 16:00:00 2017-09-01 18:00:00
> # 月で丸める
> cut(test,"month")
[1] 2017-09-01 2017-09-01 2017-09-01 2017-09-01 2017-09-01
Levels: 2017-09-01
> # 年だってOK
> cut(test,"years")
[1] 2017-01-01 2017-01-01 2017-01-01 2017-01-01 2017-01-01
Levels: 2017-01-01
これはちょっとうれしい。
※ただしfactor形式になるので注意。
(追記)パッケージlubridateを使う。
コメントを頂いて、tidyverseの中のlubridateでもできるとのこと。
lubridateは ymd()だとか mdy()だとかくらいしか認知していなかったけど、こんなのもあるのね。
> library(lubridate)
> # 15分で丸める
> round_date(test,"15 mins")
[1] "2017-09-01 14:30:00 JST" "2017-09-01 15:45:00 JST" "2017-09-01 16:15:00 JST" "2017-09-01 17:15:00 JST" "2017-09-01 18:30:00 JST"
> # 2時間で丸める
> round_date(test,"2 hours")
[1] "2017-09-01 14:00:00 JST" "2017-09-01 16:00:00 JST" "2017-09-01 16:00:00 JST" "2017-09-01 18:00:00 JST" "2017-09-01 18:00:00 JST"
> # 月で丸める
> round_date(test,"month")
[1] "2017-09-01 JST" "2017-09-01 JST" "2017-09-01 JST" "2017-09-01 JST" "2017-09-01 JST"
> # 年だってOK
> round_date(test,"years")
[1] "2018-01-01 JST" "2018-01-01 JST" "2018-01-01 JST" "2018-01-01 JST" "2018-01-01 JST"
しかも返り値がPOSIXctのままで便利。
マニュアル見ると、
"week", "bimonth", "quarter"=="3 months", "halfyear" などでもOKらしい。
また、加えて、丸めるだけでなく、切り捨てや切り上げも出来て便利。
> test
[1] "2017-09-01 14:31:21 JST" "2017-09-01 15:40:43 JST" "2017-09-01 16:20:26 JST" "2017-09-01 17:22:16 JST"
[5] "2017-09-01 18:30:37 JST"
> # そのまま丸める
> round_date(test,unit="5 mins")
[1] "2017-09-01 14:30:00 JST" "2017-09-01 15:40:00 JST" "2017-09-01 16:20:00 JST" "2017-09-01 17:20:00 JST"
[5] "2017-09-01 18:30:00 JST"
> # 切り捨て(それより小さい最大)
> floor_date(test,unit="5 mins")
[1] "2017-09-01 14:30:00 JST" "2017-09-01 15:40:00 JST" "2017-09-01 16:20:00 JST" "2017-09-01 17:20:00 JST"
[5] "2017-09-01 18:30:00 JST"
> # 切り下げ(それより大きい最少)
> ceiling_date(test,unit="5 mins")
[1] "2017-09-01 14:35:00 JST" "2017-09-01 15:45:00 JST" "2017-09-01 16:25:00 JST" "2017-09-01 17:25:00 JST"
[5] "2017-09-01 18:35:00 JST"
>
enjoy!