Rの日時データ #3 - CSVの読み書き
はじめに
Rの日時データ[1]に関する記事#3です。
今回の#3は、CSVファイルからの読み書きについて紹介します。
(長いので結論を読みたい方はまとめを先にご覧ください)
CSVファイルを読み書きするパッケージ3つ
CSVファイルを読み書きするパッケージは、base
, readr
, data.table
があります。
-
base
: read.csv, write.csv -
readr
: read_csv, write_csv -
data.table
: fread, fwrite
各パッケージの比較はreadr
ライブラリのドキュメントに記載がありますので、参照ください。
本稿では、base
, readr
, data.table
の3つのパッケージついて、
- read/write
- 文字列に明示的にタイムゾーン情報があるか(あり(ISO8601形式) or なし)
によって、以下の12に分けて説明します。
read/write | タイムゾーン情報 | base |
readr |
data.table |
---|---|---|---|---|
read | あり | 1 | 2 | 3 |
read | なし | 4 | 5 | 6 |
write | あり | 7 | 8 | 9 |
write | なし | 10 | 11 | 12 |
read
タイムゾーン情報あり
以下のようなISO8601拡張形式を含むcsvファイルを読み出す場合です。
datetime
2021-08-08T02:00:00+09:00
1. base::read.csv
文字列として読み出されます。
> tf <- tempfile()
> writeLines("datetime\n2021-08-08T02:00:00+09:00", tf)
> df <- read.csv(tf)
> df$datetime
[1] "2021-08-08T02:00:00+09:00"
> class(df$datetime)
[1] "character"
適宜日時オブジェクト(POSIXct型のオブジェクト)に変換する必要があります。
> df$dt <- lubridate::ymd_hms(df$datetime)
> df$dt
[1] "2021-08-07 17:00:00 UTC"
> class(df$dt)
[1] "POSIXct" "POSIXt"
2. readr::read_csv
日時オブジェクト(POSIXct型のオブジェクト)として読み出されます。
> df <- readr::read_csv("datetime\n2021-08-08T02:00:00+09:00")
> df$datetime
[1] "2021-08-07 17:00:00 UTC"
>
正確にパースされていますが、タイムゾーンは必ずUTCが設定されています。
3. data.table::fread
動作としては、readr::read_csv
と全く同じです。
> df <- data.table::fread("datetime\n2021-08-08T02:00:00+09:00")
> df$datetime
[1] "2021-08-07 17:00:00 UTC"
>
タイムゾーン情報なし
以下のようなタイムゾーンが無い日時文字列を含むcsvファイルを読み出す場合です。
datetime
2021-08-08 02:00:00
4. base::read.csv
文字列として読み出されます。
> tf <- tempfile()
> writeLines("datetime\n2021-08-08 02:00:00", tf)
> df <- read.csv(tf)
> df$datetime
[1] "2021-08-08 02:00:00"
> class(df$datetime)
[1] "character"
>
適宜日時オブジェクト(POSIXct型のオブジェクト)に変換する必要があります。
5. readr::read_csv
日時オブジェクト(POSIXct型のオブジェクト)として読み出されます。
> df <- readr::read_csv("datetime\n2021-08-08 02:00:00")
> df$datetime
[1] "2021-08-08 02:00:00 UTC"
> class(df$datetime)
[1] "POSIXct" "POSIXt"
>
必ずUTCの時刻として読み出されるので、UTC以外のタイムゾーンの場合には、lubridate::force_tz
関数で変換する必要があります。
> df$dt <- lubridate::force_tz(df$datetime, tzone = "Asia/Tokyo")
> df$dt
[1] "2021-08-08 02:00:00 JST"
>
6. data.table::fread
動作としては、readr::read_csv
と全く同じです。
必ずUTCの時刻として読み出されるので、UTC以外のタイムゾーンの場合には、lubridate::force_tz
関数で変換する必要があります。
> df <- data.table::fread("id,datetime\n1,2021-08-08 02:00:00")
> df$datetime
[1] "2021-08-08 02:00:00 UTC"
> class(df$datetime)
[1] "POSIXct" "POSIXt"
> df$dt <- lubridate::force_tz(df$datetime, tzone = "Asia/Tokyo")
> df$dt
[1] "2021-08-08 02:00:00 JST"
>
write
以下のようなdata.frameを書き込みする場合です。
> library(lubridate)
> dt <- with_tz(ymd_hms("2021-08-08T02:00:00+09:00"), tz = "Asia/Tokyo")
> dt
[1] "2021-08-08 02:00:00 JST"
> class(dt)
[1] "POSIXct" "POSIXt"
> x <- data.frame(id = "a", timestamp = dt)
> x
id timestamp
1 a 2021-08-08 02:00:00
> x$timestamp
[1] "2021-08-08 02:00:00 JST"
> class(x$timestamp)
[1] "POSIXct" "POSIXt"
タイムゾーン情報あり
7. base::write.csv
日時オブジェクトを文字列に変換してから書き込む必要があります。
> library(lubridate)
> dt <- with_tz(ymd_hms("2021-08-08T02:00:00+09:00"), tz = "Asia/Tokyo")
> x <- data.frame(id = "a", timestamp = dt)
> x$timestamp <- format(x$timestamp, "%Y-%m-%dT%H:%M:%S+09:00", tz="Asia/Tokyo")
> x$timestamp
[1] "2021-08-08T02:00:00+09:00"
> class(x$timestamp)
[1] "character"
> write.csv(x, "base.csv")
> print(read_file("base.csv"))
[1] "\"\",\"id\",\"timestamp\"\n\"1\",\"a\",\"2021-08-08T02:00:00+09:00\"\n"
>
以下のように、日時オブジェクトのまま書き込むとISO8601形式にならないので注意が必要です。
> library(lubridate)
> dt <- with_tz(ymd_hms("2021-08-08T02:00:00+09:00"), tz = "Asia/Tokyo")
> dt
[1] "2021-08-08 02:00:00 JST"
> class(dt)
[1] "POSIXct" "POSIXt"
> x <- data.frame(id = "a", timestamp = dt)
> write.csv(x, "base.csv")
> print(read_file("base.csv"))
[1] "\"\",\"id\",\"timestamp\"\n\"1\",\"a\",2021-08-08 02:00:00\n"
>
8. readr::write_csv
日時オブジェクトをそのまま書き込むと、ISO8601形式かつUTCになります。
> library(lubridate)
> dt <- with_tz(ymd_hms("2021-08-08T02:00:00+09:00"), tz = "Asia/Tokyo")
> x <- data.frame(id = "a", timestamp = dt)
> x
id timestamp
1 a 2021-08-08 02:00:00
> readr::write_csv(x, "readr.csv")
> print(read_file("readr.csv"))
[1] "id,timestamp\na,2021-08-07T17:00:00Z\n"
>
UTC以外にするには日時オブジェクトを文字列に変換してから書き込む必要があります。
> x$timestamp <- format(x$timestamp, "%Y-%m-%dT%H:%M:%S+09:00", tz="Asia/Tokyo")
> x$timestamp
[1] "2021-08-08T02:00:00+09:00"
> class(x$timestamp)
[1] "character"
> readr::write_csv(x, "readr.csv")
> print(read_file("readr.csv"))
[1] "id,timestamp\na,2021-08-08T02:00:00+09:00\n"
>
9. data.table::fwrite
動作としては、readr::write_csv
と全く同じです。ISO8601形式かつUTCになりました。
> library(lubridate)
> dt <- with_tz(ymd_hms("2021-08-08T02:00:00+09:00"), tz = "Asia/Tokyo")
> x <- data.frame(id = "a", timestamp = dt)
> x
id timestamp
1 a 2021-08-08 02:00:00
> data.table::fwrite(x, "dtable.csv")
> print(read_file("dtable.csv"))
[1] "id,timestamp\na,2021-08-07T17:00:00Z\n"
>
こちらも同様に、UTC以外にするには日時オブジェクトを文字列に変換してから書き込む必要があります。
> x$timestamp <- format(x$timestamp, "%Y-%m-%dT%H:%M:%S+09:00", tz="Asia/Tokyo")
> x$timestamp
[1] "2021-08-08T02:00:00+09:00"
> class(x$timestamp)
[1] "character"
> data.table::fwrite(x, "dtable.csv")
> print(read_file("dtable.csv"))
[1] "id,timestamp\na,2021-08-08T02:00:00+09:00\n"
>
タイムゾーン情報なし
以下いずれのパッケージの場合でも、日時オブジェクトを文字列に変換してから書き込む必要があります。
10. base::write.csv
> library(lubridate)
> dt <- with_tz(ymd_hms("2021-08-08T02:00:00+09:00"), tz = "Asia/Tokyo")
> x <- data.frame(id = "a", timestamp = dt)
> x
id timestamp
1 a 2021-08-08 02:00:00
> x$timestamp
[1] "2021-08-08 02:00:00 JST"
> class(x$timestamp)
[1] "POSIXct" "POSIXt"
> x$timestamp <- format(x$timestamp, "%Y-%m-%d %H:%M:%S", tz="Asia/Tokyo")
> x
id timestamp
1 a 2021-08-08 02:00:00
> x$timestamp
[1] "2021-08-08 02:00:00"
> class(x$timestamp)
[1] "character"
> write.csv(x, "base.csv")
> print(read_file("base.csv"))
[1] "\"\",\"id\",\"timestamp\"\n\"1\",\"a\",\"2021-08-08 02:00:00\"\n"
>
11. readr::write_csv
> library(lubridate)
> library(readr)
> dt <- with_tz(ymd_hms("2021-08-08T02:00:00+09:00"), tz = "Asia/Tokyo")
> x <- data.frame(id = "a", timestamp = dt)
> x
id timestamp
1 a 2021-08-08 02:00:00
> x$timestamp
[1] "2021-08-08 02:00:00 JST"
> class(x$timestamp)
[1] "POSIXct" "POSIXt"
> x$timestamp <- format(x$timestamp, "%Y-%m-%d %H:%M:%S", tz="Asia/Tokyo")
> x
id timestamp
1 a 2021-08-08 02:00:00
> x$timestamp
[1] "2021-08-08 02:00:00"
> class(x$timestamp)
[1] "character"
> readr::write_csv(x, "readr.csv")
> print(read_file("readr.csv"))
[1] "id,timestamp\na,2021-08-08 02:00:00\n"
>
12. data.table::fwrite
> library(lubridate)
> library(data.table)
> dt <- with_tz(ymd_hms("2021-08-08T02:00:00+09:00"), tz = "Asia/Tokyo")
> x <- data.frame(id = "a", timestamp = dt)
> x
id timestamp
1 a 2021-08-08 02:00:00
> x$timestamp
[1] "2021-08-08 02:00:00 JST"
> class(x$timestamp)
[1] "POSIXct" "POSIXt"
> x$timestamp <- format(x$timestamp, "%Y-%m-%d %H:%M:%S", tz="Asia/Tokyo")
> x
id timestamp
1 a 2021-08-08 02:00:00
> x$timestamp
[1] "2021-08-08 02:00:00"
> class(x$timestamp)
[1] "character"
> data.table::fwrite(x, "dtable.csv")
> print(read_file("dtable.csv"))
[1] "id,timestamp\na,2021-08-08 02:00:00\n"
>
まとめ
長い記事になりましたが、雑にまとめると各パッケージは以下のような動作になります。
base
: read/writeとも文字列として扱う
readr
,data.table
: read/writeとも日時オブジェクトをそのまま扱う
個人的には、CSVファイルの読み書きで日時データの曖昧さを回避可能な方法は、以下の組み合わせだと思っているところです。
-
readr
,data.table
で、ISO8601形式かつUTCで書き込み -
readr
,data.table
で、ISO8601形式を読み出し
逆に注意が必要なのは、7. base::write.csvのケースで、特に注意せずbase::write.csv
を使って書き込みすると、タイムゾーン情報のない2021-08-08 02:00:00
の形式で書き込まれてしまい、別途ドキュメント等でカラムの仕様を提示する必要があります。
-
「日時データ」や「日時オブジェクト」といった用語は、野間口謙太郎, 菊池泰樹 訳 「統計学:Rを用いた入門書 第2版」共立出版 2016 pp.348-349 を参考にしています。 ↩︎
Discussion