Поиск ближайшей точки путем сравнения двух фреймов данных в R

#r #distance #latitude-longitude

#r #расстояние #широта-долгота

Вопрос:

У меня есть набор из двух фреймов данных в R

     First:    
site_no <- c("02110500","02110550", "02110701" , "02110704", "02110760", "02110777", "021108044", "02110815")
lat_coor <- c(33.91267, 33.85083, 33.86100, 33.83295, 33.74073, 33.85156, 33.65017, 33.44461)
long_coor <- c(-78.71502, -78.89722, -79.04115, -79.04365, -78.86669, -78.65585, -79.12310, -79.17393)
AllStations <- data.frame(site_no, lat_coor, long_coor)


    Second:
station <- c("USGS-02146110","USGS-02146110","USGS-02146110","USGS-02146110","USGS-02146110","USGS-021473426","USGS-021473426","USGS-021473426")
latitude <- c(34.88928, 34.85651, 34.85651, 34.85651, 34.71679, 34.24320, 34.80012, 34.80012)
longitude <- c(-81.06869, -82.22622, -82.22622, -82.22622, -82.17372, -81.31954, -82.36512, -82.36512)
ContaminantStations <- data.frame(station, latitude, longitude)
  

Мои наборы данных намного длиннее, но для целей этого вопроса, я думаю, этого должно быть достаточно.

Я хотел бы найти все станции из первого фрейма данных (AllStations), которые находятся внутри радиуса точек во втором фрейме данных (ContaminantStations), и добавить их в новый фрейм данных (только те, что со всех станций), мне нужно извлечь станцию со всей ее информацией. Я пробовал некоторые логические, но ни один из них не работает и не имеет смысла. Я также пытаюсь использовать RANN: nn2, но это дает мне только подсчет.

Любая помощь будет оценена

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

1. «Я пробовал некоторые» … что вы пробовали? Часто лучше помочь с кодом, с которым вы знакомы, чем начинать с нуля.

2. Что происходит, когда она находится в радиусе действия нескольких станций загрязнения?

3. Затем я должен выбрать только ближайший

Ответ №1:

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

 func <- function(stations, constations, radius = 250000) {
  if (!NROW(stations) || !NROW(constations)) return()
  if (length(radius) == 1 amp;amp; NROW(constations) > 1) {
    radius <- rep(radius, NROW(constations))
  } else if (length(radius) != NROW(constations)) {
    stop("'radius' must be length 1 or the same as the number of rows in 'constations'")
  }
  out <- integer(NROW(stations))
  for (i in seq_len(NROW(stations))) {
    dists <- geosphere::distHaversine(stations[i,], constations)
    out[i] <- if (any(dists <= radius)) which.min(dists) else 0L
  }
  return(out)
}
  

Это возвращает целочисленный вектор, указывающий ближайшую станцию загрязнения. Если ни один из них не находится в радиусе, он возвращается 0 . Это безопасно используется в качестве индекса строки в исходном фрейме.

Каждый аргумент должен содержать только два столбца, причем первым столбцом является долгота. (Я не делаю никаких предположений относительно имен столбцов в функции.) radius выражается в метрах, что согласуется с geosphere предположениями пакета.

 ind <- func(AllStations[,c("long_coor", "lat_coor")], ContaminantStations[,c("longitude", "latitude")],
            radius = 230000)
ind
# [1] 0 6 6 6 0 0 6 6
  

Это индексы в ContaminantStations строках, где ненулевое значение означает, что эта станция загрязнения находится ближе всего к конкретной строке AllStations . .

Мы можем определить, какая станция загрязнения находится ближе всего с помощью этого (есть много способов сделать это, включая tidyverse и другие методы… это только начало).

 AllStations$ClosestContaminantStation <- NA_character_
AllStations$ClosestContaminantStation[ind > 0] <- ContaminantStations$station[ind]
AllStations
#     site_no lat_coor long_coor ClosestContaminantStation
# 1  02110500 33.91267 -78.71502                      <NA>
# 2  02110550 33.85083 -78.89722            USGS-021473426
# 3  02110701 33.86100 -79.04115            USGS-021473426
# 4  02110704 33.83295 -79.04365            USGS-021473426
# 5  02110760 33.74073 -78.86669                      <NA>
# 6  02110777 33.85156 -78.65585                      <NA>
# 7 021108044 33.65017 -79.12310            USGS-021473426
# 8  02110815 33.44461 -79.17393            USGS-021473426
  

Обзор ваших данных для перспективы:

ggplot местоположений станций


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

 func2 <- function(stations, constations, radius = 250000) {
  if (!NROW(stations) || !NROW(constations)) return()
  if (length(radius) == 1 amp;amp; NROW(constations) > 1) {
    radius <- rep(radius, NROW(constations))
  } else if (length(radius) != NROW(constations)) {
    stop("'radius' must be length 1 or the same as the number of rows in 'constations'")
  }
  out <- data.frame(ind = integer(NROW(stations)), dist = numeric(NROW(stations)))
  for (i in seq_len(NROW(stations))) {
    dists <- geosphere::distHaversine(stations[i,], constations)
    out$ind[i] <- which.min(dists)
    out$dist[i] <- min(dists)
  }
  return(out)
}
  

Демонстрация, включая включение станции загрязнения в тот же фрейм.

 AllStations2 <- cbind(
  AllStations,
  func2(AllStations[,c("long_coor", "lat_coor")], ContaminantStations[,c("longitude", "latitude")])
)
AllStations2
#     site_no lat_coor long_coor ind     dist
# 1  02110500 33.91267 -78.71502   1 241971.5
# 2  02110550 33.85083 -78.89722   6 227650.6
# 3  02110701 33.86100 -79.04115   6 214397.8
# 4  02110704 33.83295 -79.04365   6 214847.7
# 5  02110760 33.74073 -78.86669   6 233190.8
# 6  02110777 33.85156 -78.65585   6 249519.7
# 7 021108044 33.65017 -79.12310   6 213299.3
# 8  02110815 33.44461 -79.17393   6 217378.9
AllStations3 <- cbind(
  AllStations2,
  ContaminantStations[AllStations2$ind,]
)
AllStations3
#       site_no lat_coor long_coor ind     dist        station latitude longitude
# 1    02110500 33.91267 -78.71502   1 241971.5  USGS-02146110 34.88928 -81.06869
# 6    02110550 33.85083 -78.89722   6 227650.6 USGS-021473426 34.24320 -81.31954
# 6.1  02110701 33.86100 -79.04115   6 214397.8 USGS-021473426 34.24320 -81.31954
# 6.2  02110704 33.83295 -79.04365   6 214847.7 USGS-021473426 34.24320 -81.31954
# 6.3  02110760 33.74073 -78.86669   6 233190.8 USGS-021473426 34.24320 -81.31954
# 6.4  02110777 33.85156 -78.65585   6 249519.7 USGS-021473426 34.24320 -81.31954
# 6.5 021108044 33.65017 -79.12310   6 213299.3 USGS-021473426 34.24320 -81.31954
# 6.6  02110815 33.44461 -79.17393   6 217378.9 USGS-021473426 34.24320 -81.31954
  

Здесь вы можете выбрать радиус по своему усмотрению:

 subset(AllStations3, dist < 230000)
#       site_no lat_coor long_coor ind     dist        station latitude longitude
# 6    02110550 33.85083 -78.89722   6 227650.6 USGS-021473426  34.2432 -81.31954
# 6.1  02110701 33.86100 -79.04115   6 214397.8 USGS-021473426  34.2432 -81.31954
# 6.2  02110704 33.83295 -79.04365   6 214847.7 USGS-021473426  34.2432 -81.31954
# 6.5 021108044 33.65017 -79.12310   6 213299.3 USGS-021473426  34.2432 -81.31954
# 6.6  02110815 33.44461 -79.17393   6 217378.9 USGS-021473426  34.2432 -81.31954