Допустим, у меня есть регулярная сетка широты и долготы и данные в нестандартных местах, например:
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
?
Вы можете объединить два фрейма данных и найти наименьшее расстояние для каждой группы координат:
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()
Ответ обновлен. Пожалуйста, посмотрите :)
Если сетка однородная, вы можете просто округлить ее до степени детализации.
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, я бы посоветовал оставить lon
и lat
в data_sf
(с remove=FALSE
), чтобы эти числовые столбцы появлялись в joined
: data_sf = st_as_sf(data , coords =c("lon","lat"), remove = FALSE)
@sieste, имейте в виду, что это решение будет работать с «проецируемыми» координатами, например. в метрах. Если ваши координаты указаны в градусах, расстояния на экваторе и вблизи полюсов не будут одинаковыми. sf
может управлять несколькими системами координат (CRS). (Я не знаю, как мое решение будет работать с неспроецированными данными (в градусах), я обычно работаю с спроецированными координатами). Геопространственные данные — это хорошо, но иногда сложно! Хорошая ссылка: r-spatial.org/book
Вот базовый подход 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))
Спасибо, это определенно хорошее решение для небольших наборов данных. Но для более крупных промежуточный фрейм данных размером nrow(grid) * nrow(data) может сделать это непрактичным. Есть ли решение, которое позволяет избежать этого?