Расчет географических расстояний до точек данных с помощью dplyr :: mutate

Я пытаюсь использовать R с пакетами tidyverse, и у меня возникают проблемы с применением функции к моим данным. Мои данные включают координаты широты и долготы, и я хочу рассчитать расстояние от каждого местоположения (строки моего фрейма данных) до контрольного местоположения. Я пытаюсь использовать функцию geosphere :: distm.

library(tidyverse)
library(geosphere)

my_long <- 172
my_lat <- -43

data <- data %>%  rowwise() %>% mutate(
  dist = distm(c(myLong, myLat), c(long, lat), fun=distHaversine) # this works
)

У меня он работает с помощью функции rowwise(), как указано выше, но она устарела, поэтому я хочу знать, как это сделать с современными tidyverse, то есть dplyr или purrr, я думаю, например, самое близкое, что у меня есть, - это map2:

my_distm <- function(long1, lat1, long2, lat2)
  distm(c(long1, lat1), c(long2, lat2), fun=distHaversine)

data <- data %>%  mutate(
  dist = map2(long, lat, my_distm, my_long, my_lat) # this doesn't
)

Пока что я потерпел неудачу.


person Simon Woodward    schedule 22.08.2017    source источник
comment
Моя проблема в том, что distm не является векторизованной функцией? Если бы это было так, я мог бы использовать его непосредственно в mutate ()?   -  person Simon Woodward    schedule 22.08.2017
comment
Да вот почему. Просто сделайте Vectorize(my_distm), и он должен работать прямо в вашем mutate() вызове.   -  person Steven Beaupré    schedule 23.08.2017
comment
Привет, @SimonWoodward, похоже, вы получили несколько отличных ответов ниже. Пожалуйста, подумайте о том, чтобы принять ответ (галочка слева), чтобы сообщить сообществу, что этот ответ сработал для вас.   -  person CPak    schedule 09.09.2017
comment
На самом деле я застрял с rowwise (). Другие решения были более сложными, чем я хотел. Стоит ли мне еще проверить ответ ниже?   -  person Simon Woodward    schedule 10.09.2017


Ответы (4)


Вы можете использовать mutate с mapply:

library(tidyverse)
library(geosphere)

my_long <- 172
my_lat <- -43

df <- data.frame(long = c(170, 180), lat = c(-43, 43))
df %>% rowwise() %>% mutate(
  dist = distm(c(my_long, my_lat), c(long, lat), fun=distHaversine) # this works
)

#Source: local data frame [2 x 3]
#Groups: <by row>

# A tibble: 2 x 3
#   long   lat    dist
#  <dbl> <dbl>   <dbl>
#1   170   -43  162824
#2   180    43 9606752

df %>% mutate(
    dist = mapply(function(lg, lt) distm(c(my_long, my_lat), c(lg, lt), fun=distHaversine), long, lat)
)

#  long lat    dist
#1  170 -43  162824
#2  180  43 9606752

Обновить при использовании map2:

df %>% 
    mutate(dist = map2(long, lat, ~distm(c(my_long, my_lat), c(.x, .y), fun=distHaversine)))
# here .x stands for a value from long column, and .y stands for a value from lat column
#  long lat    dist
#1  170 -43  162824
#2  180  43 9606752

Чтобы использовать my_distm:

my_distm <- function(long1, lat1, long2, lat2)
    distm(c(long1, lat1), c(long2, lat2), fun=distHaversine)

df %>% mutate(dist = map2(long, lat, ~my_distm(my_long, my_lat, .x, .y)))
#  long lat    dist
#1  170 -43  162824
#2  180  43 9606752
person Psidom    schedule 22.08.2017
comment
Большой! Так почему это работает, а мой - нет? Документация R невероятно лаконична и трудна для понимания. - person Simon Woodward; 22.08.2017
comment
в документации написано map2 (.x, .y, .f, ...), разве это не то, что я сделал? - person Simon Woodward; 22.08.2017
comment
Хм. Я только что тестировал вашу map2 версию, у меня она работает ... У вас могут быть проблемы где-то еще. - person Psidom; 22.08.2017

Вы можете использовать distHaversine вместо distm и cbind:

data %>%  mutate(dist = distHaversine(cbind(myLong, myLat), cbind(long, lat)))

Пример данных:

myLong = 172
myLat = -43 
long = c(180,179,179)
lat = c(-40,-41,-40)
data = data.frame(myLong,myLat,long,lat)

Что дает в результате:

  myLong myLat long lat     dist
1    172   -43  180 -40 745481.0
2    172   -43  179 -41 620164.8
3    172   -43  179 -40 672076.2
person Lamia    schedule 22.08.2017
comment
Если предоставлено два набора из n координат, distm возвращает матрицу размера nxn расстояний между каждой комбинацией позиций, взятых из 2 наборов, тогда как distHaversine возвращает вектор длины n с расстояниями между двумя первыми позициями, 2 вторыми .. - person Lamia; 22.08.2017
comment
зачем вам cbind ()? - person Simon Woodward; 23.08.2017
comment
Вам нужен cbind, потому что distHaversine принимает на вход либо вектор из 2 чисел, либо матрицу с 2 столбцами. Если вы используете rowwise(), то mutate применяется построчно, а затем вы можете использовать c(), но если нет, вам нужно использовать cbind для объединения двух векторов long и lat. Я предположил, что два набора позиций (myLong, myLat, long, lat) были в кадре данных. Если myLong и myLat являются отдельными значениями вместо столбцов фрейма данных, вы можете использовать для них c() вместо cbind(). - person Lamia; 23.08.2017
comment
Внимание: distHaversine() указывает расстояние по Большому кругу, которое, в зависимости от варианта использования, может давать неточные результаты. - person Roman; 18.05.2019

Я тоже очень люблю rowwise, но, поскольку вы ищете другие решения

Данные Псидома

my_long <- 172
my_lat <- -43
myval <- c(my_long, my_lat)

df <- data.frame(long = c(170, 180), lat = c(-43, 43))

мурлыканье решение

Вот purrr::map

library(purrr)
df1 <- df %>%  
         mutate(dist = map(1:nrow(.), ~distm(myval, df[.x,], fun=distHaversine)))

#   long lat    dist
# 1  170 -43  162824
# 2  180  43 9606752

Вы можете использовать map2, повторяя myval несколько раз в форме 2-столбца data.frame, но не как вектор

запрос OP

Чтобы выбрать long и lat из большего фрейма данных для использования с distm, используйте select в операторе map

garbage <- data.frame(long = c(170, 180), lat = c(-43, 43), junk=c(0,0))
df1 <- garbage %>%  
         mutate(dist = map(1:nrow(.), ~distm(myval, select(garbage[.x,],long,lat), fun=distHaversine)))

#   long lat junk    dist
# 1  170 -43    0  162824
# 2  180  43    0 9606752

решение sapply с итераторами

Мне также нравится использовать iterators для построчных операций

library(iterators)
df2 <- df %>%
         mutate(dist = sapply(iter(df, by="row"), function(x) distm(myval, x, fun=distHaversine)))

#   long lat    dist
# 1  170 -43  162824
# 2  180  43 9606752
person CPak    schedule 22.08.2017
comment
Спасибо, я пытался заставить его работать с мурлыканьем. Как выбрать столбцы df по имени? - person Simon Woodward; 22.08.2017
comment
Извините, что вы имеете в виду pick df columns by name? Насколько я понимаю, distm работает только с векторной или двухколоночной матрицей ... - person CPak; 22.08.2017
comment
Да, и мои данные long и lat являются столбцами в более крупном фрейме данных (например, c (data [.x, long], data [.x, lat]), но это не работает). - person Simon Woodward; 22.08.2017

Вы можете использовать pmap()

f  <- function(StartLong, StartLat, EndLong, EndLat) 
  distm(c(StartLong, StartLat), c(EndLong, EndLat))

df %>% mutate(dist = pmap_dbl(., f))

Или Vectorize() ваша функция и используйте ее прямо в mutate():

g <- Vectorize(f)
df %>% mutate(dist = g(StartLong, StartLat, EndLong, EndLat))

Который дает:

#  StartLong StartLat EndLong EndLat    dist
#1       170      -43     172    -43  162824
#2       180       43     172    -43 9606752

Еще одна идея с by_row() от purrrlyr

library(purrrlyr)

df %>%
  by_row(function(x) {
    distm(c(x$StartLong, x$StartLat), 
          c(x$EndLong, x$EndLat)) },
    .collate = "rows", .to = "dist") 

Который дает:

## tibble [2 x 5]
#  StartLong StartLat EndLong EndLat    dist
#      <dbl>    <dbl>   <dbl>  <dbl>   <dbl>
#1       170      -43     172    -43  162824
#2       180       43     172    -43 9606752

Данные

df <- structure(list(StartLong = c(170, 180), StartLat = c(-43, 43), 
      EndLong = c(172, 172), EndLat = c(-43, -43)), .Names = c("StartLong", 
      "StartLat", "EndLong", "EndLat"), row.names = c(NA, -2L), class = "data.frame")
person Steven Beaupré    schedule 22.08.2017
comment
Не знал о _1 _... Это доступно на CRAN? - person CPak; 22.08.2017
comment
@ChiPak Да, это так. Кроме того, как указано на GitHub: purrrlyr содержит некоторые функции, которые находятся на пересечении purrr и dplyr. Они были удалены из purrr, чтобы облегчить упаковку, и потому что они были заменены другими решениями в tidyverse - person Steven Beaupré; 22.08.2017
comment
Теперь я вижу, что вы предоставили свои данные в конце. Я пропустил это раньше - person CPak; 23.08.2017