Объединение кадров данных широты и долготы по ближайшему расстоянию

Допустим, у меня есть регулярная сетка широты и долготы и данные в нестандартных местах, например:

grid = tidyr::crossing(lon = seq(0, 1, 0.25), lat = seq(0, 1, 0.25))
data = tibble::tibble(lon = runif (4), lat=runif (4), y=rnorm(4))

Как мне использовать, например, dplyr::inner_join и join_by для объединения этих фреймов данных, чтобы я получал значения y из data и соответствующие значения lat и lon из grid из ближайшего местоположения, то есть точки сетки с наименьшим (grid$lon - data$lon)^2 + (grid$lat - data$lat)^2 для каждой строки в data?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
74
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете объединить два фрейма данных и найти наименьшее расстояние для каждой группы координат:

grid = tidyr::crossing(lon_grid = seq(0, 1, 0.25), lat_grid = seq(0, 1, 0.25))
data = tibble::tibble(lon = runif (4), lat=runif (4), y=rnorm(4))

library(dplyr)
tidyr::expand_grid(data, grid) %>%
  group_by(lon, lat) %>%
  filter(row_number() == which.min((lon_grid - lon) ^ 2 + (lat_grid - lat) ^ 2)) %>%
  ungroup()


# A tibble: 4 × 5
    lon    lat       y lon_grid lat_grid
  <dbl>  <dbl>   <dbl>    <dbl>    <dbl>
1 0.612 0.208   0.0407     0.5      0.25
2 0.381 0.520   2.01       0.5      0.5 
3 0.198 0.399  -0.455      0.25     0.5 
4 0.150 0.0847 -0.717      0.25     0  

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

data %>%
  rowwise() %>%
  mutate(grid = list(grid[(abs(grid$lon_grid - lon) < 0.25 & abs(grid$lat_grid - lat) < 0.25), ])) %>%
  tidyr::unnest(grid) %>%
  group_by(lon, lat) %>%
  filter(row_number() == which.min((lon_grid - lon) ^ 2 + (lat_grid - lat) ^ 2)) %>%
  ungroup()

Спасибо, это определенно хорошее решение для небольших наборов данных. Но для более крупных промежуточный фрейм данных размером nrow(grid) * nrow(data) может сделать это непрактичным. Есть ли решение, которое позволяет избежать этого?

sieste 12.07.2024 11:25

Ответ обновлен. Пожалуйста, посмотрите :)

Clemsang 12.07.2024 11:37

Если сетка однородная, вы можете просто округлить ее до степени детализации.

set.seed(42)

grid <- tidyr::crossing(lon = seq(0, 1, 0.25), lat = seq(0, 1, 0.25))
data <- tibble::tibble(lon = runif (4), lat = runif (4), y = rnorm(4))

round_to <- function(x, precision) {
  precision * round(x / precision)
}

data |>
  dplyr::mutate(
    lon_grid = round_to(lon, 0.25),
    lat_grid = round_to(lat, 0.25),
  )
#> # A tibble: 4 × 5
#>     lon   lat       y lon_grid lat_grid
#>   <dbl> <dbl>   <dbl>    <dbl>    <dbl>
#> 1 0.915 0.642  0.404      1        0.75
#> 2 0.937 0.519 -0.106      1        0.5 
#> 3 0.286 0.737  1.51       0.25     0.75
#> 4 0.830 0.135 -0.0947     0.75     0.25

Для соединения произвольных точек вам потребуется вычислить все попарные расстояния. Это можно эффективно сделать с помощью Rfast::dista().

data |>
  dplyr::mutate(
    grid_index = dplyr::pick(lon, lat) |>
      Rfast::dista(grid[c("lon", "lat")]) |>
      Rfast::rowMins(),
    grid[grid_index, ] |> dplyr::rename_all(paste0, "_grid")
  )
#> # A tibble: 4 × 6
#>     lon   lat       y grid_index lon_grid lat_grid
#>   <dbl> <dbl>   <dbl>      <dbl>    <dbl>    <dbl>
#> 1 0.915 0.642  0.404          24     1        0.75
#> 2 0.937 0.519 -0.106          23     1        0.5 
#> 3 0.286 0.737  1.51            9     0.25     0.75
#> 4 0.830 0.135 -0.0947         17     0.75     0.25
Ответ принят как подходящий

Пакет sf предназначен для манипулирования пространственной геометрией; бывший. точки, линии, многоугольники. Вам необходимо преобразовать фреймы данных в объекты sf, затем вы можете указать пространственное соединение st_join() с join = st_nearest_feature в качестве аргумента.

library(sf)
library(tidyverse)

set.seed(42)

grid <- tidyr::crossing(lon = seq(0, 1, 0.25), lat = seq(0, 1, 0.25))
data <- tibble::tibble(lon = runif (4), lat = runif (4), y = rnorm(4))

grid_sf = st_as_sf(grid , coords =c("lon","lat"))
data_sf = st_as_sf(data , coords =c("lon","lat"))

joined = st_join(grid_sf, data_sf, join = st_nearest_feature)

ggplot() + geom_sf(data= joined, aes(col = y))+ 
  geom_sf(data= data_sf, aes(col = y, fill = y),size= 4, shape = 22)

Created on 2024-07-12 with reprex v2.1.0

Хороший! Можно ли добавить в код строку, в которой вы преобразуете joined, чтобы в нем были столбцы lon и lat?

sieste 15.07.2024 11:14

@sieste, я бы посоветовал оставить lon и lat в data_sfremove=FALSE), чтобы эти числовые столбцы появлялись в joined: data_sf = st_as_sf(data , coords =c("lon","lat"), remove = FALSE)

VinceGreg 15.07.2024 16:20

@sieste, имейте в виду, что это решение будет работать с «проецируемыми» координатами, например. в метрах. Если ваши координаты указаны в градусах, расстояния на экваторе и вблизи полюсов не будут одинаковыми. sf может управлять несколькими системами координат (CRS). (Я не знаю, как мое решение будет работать с неспроецированными данными (в градусах), я обычно работаю с спроецированными координатами). Геопространственные данные — это хорошо, но иногда сложно! Хорошая ссылка: r-spatial.org/book

VinceGreg 15.07.2024 16:28

Вот базовый подход R с использованием max.col + outer

cp_grid <- with(grid, lon + 1i * lat)
cp_data <- with(data, lon + 1i * lat)
cbind(
    data,
    setNames(
        grid,
        paste0("grid_", names(grid))
    )[max.col(-abs(outer(cp_data, cp_grid, `-`))), ]
)

что дает результат, похожий на

        lon       lat          y grid_lon grid_lat
1 0.8966972 0.9082078  0.4146414     1.00     1.00
2 0.2655087 0.2016819 -1.5399500     0.25     0.25
3 0.3721239 0.8983897 -0.9285670     0.25     1.00
4 0.5728534 0.9446753 -0.2947204     0.50     1.00

Данные

set.seed(0)
grid <- tidyr::crossing(lon = seq(0, 1, 0.25), lat = seq(0, 1, 0.25))
data <- tibble::tibble(lon = runif (4), lat = runif (4), y = rnorm(4))

Другие вопросы по теме