Нахождение ближайших точек между несколькими наборами данных с помощью Ellipsoidal / Vincenty - PullRequest
2 голосов
/ 19 апреля 2019

Примечание. Этот вопрос является продолжением предыдущего вопроса: r - Поиск ближайших координат между двумя большими наборами данных .

Я стремлюсь определить ближайшую запись в наборе данных 2 для каждой записи в наборе данных 1 на основе координат в обоих наборах данных. Набор данных 1 содержит 180 000 строк (всего 1800 уникальных координат), а набор данных 2 содержит 4500 строк (полных 4500 уникальных координат).

Ранее упомянутый пост содержит решение проблемы, однако он использует RANN::nn2, который использует евклидово расстояние, в отличие от цели использования Ellipsoidal / Vincenty.

Текущий код:

df1[ , c(4,5)] <- as.data.frame(RANN::nn2(df2[,c(2,3)],df1[,c(2,3)],k=1))
df1[,4] <- df2[df1[, 4], 1]

    #    id HIGH_PRCN_LAT HIGH_PRCN_LON SRC_ID   distance
    # 1   1      52.88144     -2.873778     44  0.7990743
    # 2   2      57.80945     -2.234544   5688  2.1676868
    # 3   4      34.02335     -3.098445  61114  1.4758202
    # 4   5      63.80879     -2.439163     23  4.2415854
    # 5   6      53.68881     -7.396112     54  3.6445416
    # 6   7      63.44628     -5.162345     23  2.3577811
    # 7   8      21.60755     -8.633113    440  8.2123762
    # 8   9      78.32444      3.813290     76 11.4936496
    # 9  10      66.85533     -3.994326     55  1.9296370
    # 10  3      51.62354     -8.906553     54  3.2180026

Я подозреваю, что решение будет включать geosphere::distVincentyEllipsoid, но я не уверен, как его интегрировать в существующий код.

Данные:

r подробности

platform        x86_64-w64-mingw32
version.string  R version 3.5.3 (2019-03-11)

набор данных 1 вход (не сужен до уникальных координат)

df1 <- structure(list(id = c(1L, 2L, 4L, 5L, 
6L, 7L, 8L, 9, 10L, 3L), 
    HIGH_PRCN_LAT = c(52.881442267773, 57.8094538200198, 34.0233529, 
    63.8087900198, 53.6888144440184, 63.4462810678651, 21.6075544376207, 
    78.324442654172, 66.85532539759495, 51.623544596), HIGH_PRCN_LON = c(-2.87377812157822, 
    -2.23454414781635, -3.0984448341, -2.439163178635, -7.396111601421454, 
    -5.162345043546359, -8.63311254098095, 3.813289888829932, 
    -3.994325961186105, -8.9065532453272409), SRC_ID = c(NA, NA, 
    NA, NA, NA, NA, NA, NA, NA, NA), distance = c(NA, NA, 
    NA, NA, NA, NA, NA, NA, NA, NA)), row.names = c(NA, 10L), class = "data.frame")

набор данных 2 ввода

df2 <- structure(list(SRC_ID = c(55L, 54L, 23L, 11L, 44L, 21L, 76L, 
5688L, 440L, 61114L), HIGH_PRCN_LAT = c(68.46506, 50.34127, 61.16432, 
42.57807, 52.29879, 68.52132, 87.83912, 55.67825, 29.74444, 34.33228
), HIGH_PRCN_LON = c(-5.0584, -5.95506, -5.75546, -5.47801, -3.42062, 
-6.99441, -2.63457, -2.63057, -7.52216, -1.65532)), row.names = c(NA, 
10L), class = "data.frame")

1 Ответ

1 голос
/ 23 апреля 2019

Использование функции distVincentyEllipsoid:

library(geosphere)

t(
  apply(
        apply(df1[,c(3,2)], 1, function(mrow){distVincentyEllipsoid(mrow, df2[,c(3,2)])}), 
         2, function(x){ c(SRC_ID=df2[which.min(x),1],distance=min(x))}
       )
  )
       SRC_ID   distance
    1      44   74680.48
    2    5688  238553.51
    3   61114  137385.18
    4      23  340642.70
    5      44  308458.73
    6      23  256176.88
    7     440  908292.28
    8      76 1064419.47
    9      55  185119.29
    10     54  251580.45

Просто используйте df1[,c(4,5)] <- t(apply(..., чтобы присвоить значения столбцу df1



Использование rgeos::gDistance.Это декартово расстояние, но, исходя из приведенного ниже решения, мне удалось опубликовать обновленный ответ выше;

library(sp);library(rgeos)

#convert to spatial datasets    
df1rgsp <- SpatialPointsDataFrame(df1[,c(3,2)], df1[,-c(3,2)])
df2rgsp <- SpatialPointsDataFrame(df2[,c(3,2)], data.frame(SRC_ID=df2[,1]))

#apply it on each rows
#find the minimum value and the corresponding row number
#transform it to become to columns and assign it to the columns of `df1`
df1[,c(4,5)] <- t( apply(gDistance(df1rgsp, df2rgsp, byid=TRUE), 1, function(x){
                            c(SRC_ID=which.min(x),distance=min(x))}))

#replace row numbers with `SRC_ID
df1[,4] <- df2[as.integer(df1[, 4]), 1] #same as what you have in the Q

    #    id HIGH_PRCN_LAT HIGH_PRCN_LON SRC_ID   distance
    # 1   1      52.88144     -2.873778    440  1.9296370
    # 2   2      57.80945     -2.234544  61114  3.2180026
    # 3   4      34.02335     -3.098445     21  2.3577811
    # 4   5      63.80879     -2.439163     23  8.8794997
    # 5   6      53.68881     -7.396112     55  0.7990743
    # 6   7      63.44628     -5.162345    440  3.4316239
    # 7   8      21.60755     -8.633113   5688 11.4936496
    # 8   9      78.32444      3.813290     54  2.1676868
    # 9  10      66.85533     -3.994326     23  6.1545391
    # 10  3      51.62354     -8.906553     23  1.4758202

...