Rの日時データ #1 - 躓きポイント2つ
はじめに
今回からRの日時データ[1]について何回か書いていきたいと思います。
#1の今回は、Rで日時データを扱っていて躓いた以下の2点を紹介します。
- 実行環境により
as.POSIXct
の結果が異なる - ISO8601形式をパースできない
as.POSIXct
の結果が異なる
1. 実行環境により以下は、POSIXct型のオブジェクトを作成する全く同一のコードas.POSIXct("2011-03-20 12:34:56")
を、いくつかの実行環境で実行したものですが、結果が異なっています。
- ローカルマシン(Mac)上
> as.POSIXct("2011-03-20 12:34:56")
[1] "2011-03-20 12:34:56 JST"
- Google Compute Engineのインスタンス(Linux)上
> as.POSIXct("2011-03-20 12:34:56")
[1] "2011-03-20 12:34:56 UTC"
- Google Colaboratory上
%%R
as.POSIXct("2011-03-20 12:34:56")
[1] "2011-03-20 12:34:56 UTC"
このようなコードがgitに入っていて複数人の環境で実行すると、環境によって結果が一致しないという事態が発生してしまいます。
原因
原因は、引数tz
を指定せずas.POSIXct("2011-03-20 12:34:56")
のように実行すると、システムのタイムゾーンを参照することにあります。
システムのタイムゾーンは、Sys.timezone()
で調べることが可能です。上記のそれぞれの環境で、Sys.timezone()
を実行すると、返ってくる値が異なることが分かると思います。
- ローカルマシン(Mac)上
> Sys.timezone()
[1] "Asia/Tokyo"
- Google Compute Engineのインスタンス(Linux)上
> Sys.timezone()
[1] "Etc/UTC"
- Google Colaboratory上
%%R
Sys.timezone()
[1] "Etc/UTC"
対応方法
対応としては、2つの方法があります。
-
as.POSIXct
でtz
引数を指定する -
Sys.setenv
で環境変数TZ
を設定する
as.POSIXct
でtz
引数を指定する
以下のように、tz
引数として"Asia/Tokyo"
を明示的に指定すれば、どの環境でも同じ日時を差すPOSIXct型のオブジェクトを生成できます。
> as.POSIXct("2011-03-20 12:34:56", tz="Asia/Tokyo")
[1] "2011-03-20 12:34:56 JST"
Sys.setenv
で環境変数TZ
を設定する
as.POSIXct
を呼び出す前に、Sys.setenv
で環境変数TZ
を必ず設定するようにしておくのが、もう一つの方法です。但しこれはコードの冒頭に書くなど工夫が必要なのが難点と言えます。
> Sys.timezone()
[1] "Asia/Tokyo"
> Sys.getenv('TZ')
[1] ""
> as.POSIXct("2011-03-20 12:34:56")
[1] "2011-03-20 12:34:56 JST"
> Sys.setenv(TZ='UTC')
> Sys.getenv('TZ')
[1] "UTC"
> as.POSIXct("2011-03-20 12:34:56")
[1] "2011-03-20 12:34:56 UTC"
>
timezones {base}
ヘルプの見解
timezones {base}のドキュメントには、以下のような記述があります。
Time zones are a system-specific topic,
とした上で、以下のように、環境変数TZ
による設定は、Rのプロセス起動前に(.Rprofile
に記述するなどで)実施すると、Sys.timezone()
に反映されるとしていますが、この対応を実施したところで環境依存であることに変わりはないでしょう。コードのみで一致した結果を得るには、上で紹介した2つの対応方法が良さそうです。
It should be possible to set the current time zone via the environment variable TZ: see the section on ‘Time zone names’ for suitable values. Sys.timezone() will return the value of TZ if set initially (and on some OSes it is always set), otherwise it will try to retrieve from the OS a value which if set for TZ would give the initial time zone. (‘Initially’ means before any time-zone functions are used: if TZ is being set to override the OS setting or if the ‘try’ does not get this right, it should be set before the R process is started or (probably early enough) in file .Rprofile).
ISO8601形式をパースできない
2.baseパッケージにISO8601形式(よく使われる2022-11-24T00:30:38+09:00
のような拡張形式と呼ばれるもの)をパースする関数が存在しないようでした。
> as.POSIXct("2022-11-24T00:30:38+09:00")
[1] "2022-11-24 JST"
ISO8601形式の文字列をas.POSIXct
関数でパースしようとすると、T
区切り記号(timeを表す cf. https://en.wikipedia.org/wiki/ISO_8601#Times )を解釈できず、年月日までのオブジェクトになっていました。
原因
POSIXct
はその名のとおり、POSIX時間を対象とした型であり、ISO8601には対応していないのが原因と思われます。
as.POSIXct
関数のヘルプを参照すると以下の記述があり、2001-02-03 14:52:03
のようなスペース区切りの形式の文字列を受け付けるとありました。
They can also convert character strings of the formats "2001-02-03" and "2001/02/03" optionally followed by white space and a time in the format "14:52" or "14:52:03".
また、as.POSIXct
関数のヘルプが参照しているstrftime
関数のヘルプによれば、formatのデフォルトとしては、"%Y-%m-%d %H:%M:%S"
が使われているようでした。
The default for the format methods is "%Y-%m-%d %H:%M:%S"
対応方法
lubridate
パッケージをインストールし、ymd_hms
関数を使うと解決できます。
> lubridate::ymd_hms("2022-11-24T00:30:38+09:00")
[1] "2022-11-23 15:30:38 UTC"
>
lubridate
パッケージは、他にも
をはじめとして便利な関数があるので、一通りチェックするのがおすすめです。
まとめ
以上、Rの日時データについて私自身が特に嵌ったポイント2つでした。
次回の#2は、今回紹介した手順も含めた文字列⇔日時オブジェクトの変換について紹介したいと思います。
参考
- https://oku.edu.mie-u.ac.jp/~okumura/stat/timeseries.html
- http://www.okadajp.org/RWiki/?日付、時間関数Tips大全
- https://search.r-project.org/R/refmans/base/html/as.POSIXlt.html
- https://search.r-project.org/R/refmans/base/html/strptime.html
- https://lubridate.tidyverse.org/
- https://ja.wikipedia.org/wiki/ISO_8601
-
「日時データ」や「日時オブジェクト」といった用語は、野間口謙太郎, 菊池泰樹 訳 「統計学:Rを用いた入門書 第2版」共立出版 2016 pp.348-349 を参考にしています。 ↩︎
Discussion