2016-12-01 13 views
2

Я уже просмотрел несколько ответов, но не смог применить его к моим проблемам. См:Как рассчитать расстояние между местоположениями из отдельных df в R

Calculating the distance between points in different data frames

Calculating number of points within a certain radius

find locations within certain lat/lon distance in r

find number of points within a radius in R using lon and lat coordinates

Identify points within specified distance in R

У меня есть ДФ loc и stop. За каждый stop Я хочу найти расстояние до loc.

Мои места

loc <- data.frame(station = c('Baker Street','Bank'), 
        lat = c(51.522236,51.5134047), 
        lng = c(-0.157080, -0.08905843), 
        postcode = c('NW1','EC3V') 
       ) 

Мои остановки

stop <- data.frame(station = c('Angel','Barbican','Barons Court','Bayswater'), 
        lat = c(51.53253,51.520865,51.490281,51.51224), 
        lng = c(-0.10579,-0.097758,-0.214340,-0.187569), 
        postcode = c('EC1V','EC1A', 'W14', 'W2')) 

В конечном итоге я хотел бы что-то вроде этого:

df <- data.frame(loc = c('Baker Street','Bank','Baker Street','Bank','Baker Street','Bank','Baker Street','Bank'), 
       stop = c('Angel','Barbican','Barons Court','Bayswater','Angel','Barbican','Barons Court','Bayswater'), 
       dist = c('x','x','x','x','x','x','x','x'), 
       lat = c(51.53253,51.520865,51.490281,51.51224,51.53253,51.520865,51.490281,51.51224), 
       lng = c(-0.10579,-0.097758,-0.214340,-0.187569,-0.10579,-0.097758,-0.214340,-0.187569), 
       postcode = c('EC1V','EC1A', 'W14', 'W2','EC1V','EC1A', 'W14', 'W2') 
       ) 

Мой набор данных является относительно большой, так что я ищу для эффективного метода решения этой проблемы.

Любые идеи о том, как достичь этого?

+0

Возможно, я не читаю вопрос правильно, но пытаетесь ли вы найти расстояние между каждой точкой в ​​стоп-кадрах данных из каждой точки в ядре данных? – Awhstin

+0

@Awhstin Да точно ... каждое расстояние от 'stop' до' loc' – Davis

+1

По совпадению, я [ответил на вопрос вчера] (http://stackoverflow.com/a/40898595/496488), который имеет базовый подход R, который будет работать здесь, если вы замените 'loc' на' круги' и 'stop' для' dat', а также убедитесь, что вы переносите любые столбцы, которые вы хотите сохранить из каждого фрейма данных. (Вопросы не дубликаты, но ответы схожи.) – eipi10

ответ

4

Использует expand.grid и merge переменную переименования. Это немного по-человечески, но это довольно эффективно, поскольку операции векторизованы.

library(dplyr) 
df <- expand.grid(station = loc$station, stop = stop$station) %>% 
    merge(loc, by = 'station') %>% 
    rename(loc = station, lat1 = lat, lng1 = lng, station = stop) %>% 
    select(-postcode) %>% 
    merge(stop, by = 'station') %>% 
    rename(stop = station, lat2 = lat, lng2 = lng) 
#   stop   loc  lat1  lng1  lat2  lng2 postcode 
# 1  Angel Baker Street 51.52224 -0.15708000 51.53253 -0.105790  EC1V 
# 2  Angel   Bank 51.51340 -0.08905843 51.53253 -0.105790  EC1V 
# 3  Barbican Baker Street 51.52224 -0.15708000 51.52087 -0.097758  EC1A 
# 4  Barbican   Bank 51.51340 -0.08905843 51.52087 -0.097758  EC1A 
# 5 Barons Court Baker Street 51.52224 -0.15708000 51.49028 -0.214340  W14 
# 6 Barons Court   Bank 51.51340 -0.08905843 51.49028 -0.214340  W14 
# 7 Bayswater Baker Street 51.52224 -0.15708000 51.51224 -0.187569  W2 
# 8 Bayswater   Bank 51.51340 -0.08905843 51.51224 -0.187569  W2 

Затем мы можем использовать geosphere::distHaversine() (вдохновленный Иакову) для вычисления расстояния с помощью Haversine formula.

df$dist_meters <- geosphere::distHaversine(select(df, lng1, lat1), 
              select(df, lng2, lat2)) 
df %>% 
    select(stop, loc, dist_meters) 
#   stop   loc dist_meters 
# 1  Angel Baker Street 3732.422 
# 2  Angel   Bank 2423.989 
# 3  Barbican Baker Street 4111.786 
# 4  Barbican   Bank 1026.091 
# 5 Barons Court Baker Street 5328.649 
# 6 Barons Court   Bank 9054.998 
# 7 Bayswater Baker Street 2387.231 
# 8 Bayswater   Bank 6825.897 

И в случае, если ваш интересно, как формула Haversine работает,

latrad1 <- df$lat1 * pi/180 
latrad2 <- df$lat2 * pi/180 
dlat <- df$dlat * pi/180 
dlng <- df$dlng * pi/180 
a <- sin(dlat/2)^2 + sin(dlng/2)^2 * cos(latrad1) * cos(latrad2) 
dist_rad <- 2 * atan2(sqrt(a), sqrt(1-a)) 
df %>% 
    mutate(dist_meters_byhand = dist_rad * 6378137) %>% 
    select(stop, loc, dist_meters_geosphere = dist_meters, dist_meters_byhand) 
#   stop   loc dist_meters_geosphere dist_meters_byhand 
# 1  Angel Baker Street    3732.422   3732.422 
# 2  Angel   Bank    2423.989   2423.989 
# 3  Barbican Baker Street    4111.786   4111.786 
# 4  Barbican   Bank    1026.091   1026.091 
# 5 Barons Court Baker Street    5328.649   5328.649 
# 6 Barons Court   Bank    9054.998   9054.998 
# 7 Bayswater Baker Street    2387.231   2387.231 
# 8 Bayswater   Bank    6825.897   6825.897 
+0

спасибо за ваш ответ, очень полезно. Что считается близким? Будет ли это работать с точками данных в пределах одной страны (например, в Великобритании) или мне нужны сферические координаты для больших расстояний? Кроме того, какая единица измеряется расстоянием в вашем ответе? – Davis

+1

Я изменил результаты на метры, используя пакет геосферы, как предположил Джейкоб. –

0

Не как умный (или, возможно, так же быстро), как @ Бен, но вот еще один способ:

library(geosphere) 

master_df <- data.frame() 

for (i in 1:nrow(loc)){ 
    this_loc <- loc[i, 1] 
    temp_df <- cbind(stop, 
        data.frame(loc = this_loc, 
        dist = distm(as.matrix(stop[, 2:3]), c(loc[i, 2], loc[i, 3])))) 
    master_df <- rbind(master_df, temp_df) 
} 

Пакет geosphere по умолчанию использует haverine, который может быть полезен, если требуется точность.

+0

благодарит за вашу помощь. Я заметил, что, если я попробую ваш подход, я не получаю уникальные расстояния, то есть dist. «Ангел» на «Бейкер-стрит» - это то же самое, что и dist. 'Ангел' на' Банк'? – Davis

+0

ooops! незначительный snafu теперь исправлен ... – Jacob