R の Date
/ POSIXct
型を利用していて過去にハマったポイントを備忘録として整理しておく。
for
loop 内で Date
が numeric
になってしまう問題
現象
-
Date
vector
に対してfor
loop でアクセスすると意図した結果にならない -
for
loop 内で class attribute が欠落してしまうことが原因- For loops in R can lose class information@R-bloggers
-
Date
はnumeric
に class attribute を追加したものであるため
dates <- c(as.Date("2020-05-01"), as.Date("2020-05-02"))
for (date in dates) {
print(date)
}
[1] 18383
[1] 18384
対策 1: list
に変換してからループする
for (date in as.list(dates)) {
print(date)
}
[1] "2020-05-01"
[1] "2020-05-02"
対策 2: インデックスでアクセスする
for (i in seq_along(dates)) {
print(dates[i])
}
[1] "2020-05-01"
[1] "2020-05-02"
POSIXct
から Date
への変換で日付がずれる問題
現象
td <- as.POSIXct("2020-05-01")
as.Date(td)
[1] "2020-04-30"
- これは
as.Date()
は元のPOSIXct
のタイムゾーンを意識せず、デフォルトで UTC へ変換してしまうことが原因-
as.POSIXct()
で作成した場合、デフォルトでシステムのタイムゾーンを利用する (この場合は、JST) - そのため、JST から 9 時間分の差が発生する
-
- 以下の例を見れば、違いが良くわかる
as.Date(as.POSIXct("2020-05-01 8:00:00")) # 2020-04-30 23:00 へ変換されてから、時間情報が削除されている
as.Date(as.POSIXct("2020-05-01 9:00:00")) # 2020-05-01 00:00 へ変換されてから、時間情報が削除されている
[1] "2020-04-30"
[1] "2020-05-01"
対策 1: tz
を指定する
- 変換前と変換後のタイムゾーンを揃えることを意識しておけば良い
- タイムゾーンは、“Area/Locality” の形式で指定すべき
# UTC に統一して変換
td <- as.POSIXct("2020-05-01", tz = "UTC")
as.Date(td)
# もしくは、JST に統一して変換
## td <- as.POSIXct("2020-05-01")
## as.Date(td, tz = "Asia/Tokyo")
[1] "2020-05-01"
対策 2: lubridate::as_date()
を利用する
-
lubridate::as_Date()
は、元のPOSIXct
のタイムゾーンを保持して変換してくれる
td <- as.POSIXct("2020-05-01")
lubridate::as_date(td)
[1] "2020-05-01"
ミリ秒の丸め問題
現象
- 文字列から
POSIXct
を作成する際に、ミリ秒がずれる (切り捨てられる)- R issue with rounding milliseconds@Stackoverflow
-
format
の%OS
は"second.millisecond"
の形式
options(digits.secs = 3)
ms_dt <- as.POSIXct("2020-05-01 00:00:00.123", format = "%Y-%m-%d %H:%M:%OS")
ms_dt
[1] "2020-05-01 00:00:00.122 JST"
対策 1: lubridate::ymd_hms()
を使う
options(digits.secs = 3)
lubridate::ymd_hms("2020-05-01 00:00:00.123", tz = "Asia/Tokyo")
[1] "2020-05-01 00:00:00.123 JST"
[番外] ミリ秒単位の経過時間を POSIXct
に変換する
- R How to convert milliseconds from origin to date and keep the milliseconds@Stackoverflow
- 株価のティックデータなど、ミリ秒単位の経過時間で表現されるデータがある
- 1000 で割って秒数に換算する
- +0.0005 を足すことで丸め誤差を消すことができる
msec <- 1588291200123 # 2020-05-01 00:00:00.123 JST
dt <- as.POSIXct(msec/1000, origin = "1970-01-01", tz = "JST")
format(dt + 0.0005, "%Y-%m-%d %H:%M:%OS")
[1] "2020-05-01 00:00:00.123"
-
lubridate::as_datetime()
でも同じようにずれるので、+0.0005 する
lubridate::as_datetime(msec/1000 + 0.0005, tz = "JST")
[1] "2020-05-01 00:00:00.123 JST"
セッション情報
sessionInfo()
R version 3.6.3 (2020-02-29)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.4 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=C
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=C
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
loaded via a namespace (and not attached):
[1] compiler_3.6.3 generics_0.0.2 tools_3.6.3 Rcpp_1.0.4.6
[5] lubridate_1.7.8