我有一个POSIXct
向量,略微误用了这种格式:
> head(df$datetime)
[1] "2016-03-03 12:30:00 UTC" "2016-03-03 12:00:00 UTC" "2016-02-27 09:00:00 UTC" "2016-03-03 17:30:00 UTC"
[5] "2016-03-03 10:30:00 UTC" "2016-03-03 14:30:00 UTC"
这些日期时间标记为UTC时间,但实际上是各种各样的本地时区:
> df %>% select(datetime, timezone) %>% head
datetime timezone
1 2016-03-03 12:30:00 Australia/Melbourne
2 2016-03-03 12:00:00 Europe/Berlin
3 2016-02-27 09:00:00 Europe/Amsterdam
4 2016-03-03 17:30:00 Australia/Brisbane
5 2016-03-03 10:30:00 Europe/Amsterdam
6 2016-03-03 14:30:00 Europe/Berlin
我想将这些日期时间转换为适当的UTC - 在某种意义上,面临here和here的反问题 - 但是我很难过。来自第二个链接的解决方案的变体有效:
get_utc_time <- function(timestamp_local, local_tz) {
l <- lapply(seq(length(timestamp_local)),
function(x) {with_tz(force_tz(timestamp_local[x], tzone=local_tz[x]), tzone='UTC')})
as.POSIXct(combine(l), origin = '1970-01-01 00:00.00', tz = 'UTC')
}
df$datetime_utc <- get_utc_time(df$datetime, df$timezone)
(dplyr::mutate(df, datetime_utc = get_utc_time(datetime, timezone))
,我认为它是等价的,会引发错误。)
但由于这不是矢量化的,因此在具有50万行的data.frame上速度非常慢。有更优雅,更快捷的方法吗?
答案 0 :(得分:10)
最具官方的官员&#39;我知道的方式涉及格式化和重新分析;大卫史密斯had a post on this a while ago在REvolutions博客上。
时间序列库,特别是那些具有时区感知能力的库,也可以做到。这是一种使用RcppCCTZ的方法,它是我的CCTZ包装器(由一些Google员工编写但不是官方Google库) - 它计算两个时区之间的差异(默认为小时)。
library(RcppCCTZ) # you need the GitHub version though
# your data
df <- read.csv(text="datetime,timezone
2016-03-03 12:30:00,Australia/Melbourne
2016-03-03 12:00:00,Europe/Berlin
2016-02-27 09:00:00,Europe/Amsterdam
2016-03-03 17:30:00,Australia/Brisbane
2016-03-03 10:30:00,Europe/Amsterdam
2016-03-03 14:30:00,Europe/Berlin", stringsAsFactor=FALSE)
# parse to POSIXct
df[,"pt"] <- as.POSIXct(df[,"datetime"])
# compute difference
for (i in 1:6)
df[i,"diff"] <- tzDiff("UTC", df[i,"timezone"], df[i,"pt"])
这让我们得到了这个data.frame:
R> df
datetime timezone pt diff
1 2016-03-03 12:30:00 Australia/Melbourne 2016-03-03 12:30:00 11
2 2016-03-03 12:00:00 Europe/Berlin 2016-03-03 12:00:00 1
3 2016-02-27 09:00:00 Europe/Amsterdam 2016-02-27 09:00:00 1
4 2016-03-03 17:30:00 Australia/Brisbane 2016-03-03 17:30:00 10
5 2016-03-03 10:30:00 Europe/Amsterdam 2016-03-03 10:30:00 1
6 2016-03-03 14:30:00 Europe/Berlin 2016-03-03 14:30:00 1
R>
返回解析后的Datetime偏移量也很简单, 但是小辅助函数tzDiff目前没有这样做。如果你想走这条路,我可以把它作为第二个辅助函数添加......
编辑:这是一个有趣的问题。我现在已经向RcppCCTZ添加了一些代码来执行此操作,但它(至少)没有矢量化。也就是说,我们可以使用data.table为IMHO提供更简单的和更快的解决方案。
让我们首先对您的解决方案及其所需的三个软件包进行编码:
library(lubridate)
library(magrittr)
library(dplyr)
useLubridate <- function(df) {
df %>%
group_by(timezone) %>%
mutate(datetime_local = ymd_hms(datetime, tz=unique(timezone))) %>%
mutate(datetime_utc = with_tz(datetime_local, tzone = 'UTC')) %>%
ungroup %>%
select(datetime_local) -> df
df
}
让我们对data.table执行相同的操作:
library(data.table)
useDataTable <- function(df) {
dt <- as.data.table(df)
dt[, pt := as.POSIXct(datetime, tz=timezone[1]), by=timezone]
dt[]
}
请注意,这会返回三列而不是一列。
虽然我们正在努力,但让我们进行一场赛马:
R> library(microbenchmark)
R> microbenchmark( useDataTable(df), useLubridate(df) )
Unit: milliseconds
expr min lq mean median uq max neval cld
useDataTable(df) 1.23148 1.53900 1.61174 1.57635 1.64734 3.85423 100 a
useLubridate(df) 7.51158 8.88734 9.10439 9.19390 9.38032 15.27572 100 b
R>
因此data.table更快,同时还返回更多有用的信息。将第三列整理回data.frame(或类似的)会占用更多时间。
答案 1 :(得分:2)
此dplyr
+ lubridate
解决方案似乎有效且速度非常快:
df %>%
group_by(timezone) %>%
mutate(datetime_local = ymd_hms(datetime, tz=unique(timezone))) %>%
mutate(datetime_utc = with_tz(datetime_local, tzone = 'UTC')) %>%
ungroup %>%
select(datetime_local) -> df
请注意,生成的df中的datetime_local
位于AEST
中,可能看起来并不像您期望的那样。我怀疑这是由于R的限制,POSIXct
向量中的所有元素必须具有相同的时区。因此datetime_local
被AEST
ungroup