Во-первых, функция data.table
rleid
используется для создания группирующей переменной: для каждого отдельного пользователя каждое изменение сайта представляет новую группу. Внутри каждой группы рассчитайте желаемую статистику:
library(data.table)
library(geosphere)
setDT(df)
df2 <- df[ , .(id = id[1],
site = site[1],
lat = lat[1],
lon = lon[1],
first_time = min(time),
last_time = max(time)),
by = .(id_site = rleid(id, site))]
Затем для каждого отдельного человека вычисляется последовательное расстояние большого круга между последовательными участками с помощью geosphere::distHaversine
. Чтобы избежать проблем, когда у людей есть только одна или две записи *, добавлены некоторые проверки:
df2[ , dist := if(.N == 1){
0 } else if(.N == 2){
c(0, distHaversine(c(lon[1], lat[1]), c(lon[2], lat[2])))
} else c(0, distHaversine(as.matrix(.SD[ , .(lon, lat)]))), by = id]
# id_site id site lat lon first_time last_time dist
# 1: 1 A a 1 1 1 2 0.0
# 2: 2 A b 2 2 3 3 157401.6
# 3: 3 A a 1 1 4 4 157401.6
# 4: 4 A c 3 3 5 7 314755.2
# 5: 5 A d 4 4 8 8 157281.8
# 6: 6 B a 1 1 9 9 0.0
# 7: 7 B b 2 2 10 10 157401.6
# 8: 8 C a 1 1 11 11 0.0
Таким образом, для каждого человека расстояние рассчитывается только один раз для нового сайта . Это контрастирует с другим ответом, где вычисления расстояния выполняются между каждый временной шаг (кажется, многие из них).
* Попробуйте, например, distHaversine(cbind(1, 1))
(distGeo(cbind(1, 1))
) или distHaversine(cbind(c(1, 1), c(1, 2)))
(distGeo(cbind(c(1, 1), c(1, 2)))
)
Данные
Я добавил человека с одной записью в качестве контрольного примера.
id <- c("A", "A", "A", "A", "A", "A", "A", "A", "B", "B", "C")
site <- c("a", "a", "b", "a", "c", "c", "c", "d", "a", "b", "a")
time <- seq(1:11)
lat <- c(1, 1, 2, 1, 3, 3, 3, 4, 1, 2, 1)
lon <- c(1, 1, 2, 1, 3, 3, 3, 4, 1, 2, 1)
df <- data.frame(id, site, time, lat, lon)