Я столкнулся с проблемой кодирования (оптимизации) в R. У меня есть длинный набор данных с координатами GPS (долгота, широта, временная метка), и для каждой строки мне нужно проверять, находится ли местоположение рядом с автобусной остановкой. У меня есть файл .csv со всеми автобусными остановками (в Нидерландах). Файл с координатами GPS состоит из миллионов записей, но при необходимости его можно разделить. Набор данных об автобусных остановках насчитывает около 5500 записей.
Использование кода и советов, приведенных, в частности, на этих страницах:
1) Как эффективно рассчитать расстояние между пара координат с использованием data.table: =
2) Использование простого цикла for для пространственных данных
3) Рассчитать расстояние между двумя точками широты и долготы? (Формула Хаверсина)
4) Самый быстрый способ определить СТРАНУ из миллионов Координаты GPS [R]
Мне удалось создать код, который работает, но (слишком) медленно. Мне было интересно, может ли кто-нибудь помочь мне с более быстрой реализацией data.table () или указать, где находится узкое место в моем коде? Это функция spDistsN1 () или, может быть, комбинация функций apply и melt ()? Мне удобнее всего использовать R, но я открыт для другого программного обеспечения (при условии, что это открытый исходный код).
Из соображений конфиденциальности я не могу загрузить полный набор данных, но это (небольшой) воспроизводимый пример, который не слишком отличается от того, как выглядят реальные данные.
# packages:
library(data.table)
library(tidyverse)
library(sp)
# create GPS data
number_of_GPS_coordinates <- 20000
set.seed(1)
gpsdata<-as.data.frame(cbind(id=1:number_of_GPS_coordinates,
lat=runif(number_of_GPS_coordinates,50.5,53.5),
lon=runif(number_of_GPS_coordinates,4,7)))
# create some busstop data. In this case only 2000 bus stops
set.seed(1)
number_of_bus_stops <- 2000
stop<-as.data.frame(gpsdata[sample(nrow(gpsdata), number_of_bus_stops), -1]) # of course do not keep id variable
stop$lat<-stop$lat+rnorm(number_of_bus_stops,0,.0005)
stop$lon<-stop$lon+rnorm(number_of_bus_stops,0,.0005)
busdata.data<-cbind(stop, name=replicate(number_of_bus_stops, paste(sample(LETTERS, 15, replace=TRUE), collapse="")))
names(busdata.data) <- c("latitude_bustops", "longitude_bustops", "name")
Загрузите настоящие данные автобусной остановки, если хотите, довольно сложно воспроизвести случайную выборку из этого.
#temp <- tempfile()
#download.file("http://data.openov.nl/haltes/stops.csv.gz", temp) #1.7MB
#gzfile(temp, 'rt')
#busstopdata <- read.csv(temp, stringsAsFactors = FALSE)
#unlink(temp)
#bus_stops <- fread("bus_stops.csv")
#busdata.data <- busstopdata %>%
# mutate(latitude_bustops = latitude)%>%
# mutate(longitude_bustops = longitude)%>%
# dplyr::select(name, latitude_bustops, longitude_bustops)
Код, который я использую сейчас для расчета расстояний. Работает, но довольно медленно
countDataPoints3 <- function(p) {
distances <- spDistsN1(data.matrix(gpsdata[,c("lon","lat")]),
p,
longlat=TRUE) # in km
return(which(distances <= .2)) # distance is now set to 200 meters
}
# code to check per data point if a bus stop is near and save this per bus stop in a list entry
datapoints.by.bustation <- apply(data.matrix(busdata.data[,c("longitude_bustops","latitude_bustops")]), 1, countDataPoints3)
# rename list entries
names(datapoints.by.bustation) <- busdata.data$name
# melt list into one big data.frame
long.data.frame.busstops <- melt(datapoints.by.bustation)
# now switch to data.table grammar to speed up process
# set data.table
setDT(gpsdata)
gpsdata[, rowID := 1:nrow(gpsdata)]
setkey(gpsdata, key = "rowID")
setDT(long.data.frame.busstops)
# merge the data, and filter non-unique entries
setkey(long.data.frame.busstops, key = "value")
GPS.joined <- merge(x = gpsdata, y = long.data.frame.busstops, by.x= "rowID", by.y= "value", all.x=TRUE)
GPS.joined.unique <- unique(GPS.joined, by="id") # mak
# this last part of the code is needed to make sure that if there are more than 1 bus stop nearby it puts these bus stop in a list
# instead of adding row and making the final data.frame longer than the original one
GPS.joined.unique2 <- setDT(GPS.joined.unique)[order(id, L1), list(L1=list(L1)), by=id]
GPS.joined.unique2[, nearby := TRUE][is.na(L1), nearby := FALSE] # add a dummy to check if any bus stop is nearby.
# makes sense:
as.tibble(GPS.joined.unique2) %>%
summarize(sum = sum(nearby))