read_csv_arrow 中来自 R 中箭头包的时间戳解析问题
Issue with timestamp parsing in read_csv_arrow from arrow package in R
假设有一个名为 ta_sample.csv
的 csv
文件,如下所示:
"BILL_DT","AMOUNT"
"2015-07-27T18:30:00Z",16000
"2015-07-07T18:30:00Z",6110
"2015-07-26T18:30:00Z",250
"2015-07-22T18:30:00Z",1000
"2015-07-06T18:30:00Z",2640000
阅读以上使用read_csv_arrow
并自定义实际生产数据中始终需要的列类型:
library(arrow)
read_csv_arrow(
"ta_sample.csv",
col_names = c("BILL_DT", "AMOUNT"),
col_types = "td",
skip = 1,
timestamp_parsers = c("%Y-%m-%dT%H:%M:%SZ"))
结果如下:
# A tibble: 5 x 2
BILL_DT AMOUNT
<dttm> <dbl>
1 2015-07-28 00:00:00 16000
2 2015-07-08 00:00:00 6110
3 2015-07-27 00:00:00 250
4 2015-07-23 00:00:00 1000
5 2015-07-07 00:00:00 2640000
这里的问题是日期增加了一天而时间消失了。这里值得一提的是data.table::fread()
以及readr::read_csv()
读对了,eg,
library(readr)
read_csv("ta_sample.csv")
# A tibble: 5 x 2
BILL_DT AMOUNT
<dttm> <dbl>
1 2015-07-27 18:30:00 16000
2 2015-07-07 18:30:00 6110
3 2015-07-26 18:30:00 250
4 2015-07-22 18:30:00 1000
5 2015-07-06 18:30:00 2640000
使用 strptime
解析 BILL_DT
列中的示例值也可以完美地工作,如下所示:
strptime(c("2015-07-27T18:30:00Z", "2015-07-07T18:30:00Z"), "%Y-%m-%dT%H:%M:%SZ")
[1] "2015-07-27 18:30:00 IST" "2015-07-07 18:30:00 IST"
需要调整 read_csv_arrow
中的哪些参数才能获得与 readr::read_csv()
给出的结果相同的结果?
这里发生了一些事情,但它们都与时区有关 + R + Arrow + 其他包的各个部分如何解释它们。
当 Arrow 读入时间戳时,它会将值视为 UTC。 Arrow 还不能在解析时指定替代时区 [1],因此将这些值存储为无时区(并假定为 UTC)。尽管在这种情况下,由于您拥有的时间戳是 UTC(根据 ISO_8601,末尾的 Z
表示 UTC),它们作为无时区 UTC 时间戳正确存储在 Arrow 中。时间戳的值是相同的(即它们代表相同的 UTC 时间),区别在于它们的显示方式:它们是显示为 UTC 时间还是显示为本地时区。
将时间戳转换为 R 时,将保留时区性:
> from_arrow <- read_csv_arrow(
+ "ta_sample.csv",
+ col_names = c("BILL_DT", "AMOUNT"),
+ col_types = "td",
+ skip = 1,
+ timestamp_parsers = c("%Y-%m-%dT%H:%M:%SZ"))
>
> attr(from_arrow$BILL_DT, "tzone")
NULL
R 默认显示本地时区中没有 tzone
属性的时间戳(对我来说它当前是 CDT,对你来说它看起来像是 IST)。并且请注意,具有明确时区的时间戳显示在该时区中。
> from_arrow$BILL_DT
[1] "2015-07-27 13:30:00 CDT" "2015-07-07 13:30:00 CDT"
[3] "2015-07-26 13:30:00 CDT" "2015-07-22 13:30:00 CDT"
[5] "2015-07-06 13:30:00 CDT"
如果您想显示 UTC 时间戳,您可以做一些事情:
- 显式设置
tzone
属性(或者您可以使用 lubridate::with_tz()
进行相同的操作):
> attr(from_arrow$BILL_DT, "tzone") <- "UTC"
> from_arrow$BILL_DT
[1] "2015-07-27 18:30:00 UTC" "2015-07-07 18:30:00 UTC"
[3] "2015-07-26 18:30:00 UTC" "2015-07-22 18:30:00 UTC"
[5] "2015-07-06 18:30:00 UTC"
- 您可以在 R 会话中设置时区,这样当 R 去显示它使用 UTC 的时间时(注意:
tzone
属性在此处仍未设置,但显示为 UTC,因为会话时区设置为 UTC)
> Sys.setenv(TZ="UTC")
> from_arrow <- read_csv_arrow(
3. "ta_sample.csv",
4. col_names = c("BILL_DT", "AMOUNT"),
5. col_types = "td",
6. skip = 1,
7. timestamp_parsers = c("%Y-%m-%dT%H:%M:%SZ"))
> from_arrow$BILL_DT
[1] "2015-07-27 18:30:00 UTC" "2015-07-07 18:30:00 UTC"
[3] "2015-07-26 18:30:00 UTC" "2015-07-22 18:30:00 UTC"
[5] "2015-07-06 18:30:00 UTC"
> attr(from_arrow$BILL_DT, "tzone")
NULL
- 您可以将数据读入 Arrow table,并在使用
collect()
将数据拉入 R 之前将时间戳转换为在 Arrow 中具有明确的时区。这个 csv -> Arrow table -> data.frame 是幕后发生的事情,所以这里没有额外的转换(除了转换)。如果您正在应用其他转换,那么在箭头 table 上进行操作可能会更有用 + 更高效,尽管它比前两个代码更多。
> library(arrow)
> library(dplyr)
> tab <- read_csv_arrow(
+ "ta_sample.csv",
+ col_names = c("BILL_DT", "AMOUNT"),
+ col_types = "td",
+ skip = 1,
+ as_data_frame = FALSE)
>
> tab_df <- tab %>%
+ mutate(BILL_DT_cast = cast(BILL_DT, timestamp(unit = "s", timezone = "UTC"))) %>%
+ collect()
> attr(tab_df$BILL_DT, "tzone")
NULL
> attr(tab_df$BILL_DT_cast, "tzone")
[1] "UTC"
> tab_df
# A tibble: 5 × 3
BILL_DT AMOUNT BILL_DT_cast
<dttm> <dbl> <dttm>
1 2015-07-27 13:30:00 16000 2015-07-27 18:30:00
2 2015-07-07 13:30:00 6110 2015-07-07 18:30:00
3 2015-07-26 13:30:00 250 2015-07-26 18:30:00
4 2015-07-22 13:30:00 1000 2015-07-22 18:30:00
5 2015-07-06 13:30:00 2640000 2015-07-06 18:30:00
这也让人有点困惑,因为基础 R 的 strptime()
不解析时区(这就是为什么你看到相同的时钟时间但在上面的示例中使用 IST)。 lubridate 的 [2] 解析函数 do 遵守这一点,您可以在这里看到不同之处:
> lubridate::parse_date_time(c("2015-07-27T18:30:00Z", "2015-07-07T18:30:00Z"), "YmdHMS")
[1] "2015-07-27 18:30:00 UTC" "2015-07-07 18:30:00 UTC"
[1] 虽然我们有两个与添加此功能相关的问题 https://issues.apache.org/jira/browse/ARROW-12820 and https://issues.apache.org/jira/browse/ARROW-13348
[2] 而且,lubridate 的文档甚至提到了这一点:
ISO8601 signed offset in hours and minutes from UTC. For example -0800, -08:00 or -08, all represent 8 hours behind UTC. This format also matches the Z (Zulu) UTC indicator. Because base::strptime() doesn't fully support ISO8601 this format is implemented as an union of 4 orders: Ou (Z), Oz (-0800), OO (-08:00) and Oo (-08). You can use these four orders as any other but it is rarely necessary. parse_date_time2() and fast_strptime() support all of the timezone formats.
https://lubridate.tidyverse.org/reference/parse_date_time.html
假设有一个名为 ta_sample.csv
的 csv
文件,如下所示:
"BILL_DT","AMOUNT"
"2015-07-27T18:30:00Z",16000
"2015-07-07T18:30:00Z",6110
"2015-07-26T18:30:00Z",250
"2015-07-22T18:30:00Z",1000
"2015-07-06T18:30:00Z",2640000
阅读以上使用read_csv_arrow
并自定义实际生产数据中始终需要的列类型:
library(arrow)
read_csv_arrow(
"ta_sample.csv",
col_names = c("BILL_DT", "AMOUNT"),
col_types = "td",
skip = 1,
timestamp_parsers = c("%Y-%m-%dT%H:%M:%SZ"))
结果如下:
# A tibble: 5 x 2
BILL_DT AMOUNT
<dttm> <dbl>
1 2015-07-28 00:00:00 16000
2 2015-07-08 00:00:00 6110
3 2015-07-27 00:00:00 250
4 2015-07-23 00:00:00 1000
5 2015-07-07 00:00:00 2640000
这里的问题是日期增加了一天而时间消失了。这里值得一提的是data.table::fread()
以及readr::read_csv()
读对了,eg,
library(readr)
read_csv("ta_sample.csv")
# A tibble: 5 x 2
BILL_DT AMOUNT
<dttm> <dbl>
1 2015-07-27 18:30:00 16000
2 2015-07-07 18:30:00 6110
3 2015-07-26 18:30:00 250
4 2015-07-22 18:30:00 1000
5 2015-07-06 18:30:00 2640000
使用 strptime
解析 BILL_DT
列中的示例值也可以完美地工作,如下所示:
strptime(c("2015-07-27T18:30:00Z", "2015-07-07T18:30:00Z"), "%Y-%m-%dT%H:%M:%SZ")
[1] "2015-07-27 18:30:00 IST" "2015-07-07 18:30:00 IST"
需要调整 read_csv_arrow
中的哪些参数才能获得与 readr::read_csv()
给出的结果相同的结果?
这里发生了一些事情,但它们都与时区有关 + R + Arrow + 其他包的各个部分如何解释它们。
当 Arrow 读入时间戳时,它会将值视为 UTC。 Arrow 还不能在解析时指定替代时区 [1],因此将这些值存储为无时区(并假定为 UTC)。尽管在这种情况下,由于您拥有的时间戳是 UTC(根据 ISO_8601,末尾的 Z
表示 UTC),它们作为无时区 UTC 时间戳正确存储在 Arrow 中。时间戳的值是相同的(即它们代表相同的 UTC 时间),区别在于它们的显示方式:它们是显示为 UTC 时间还是显示为本地时区。
将时间戳转换为 R 时,将保留时区性:
> from_arrow <- read_csv_arrow(
+ "ta_sample.csv",
+ col_names = c("BILL_DT", "AMOUNT"),
+ col_types = "td",
+ skip = 1,
+ timestamp_parsers = c("%Y-%m-%dT%H:%M:%SZ"))
>
> attr(from_arrow$BILL_DT, "tzone")
NULL
R 默认显示本地时区中没有 tzone
属性的时间戳(对我来说它当前是 CDT,对你来说它看起来像是 IST)。并且请注意,具有明确时区的时间戳显示在该时区中。
> from_arrow$BILL_DT
[1] "2015-07-27 13:30:00 CDT" "2015-07-07 13:30:00 CDT"
[3] "2015-07-26 13:30:00 CDT" "2015-07-22 13:30:00 CDT"
[5] "2015-07-06 13:30:00 CDT"
如果您想显示 UTC 时间戳,您可以做一些事情:
- 显式设置
tzone
属性(或者您可以使用lubridate::with_tz()
进行相同的操作):
> attr(from_arrow$BILL_DT, "tzone") <- "UTC"
> from_arrow$BILL_DT
[1] "2015-07-27 18:30:00 UTC" "2015-07-07 18:30:00 UTC"
[3] "2015-07-26 18:30:00 UTC" "2015-07-22 18:30:00 UTC"
[5] "2015-07-06 18:30:00 UTC"
- 您可以在 R 会话中设置时区,这样当 R 去显示它使用 UTC 的时间时(注意:
tzone
属性在此处仍未设置,但显示为 UTC,因为会话时区设置为 UTC)
> Sys.setenv(TZ="UTC")
> from_arrow <- read_csv_arrow(
3. "ta_sample.csv",
4. col_names = c("BILL_DT", "AMOUNT"),
5. col_types = "td",
6. skip = 1,
7. timestamp_parsers = c("%Y-%m-%dT%H:%M:%SZ"))
> from_arrow$BILL_DT
[1] "2015-07-27 18:30:00 UTC" "2015-07-07 18:30:00 UTC"
[3] "2015-07-26 18:30:00 UTC" "2015-07-22 18:30:00 UTC"
[5] "2015-07-06 18:30:00 UTC"
> attr(from_arrow$BILL_DT, "tzone")
NULL
- 您可以将数据读入 Arrow table,并在使用
collect()
将数据拉入 R 之前将时间戳转换为在 Arrow 中具有明确的时区。这个 csv -> Arrow table -> data.frame 是幕后发生的事情,所以这里没有额外的转换(除了转换)。如果您正在应用其他转换,那么在箭头 table 上进行操作可能会更有用 + 更高效,尽管它比前两个代码更多。
> library(arrow)
> library(dplyr)
> tab <- read_csv_arrow(
+ "ta_sample.csv",
+ col_names = c("BILL_DT", "AMOUNT"),
+ col_types = "td",
+ skip = 1,
+ as_data_frame = FALSE)
>
> tab_df <- tab %>%
+ mutate(BILL_DT_cast = cast(BILL_DT, timestamp(unit = "s", timezone = "UTC"))) %>%
+ collect()
> attr(tab_df$BILL_DT, "tzone")
NULL
> attr(tab_df$BILL_DT_cast, "tzone")
[1] "UTC"
> tab_df
# A tibble: 5 × 3
BILL_DT AMOUNT BILL_DT_cast
<dttm> <dbl> <dttm>
1 2015-07-27 13:30:00 16000 2015-07-27 18:30:00
2 2015-07-07 13:30:00 6110 2015-07-07 18:30:00
3 2015-07-26 13:30:00 250 2015-07-26 18:30:00
4 2015-07-22 13:30:00 1000 2015-07-22 18:30:00
5 2015-07-06 13:30:00 2640000 2015-07-06 18:30:00
这也让人有点困惑,因为基础 R 的 strptime()
不解析时区(这就是为什么你看到相同的时钟时间但在上面的示例中使用 IST)。 lubridate 的 [2] 解析函数 do 遵守这一点,您可以在这里看到不同之处:
> lubridate::parse_date_time(c("2015-07-27T18:30:00Z", "2015-07-07T18:30:00Z"), "YmdHMS")
[1] "2015-07-27 18:30:00 UTC" "2015-07-07 18:30:00 UTC"
[1] 虽然我们有两个与添加此功能相关的问题 https://issues.apache.org/jira/browse/ARROW-12820 and https://issues.apache.org/jira/browse/ARROW-13348
[2] 而且,lubridate 的文档甚至提到了这一点:
ISO8601 signed offset in hours and minutes from UTC. For example -0800, -08:00 or -08, all represent 8 hours behind UTC. This format also matches the Z (Zulu) UTC indicator. Because base::strptime() doesn't fully support ISO8601 this format is implemented as an union of 4 orders: Ou (Z), Oz (-0800), OO (-08:00) and Oo (-08). You can use these four orders as any other but it is rarely necessary. parse_date_time2() and fast_strptime() support all of the timezone formats. https://lubridate.tidyverse.org/reference/parse_date_time.html