GitHub | Blog | Qiita

R の Date / POSIXct 型を利用していて過去にハマったポイントを備忘録として整理しておく。

for loop 内で Datenumeric になってしまう問題

現象

  • Date vector に対して for loop でアクセスすると意図した結果にならない
  • for loop 内で class attribute が欠落してしまうことが原因
1
2
3
4
5
dates <- c(as.Date("2020-05-01"), as.Date("2020-05-02"))

for (date in dates) {
  print(date)
}
1
2
[1] 18383
[1] 18384


対策 1: list に変換してからループする

1
2
3
for (date in as.list(dates)) {
  print(date)
}
1
2
[1] "2020-05-01"
[1] "2020-05-02"


対策 2: インデックスでアクセスする

1
2
3
for (i in seq_along(dates)) {
  print(dates[i])
}
1
2
[1] "2020-05-01"
[1] "2020-05-02"


POSIXct から Date への変換で日付がずれる問題

現象

1
2
td <- as.POSIXct("2020-05-01")
as.Date(td)
1
[1] "2020-04-30"


  • これは as.Date() は元の POSIXct のタイムゾーンを意識せず、デフォルトで UTC へ変換してしまうことが原因
    • as.POSIXct() で作成した場合、デフォルトでシステムのタイムゾーンを利用する (この場合は、JST)
    • そのため、JST から 9 時間分の差が発生する
  • 以下の例を見れば、違いが良くわかる
1
2
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
2
[1] "2020-04-30"
[1] "2020-05-01"


対策 1: tz を指定する

1
2
3
4
5
6
7
# 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
[1] "2020-05-01"


対策 2: lubridate::as_date() を利用する

  • lubridate::as_Date() は、元の POSIXct のタイムゾーンを保持して変換してくれる
1
2
td <- as.POSIXct("2020-05-01")
lubridate::as_date(td)
1
[1] "2020-05-01"


ミリ秒の丸め問題

現象

1
2
3
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
[1] "2020-05-01 00:00:00.122 JST"


対策 1: lubridate::ymd_hms() を使う

1
2
options(digits.secs = 3)
lubridate::ymd_hms("2020-05-01 00:00:00.123", tz = "Asia/Tokyo")
1
[1] "2020-05-01 00:00:00.123 JST"


[番外] ミリ秒単位の経過時間を POSIXct に変換する

1
2
3
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
[1] "2020-05-01 00:00:00.123"


  • lubridate::as_datetime() でも同じようにずれるので、+0.0005 する
1
lubridate::as_datetime(msec/1000 + 0.0005, tz = "JST")
1
[1] "2020-05-01 00:00:00.123 JST"


セッション情報

1
sessionInfo()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
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