group_by()未按预期分组?计算个体连续地理点距离遇问题
问题:按ID分组计算连续坐标点距离时group_by未生效
需求
按ID_PIT分组,计算每个个体不同时间点连续地理坐标间的距离。
数据集示例
| DATE | lon | lat | ID_PIT |
|---|---|---|---|
| 08/07/2016 | -0.616080768410155 | 45.8430975765271 | 43596 |
| 24/02/2018 | -0.632287900326278 | 45.8177515273291 | 43596 |
| 13/08/2018 | -0.616080768410155 | 45.8430975765271 | 43596 |
| 28/09/2020 | -0.68536933637008 | 45.882032720692 | 220217 |
| 22/01/2021 | -0.142898405525702 | 45.6436089508837 | 220217 |
| 31/01/2021 | -0.629911009070587 | 45.7851507139944 | 43596 |
| 30/07/2021 | -0.616080768410155 | 45.8430975765271 | 43596 |
尝试的代码
a <- Data %>% group_by(ID_PIT) %>% mutate(dist = c(NA, geosphere::distVincentyEllipsoid(cbind(lon, lat)))) %>% ungroup()
异常结果
| DATE | lon | lat | ID_PIT | dist |
|---|---|---|---|---|
| 08/07/2016 | -0.616080768410155 | 45.8430975765271 | 43596 | NA |
| 24/02/2018 | -0.632287900326278 | 45.8177515273291 | 43596 | 3085.80808464715 |
| 13/08/2018 | -0.616080768410155 | 45.8430975765271 | 43596 | 3085.80808464669 |
| 28/09/2020 | -0.68536933637008 | 45.882032720692 | 220217 | 6904.98418513713 |
| 22/01/2021 | -0.142898405525702 | 45.6436089508837 | 220217 | 49831.0971131536 |
| 31/01/2021 | -0.629911009070587 | 45.7851507139944 | 43596 | 41053.013590596 |
| 30/07/2021 | -0.616080768410155 | 45.8430975765271 | 43596 | 6529.74315318872 |
问题原因
- 分组后未按时间排序:同一
ID_PIT的记录在数据集中分散排列,未按时间顺序整理,导致计算的是原始行顺序的连续点距离,而非时间维度的连续点。 - 距离函数使用逻辑错误:
geosphere::distVincentyEllipsoid传入单个矩阵时,会计算矩阵内所有连续行对的距离,但结合group_by使用时,该方式无法精准对应组内每个点与前一个时间点的点的距离,甚至会因为原始行的跨ID连续性,出现误计算跨ID距离的情况(实际是分组后组内行顺序混乱导致的错误匹配)。 - 若出现完全跨ID计算的情况,可能是
ID_PIT列类型异常(如含空格、因子与数字混合)或dplyr分组未正确生效(如未加载dplyr、版本兼容问题)。
解决方案
步骤1:转换日期格式并分组排序
先将DATE列转为日期格式,确保能按时间排序;再按ID_PIT分组,组内按时间升序排列。
步骤2:计算组内连续时间点的距离
使用lag()获取每个点的前一个时间点坐标,调用distVincentyEllipsoid计算两点距离,组内第一行设为NA。
修正后的代码
library(dplyr) library(geosphere) # 转换日期格式(按数据集的日期格式调整format参数) Data$DATE <- as.Date(Data$DATE, format = "%d/%m/%Y") a <- Data %>% group_by(ID_PIT) %>% arrange(DATE, .by_group = TRUE) %>% # 组内按时间排序 mutate( dist = case_when( row_number() == 1 ~ NA_real_, TRUE ~ distVincentyEllipsoid( cbind(lon, lat), cbind(lag(lon), lag(lat)) ) ) ) %>% ungroup()
预期结果
处理后每个ID_PIT组内的距离按时间顺序计算,不会出现跨ID的错误距离,且第一行距离为NA。
内容的提问来源于stack exchange,提问作者Gabriel Metegnier
相关产品推荐
相关产品推荐

