关于`unnest()`函数处理时区的异常行为技术问询
解决
unnest()处理时区时的异常问题 我之前也碰到过这个坑,unnest()在处理带时区的POSIXct对象时,确实会出现时区被强制覆盖的异常行为。先把完整的复现代码补全,方便重现问题:
library(tidyverse) library(lubridate) # 当前系统时区为Europe/Paris Sys.getenv("TZ") #[1] "Europe/Paris" reprex_unnest <- tribble( ~datetime, ~locale, "2018-03-08 01:00:00", "America/Argentina/Buenos_Aires", "2018-03-08 01:00:00", "Europe/Kiev", "2018-03-08 01:00:00", "UTC" ) %>% mutate( datetime = ymd_hms(datetime, tz = "UTC"), datetime_locale = map2(datetime, locale, ~ with_tz(.x, tzone = .y)) ) %>% unnest(datetime_locale) %>% mutate(tz_extracted = tz(datetime_locale)) print(reprex_unnest)
运行后你会发现,tz_extracted列的值全是Europe/Paris,完全不是我们预期的各个locale对应的时区——这就是unnest()的问题:它在展开列表列时,会强制把所有POSIXct对象转换为当前系统时区,直接丢失了原本的时区信息。
问题原因
unnest()在处理列表列时,会尝试将所有元素统一为同一类型。对于POSIXct这种带时区属性的对象,它没有正确保留每个元素的时区元数据,而是默认用系统时区重新解析,导致原有时区被覆盖。
解决方案
这里有两种可靠的解决方法:
方法1:展开后重新设置时区
既然locale列已经保存了我们需要的时区信息,在unnest()之后直接用with_tz()重新给datetime_locale设置对应时区即可:
reprex_fixed <- tribble( ~datetime, ~locale, "2018-03-08 01:00:00", "America/Argentina/Buenos_Aires", "2018-03-08 01:00:00", "Europe/Kiev", "2018-03-08 01:00:00", "UTC" ) %>% mutate( datetime = ymd_hms(datetime, tz = "UTC"), datetime_locale = map2(datetime, locale, ~ with_tz(.x, tzone = .y)) ) %>% unnest(datetime_locale) %>% # 重新根据locale列设置时区 mutate(datetime_locale = with_tz(datetime_locale, tzone = locale)) # 验证时区是否正确 tz(reprex_fixed$datetime_locale) # 输出: # [1] "America/Argentina/Buenos_Aires" "Europe/Kiev" "UTC"
方法2:用list_rbind()替代unnest()
purrr::list_rbind()在处理带属性的对象时表现更友好,能更好地保留POSIXct的时区信息:
reprex_fixed2 <- tribble( ~datetime, ~locale, "2018-03-08 01:00:00", "America/Argentina/Buenos_Aires", "2018-03-08 01:00:00", "Europe/Kiev", "2018-03-08 01:00:00", "UTC" ) %>% mutate( datetime = ymd_hms(datetime, tz = "UTC"), # 将每个转换后的时间包装成小tibble data = map2(datetime, locale, ~ tibble(datetime_locale = with_tz(.x, tzone = .y))) ) %>% # 用list_rbind展开列表列 list_rbind(data, names_to = NULL) %>% # 合并原有的datetime和locale列 bind_cols(select(., datetime, locale)) # 验证时区 tz(reprex_fixed2$datetime_locale)
这两种方法都能解决时区丢失的问题,你可以根据自己的代码风格选择合适的方式。
内容的提问来源于stack exchange,提问作者JeromeLaurent
相关产品推荐
相关产品推荐

