Расстояния точек между рядами с sf
У меня есть несколько траекторий, сохраненных в простой функции (sf
) типа POINT
, Я хотел бы рассчитать евклидово расстояние между последующими местоположениями (то есть рядами). До сих пор я "вручную" рассчитывал расстояния, используя формулу Пифагора для расчета евклидовых расстояний в двумерном пространстве. Мне было интересно, смогу ли я сделать то же самое, используя функцию sf::st_distance()
, Вот быстрый пример:
library(sf)
library(dplyr)
set.seed(1)
df <- data.frame(
gr = c(rep("a",5),rep("b",5)),
x = rnorm(10),
y = rnorm(10)
)
df <- st_as_sf(df,coords = c("x","y"),remove = F)
df %>%
group_by(gr) %>%
mutate(
dist = sqrt((lead(x)-x)^2+(lead(y)-y)^2)
)
#> Simple feature collection with 10 features and 4 fields
#> geometry type: POINT
#> dimension: XY
#> bbox: xmin: -0.8356286 ymin: -2.2147 xmax: 1.595281 ymax: 1.511781
#> epsg (SRID): NA
#> proj4string: NA
#> # A tibble: 10 x 5
#> # Groups: gr [2]
#> gr x y dist geometry
#> <fct> <dbl> <dbl> <dbl> <POINT>
#> 1 a -0.626 1.51 1.38 (-0.6264538 1.511781)
#> 2 a 0.184 0.390 1.44 (0.1836433 0.3898432)
#> 3 a -0.836 -0.621 2.91 (-0.8356286 -0.6212406)
#> 4 a 1.60 -2.21 3.57 (1.595281 -2.2147)
#> 5 a 0.330 1.12 NA (0.3295078 1.124931)
#> 6 b -0.820 -0.0449 1.31 (-0.8204684 -0.04493361)
#> 7 b 0.487 -0.0162 0.992 (0.4874291 -0.01619026)
#> 8 b 0.738 0.944 0.204 (0.7383247 0.9438362)
#> 9 b 0.576 0.821 0.910 (0.5757814 0.8212212)
#> 10 b -0.305 0.594 NA (-0.3053884 0.5939013)
Я хотел бы рассчитать dist
с sf::st_distance()
, Как бы я пошел по этому поводу?
2 ответа
Первое, что нужно знать о sf
является то, что столбец геометрии (один из класса sfc
) хранится в виде списка-столбца внутри фрейма данных. Ключ, который обычно делает что-либо со списком-столбцом, - это либо использовать purrr::map
и друзья или использовать функцию, которая принимает списочные столбцы в качестве аргументов. В случае st_distance
его аргументы могут быть объектом sf
(датафрейм), sfc
(столбец геометрии), или даже sfg
(один ряд geom), поэтому нет необходимости map
и друзья. Решение должно выглядеть примерно так:
df %>%
group_by(gr) %>%
mutate(
dist = st_distance(geometry)
)
Тем не менее, это не работает. После некоторых исследований мы обнаруживаем две проблемы. Первый, st_distance
возвращает матрицу расстояний, а не одно значение. Чтобы решить эту проблему, мы используем by_element = T
аргумент st_distance
,
Далее мы не можем просто сделать dist = st_distance(geometry, lead(geometry), by_element = T)
так как lead
работает только для векторных столбцов, но не для столбцов списка.
Чтобы решить эту вторую проблему, мы сами создаем ведущую колонку, используя geometry[row_number() + 1]
,
Вот полное решение:
library(sf)
library(dplyr)
df %>%
group_by(gr) %>%
mutate(
lead = geometry[row_number() + 1],
dist = st_distance(geometry, lead, by_element = T),
)
#> Simple feature collection with 10 features and 4 fields
#> Active geometry column: geometry
#> geometry type: POINT
#> dimension: XY
#> bbox: xmin: -0.8356286 ymin: -2.2147 xmax: 1.595281 ymax: 1.511781
#> epsg (SRID): 4326
#> proj4string: +proj=longlat +datum=WGS84 +no_defs
#> # A tibble: 10 x 6
#> # Groups: gr [2]
#> gr x y dist geometry
#> <fct> <dbl> <dbl> <dbl> <sf_geometry [degree]>
#> 1 a -0.626 1.51 1.38 POINT (-0.6264538 1.511781)
#> 2 a 0.184 0.390 1.44 POINT (0.1836433 0.3898432)
#> 3 a -0.836 -0.621 2.91 POINT (-0.8356286 -0.6212406)
#> 4 a 1.60 -2.21 3.57 POINT (1.595281 -2.2147)
#> 5 a 0.330 1.12 0 POINT (0.3295078 1.124931)
#> 6 b -0.820 -0.0449 1.31 POINT (-0.8204684 -0.04493361)
#> 7 b 0.487 -0.0162 0.992 POINT (0.4874291 -0.01619026)
#> 8 b 0.738 0.944 0.204 POINT (0.7383247 0.9438362)
#> 9 b 0.576 0.821 0.910 POINT (0.5757814 0.8212212)
#> 10 b -0.305 0.594 0 POINT (-0.3053884 0.5939013)
#> # ... with 1 more variable: lead <sf_geometry [degree]>
Вот базаR
решение. Хитрость заключается в том, чтобы
-
split()
кадр данных по столбцу группы - По каждой группе
- рассчитать расстояние между последующими местоположениями, используя
head()
иtail()
- добавить
NA
к результату (поскольку последняя позиция не имеет последующего местоположения)
- рассчитать расстояние между последующими местоположениями, используя
- Свяжите получившийся
sf
объекты собрать вместе, используяrbind
вdo.call()
.
split(df, df$gr) |>
lapply(\(x){
x$dist <- c(st_distance(head(x,-1), tail(x,-1),by_element = TRUE), NA)
x
}) |>
do.call(rbind, args = _)