Вычисление минимального расстояния между строкой и всеми предыдущими строками в R

#r

#r

Вопрос:

Я хочу вычислить минимальное расстояние между текущей строкой и каждой строкой перед ней в каждой группе. Мой фрейм данных состоит из нескольких групп, и каждая группа имеет несколько дат с долготой и широтой. Я использую функцию Haversine для вычисления расстояния, и мне нужно применить эту функцию, как описано выше. Фрейм данных выглядит следующим образом:

   grp    date    long lat rowid
1   1 1995-07-01   11  12     1
2   1 1995-07-05    3   0     2
3   1 1995-07-09   13   4     3
4   1 1995-07-13    4  25     4
5   2 1995-03-07   12   6     1
6   2 1995-03-10    3  27     2
7   2 1995-03-13   34   8     3
8   2 1995-03-16   25   9     4
  

В моей текущей попытке используется purrrlyr::by_row, но метод слишком медленный. На практике каждая группа содержит тысячи дат и географических положений. Вот часть моей текущей попытки:

 calc_min_distance <- function(df, grp.name, row){
  df %>% 
    filter(
      group_name==grp.name
    ) %>% 
    filter(
      row_number() <= row
    ) %>% 
    mutate(
      last.lat = last(lat),
      last.long = last(long),
      rowid = 1:n()
    ) %>% 
    group_by(rowid) %>% 
    purrrlyr::by_row(
      ~haversinedistance.fnct(.$last.long, .$last.lat, .$long, .$lat),
      .collate='rows',
      .to = 'min.distance'
    ) %>% 
    filter(
      row_number() < n()
    ) %>% 
    summarise(
      min = min(min.distance)
    ) %>% 
    .$min
}

df_dist <-
  df %>% 
  group_by(grp_name) %>% 
  mutate(rowid = 1:n()) %>% 
  group_by(grp_name, rowid) %>% 
  purrrlyr::by_row(
    ~calc_min_distance(df, .$grp_name,.$rowid),
    .collate='rows',
    .to = 'min.distance'
  ) %>% 
  ungroup %>% 
  select(-rowid)
  

Предположим, что расстояние определено как (широта длина) для эталонной строки — (широта длина) для каждой попарной строки, меньшей, чем эталонная строка. Мой ожидаемый результат для grp 1 следующий:

   grp       date long lat rowid min.distance
1   1 1995-07-01   11  12     1            0
2   1 1995-07-05    3   0     2          -20
3   1 1995-07-09   13   4     3           -6
4   1 1995-07-13    4  25     4            6
  

Как я могу быстро вычислить минимальное расстояние между текущим идентификатором строки и всеми идентификаторами строки перед ним?

Комментарии:

1. Пожалуйста, поделитесь тем, что вы уже пробовали, а также укажите ожидаемый результат. Я не совсем понимаю, что вы пытаетесь сделать и как должен выглядеть ваш ожидаемый результат.

2. Не видя вашего кода, нет причин думать, что любой другой метод был бы быстрее. Ваша проблема требует вычисления всех попарных расстояний внутри каждой группы, это около 500 тыс. вычислений расстояния для группы из 1000 точек, 12,5 М вычислений для 5000 точек. Я надеюсь, что вы вычисляете матрицы расстояний для каждой группы, а затем выбираете соответствующие минимумы…

Ответ №1:

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

 library(geosphere)
find_min_dist_above = function(long, lat, fun = distHaversine) {
  d = distm(x = cbind(long, lat), fun = fun)
  d[lower.tri(d, diag = TRUE)] = NA
  d[1, 1] = 0
  return(apply(d, MAR = 2, min, na.rm = TRUE))
}

df %>% group_by(grp) %>%
  mutate(min.distance = find_min_dist_above(long, lat))
# # A tibble: 8 x 6
# # Groups:   grp [2]
#     grp date        long   lat rowid min.distance
#   <int> <fct>      <int> <int> <int>        <dbl>
# 1     1 1995-07-01    11    12     1           0 
# 2     1 1995-07-05     3     0     2     1601842.
# 3     1 1995-07-09    13     4     3      917395.
# 4     1 1995-07-13     4    25     4     1623922.
# 5     2 1995-03-07    12     6     1           0 
# 6     2 1995-03-10     3    27     2     2524759.
# 7     2 1995-03-13    34     8     3     2440596.
# 8     2 1995-03-16    25     9     4      997069.
  

Используя эти данные:

 df = read.table(text = '  grp    date    long lat rowid
1   1 1995-07-01   11  12     1
2   1 1995-07-05    3   0     2
3   1 1995-07-09   13   4     3
4   1 1995-07-13    4  25     4
5   2 1995-03-07   12   6     1
6   2 1995-03-10    3  27     2
7   2 1995-03-13   34   8     3
8   2 1995-03-16   25   9     4', h = TRUE)