从经纬度转换为本地时区时间R

6

我有一个数据框,其中包含很多位置(约30,000个),我需要将每个位置的时间转换为本地时间。我尝试了一些想法,比如这个这个。但它们对我没有起作用。

我有这样的数据:

dt = data.table(date = c("2018-01-16 22:02:37",
                         "2018-01-16 22:54:00", 
                         "2018-01-16 23:08:38"),
                lat = c(-54.5010,
                        -54.5246,
                        -54.5285),
                long = c(-25.0433, 
                         -25.0929,
                         -25.0832))

我期望得到这个输出:

date                      lat           long
2018-01-16 20:02:37       -54.5010      -25.0433
2018-01-16 20:54:00       -54.5246      -25.0929
2018-01-16 21:08:38       -54.5285      -25.0832

一次尝试:

library(sf)
dt = data.table(date = c("2018-01-16 22:02:37",
                         "2018-01-16 22:54:00", 
                         "2018-01-16 23:08:38"),
                lat = c(-54.5010,
                        -54.5246,
                        -54.5285),
                long = c(-25.0433, 
                         -25.0929,
                         -25.0832))

sdf = st_as_sf(dt, coords = c("long", "lat"), crs = 4326)

## import timezones (after extraction) and intersect with spatial points
tzs = st_read("timezones.geojson/combined.json", quiet = TRUE) #HERE DONT WORK
sdf = st_join(sdf, tzs)

## convert timestamps to local time
sdf$timeL = as.POSIXlt(sdf$time1, tz = as.character(sdf$tzid))
sdf$timeL

Cannot open data source timezones.geojson/combined.json

Error in CPL_read_ogr(dsn, layer, query, as.character(options), quiet,  :
  Open failed.

然后我尝试了:

library(lutz)
library(sf)
library(purrr)
library(dplyr)

download.file("https://github.com/evansiroky/timezone-boundary-builder/releases/download/2019a/timezones-with-oceans.geojson.zip",
              destfile = "tz.zip")
unzip("tz.zip", exdir = "data-raw/dist/")
tz_full <- read_sf("data-raw/dist/combined-with-oceans.json")

但这还是没有起作用。

Cannot open data source ~/Dropbox/Érika Project/mestrado_R/bhv_loc_R/tables/data-raw/dist/combined-with-oceans.json
Error in CPL_read_ogr(dsn, layer, query, as.character(options), quiet,  : 
  Open failed.

我像这样得到了它:
library(lutz)

v <- tz_lookup_coords(lat = dt$lat, lon = dt$lon, method = "accurate")
v1<-as.data.frame(v)

输出结果:
[1] "America/Bahia" "Etc/GMT+3"     "Etc/GMT+3"     "Etc/GMT+3"     "Etc/GMT+3"     "Etc/GMT+3"   

然而,我不知道如何将时区进行转换,针对这个输出结果。

我想尝试做类似于这样的操作:

v1$tzone <- NA
v1[v1$v == "America/Bahia", "tzone"] <- "+3" 
v1[v1$v == "America/Sao_Paulo", "tzone"] <- "+3" 
v1[v1$v == "Etc/GMT+2", "tzone"] <- "+2" 
v1[v1$v == "Etc/GMT+3", "tzone"] <- "+3" 

  if (v1$tzone == "+3" ) {
  v1$timeBR <- NA
  v1$timeBR <- strptime(v1$time, format = "%Y-%m-%d %H:%M:%S")
  v1$timeBR <- v1$timeBR -3*3600 #creating a column corresponding to local Brazilian time (UTC -3)
  v1$hourBR <- as.POSIXlt(v1$timeBR)$hour
  v1 <- v1[!is.na(v1$timeBR),]
  }

#But the function not works (I dont know do functions), would gonna be better one function with the two condition +3 and +2 

编辑

根据建议:

> library(data.table)
data.table 1.12.8 using 2 threads (see ?getDTthreads).  Latest news: r-datatable.com
Warning message:
package ‘data.table’ was built under R version 3.5.2 
> library(lutz)
Warning message:
package ‘lutz’ was built under R version 3.5.2 
> library(purrr)

Attaching package: ‘purrr’

The following object is masked from ‘package:data.table’:

    transpose

> library(lubridate)

Attaching package: ‘lubridate’

The following objects are masked from ‘package:data.table’:

    hour, isoweek, mday, minute, month, quarter, second, wday, week, yday, year

The following object is masked from ‘package:base’:

    date

#t it is the original data
> head(t$time)
[1] 2017-10-16 17:01:00 2017-10-16 18:35:22 2017-10-16 20:38:54 2017-10-16 21:27:27 2017-10-16 21:43:20
[6] 2017-10-16 23:24:46
27092 Levels: 2016-10-24 15:42:00 2016-10-24 21:03:28 2016-10-24 22:04:35 2016-10-24 23:13:40 ... 2020-01-10 11:34:21
> class(t$time)
[1] "factor"
> date2<-t$time
> class(date2)
[1] "factor"
> date2<- as.character(t$time)
> class(date2)
[1] "character"
> head(date2)
[1] "2017-10-16 17:01:00" "2017-10-16 18:35:22" "2017-10-16 20:38:54" "2017-10-16 21:27:27" "2017-10-16 21:43:20"
[6] "2017-10-16 23:24:46"
> t[, date2 := as.POSIXct(date2, format = "%Y-%m-%d %H:%M:%S", tz = "GMT")][,
+                                                                          timezone := tz_lookup_coords(lat = lat, lon = long, method = "accurate")][,
+                                                                                                                                                    new_time := map2(.x = date2, .y = timezone, 
+                                                                                                                                                                     .f = function(x, y) {with_tz(time = x, tzone = y)})][]
Error in `:=`(date2, as.POSIXct(date2, format = "%Y-%m-%d %H:%M:%S", tz = "GMT")) : 
  Check that is.data.table(DT) == TRUE. Otherwise, := and `:=`(...) are defined for use in j, once only and in particular ways. See help(":=").

编辑2

我发现问题出在哪里了,我的原始数据不是data.table和data.frame格式!现在,我有一个包含在数据中的列表。我正在尝试将其转换为一个新列。

编辑3

现在已经成功了!感谢大家的评论和帮助。

t[, date2 := as.POSIXct(date2, format = "%Y-%m-%d %H:%M:%S", tz = "GMT")]
[,timezone := tz_lookup_coords(lat = lat, lon = lon, method = "accurate")]
[,new_time := map2(.x = date2, .y = timezone,
.f = function(x, y) {with_tz(time = x, tzone = y)})][]

newtime<-do.call(rbind, lapply(t$new_time, as.data.frame))
t$newtime<-paste(newtime$`X[[i]]`)

head(t$newtime)
[1] "2016-10-24 12:42:00" "2016-10-24 18:03:28" "2016-10-24 19:04:35" "2016-10-24 20:13:40" "2016-10-24 21:13:00"
[6] "2016-10-25 02:17:05"


有人知道如何做到这一点吗? 谢谢。

1
我不确定这与您链接的 https://dev59.com/DX_aa4cB1Zd3GeqP1Eg- 有何不同?您能详细说明哪个部分出了问题吗? - thelatemail
这两个函数只允许少数位置,或者只允许一个位置。 - Érika Soares Coelho
1
链接问题的第二个答案(https://dev59.com/DX_aa4cB1Zd3GeqP1Eg-#49996425)似乎只使用本地数据,并且应该支持许多点。 - thelatemail
你需要从链接的网站(https://github.com/evansiroky/timezone-boundary-builder)下载时区边界文件,将其解压到你的工作目录中,然后再尝试运行代码。 - thelatemail
我对sf不是很了解,但是你正在使用read_sf而不是根据之前的答案使用st_read。这会有什么区别吗? - thelatemail
显示剩余4条评论
1个回答

4

我认为这就是你想要的。首先,我创建了一个日期对象。然后,我使用tz_lookup_coords()搜索时区,就像你试图做的那样。接着,我使用with_tz(),它可以获取不同时区的日期时间。请注意,new_time是一个列表,因为str(dt)表示。

library(data.table)
library(lutz)
library(purrr)
library(lubridate)

dt[, date := as.POSIXct(date, format = "%Y-%m-%d %H:%M:%S", tz = "GMT")][,
    timezone := tz_lookup_coords(lat = lat, lon = long, method = "accurate")][,
      new_time := map2(.x = date, .y = timezone, 
                       .f = function(x, y) {with_tz(time = x, tzone = y)})][]

#                  date      lat     long  timezone            new_time
#1: 2018-01-16 22:02:37 -54.5010 -25.0433 Etc/GMT+2 2018-01-16 20:02:37
#2: 2018-01-16 22:54:00 -54.5246 -25.0929 Etc/GMT+2 2018-01-16 20:54:00
#3: 2018-01-16 23:08:38 -54.5285 -25.0832 Etc/GMT+2 2018-01-16 21:08:38

#str(dt)
#Classes ‘data.table’ and 'data.frame': 3 obs. of  5 variables:
# $ date    : POSIXct, format: "2018-01-16 22:02:37" "2018-01-16 22:54:00" "2018-01-16 23:08:38"
# $ lat     : num  -54.5 -54.5 -54.5
# $ long    : num  -25 -25.1 -25.1
# $ timezone: chr  "Etc/GMT+2" "Etc/GMT+2" "Etc/GMT+2"
# $ new_time:List of 3
#  ..$ : POSIXct, format: "2018-01-16 20:02:37"
#  ..$ : POSIXct, format: "2018-01-16 20:54:00"
#  ..$ : POSIXct, format: "2018-01-16 21:08:38"

更多帮助

如果你有一个数据框,你也可以使用tidyverse的方法。我在这里使用了你的dt。我将它转换为了一个data.frame对象。最后需要的是unnest()函数。然后,时间会出现在一列中。

setDF(dt) %>% 
mutate(date = as.POSIXct(date, format = "%Y-%m-%d %H:%M:%S", tz = "GMT"),
       timezone = tz_lookup_coords(lat = lat, lon = long, method = "accurate"),
       new_time = map2(.x = date, .y = timezone, 
                       .f = function(x, y) {with_tz(time = x, tzone = y)})) %>% 
unnest(new_time) 

   date                  lat  long timezone  new_time           
  <dttm>              <dbl> <dbl> <chr>     <dttm>             
1 2018-01-16 22:02:37 -54.5 -25.0 Etc/GMT+2 2018-01-16 20:02:37
2 2018-01-16 22:54:00 -54.5 -25.1 Etc/GMT+2 2018-01-16 20:54:00
3 2018-01-16 23:08:38 -54.5 -25.1 Etc/GMT+2 2018-01-16 21:08:38

> str(foo)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   3 obs. of  5 variables:
 $ date    : POSIXct, format: "2018-01-16 22:02:37" "2018-01-16 22:54:00" "2018-01-16 23:08:38"
 $ lat     : num  -54.5 -54.5 -54.5
 $ long    : num  -25 -25.1 -25.1
 $ timezone: chr  "Etc/GMT+2" "Etc/GMT+2" "Etc/GMT+2"
 $ new_time: POSIXct, format: "2018-01-16 20:02:37" "2018-01-16 20:54:00" "2018-01-16 21:08:38"

谢谢!当我使用示例运行时,一切正常!但是当我尝试使用我的数据时,它就无法工作了。出现了错误:Error in :=(date2, as.POSIXct(date2, format = "%Y-%m-%d %H:%M:%S", tz = "GMT")) : Check that is.data.table(DT) == TRUE. Otherwise, := and :=(...) are defined for use in j, once only and in particular ways. See help(":="). 我注意到在示例中日期是字符型的,所以我也将我的原始数据转换为字符型,但是没有起作用。 - Érika Soares Coelho
2
@ÉrikaSoaresCoelho 我为您添加了一个tydyverse方法。关键是使用unnest()。希望这对您足够了。 - jazzurro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接