You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于点距离反转数据框列表元素(仅用Base R实现)

问题需求

我有一个存储点数据的dataframe列表trace,数据如下:

# > trace
# [[1]]
# counter          X          Y L1  id    ref
# 1:       1 12.0165469 47.9982008  1 533 EBE 20
# 2:       2 12.0164185 47.9981120  1 533 EBE 20
# 3:       3 12.0163478 47.9980425  1 533 EBE 20
# 4:       4 12.0162577 47.9979528  1 533 EBE 20
# 5:       5 12.0161357 47.9978117  1 533 EBE 20
# 6:       6 12.0160016 47.9976360  1 533 EBE 20
# 
# [[2]]
# counter          X          Y L1   id     ref
# 1:       7 11.8333238 48.1269572  2 4665 St 2081
# 2:       8 11.8330935 48.1267962  2 4665 St 2081
# 
# [[3]]
# counter          X          Y L1   id     ref
# 1:       9 11.8338450 48.1271528  3 4666 St 2081
# 2:      10 11.8335776 48.1270632  3 4666 St 2081

我需要遍历这个列表,完成以下操作:

  • 计算当前dataframe最后一个点与下一个dataframe第一个点的距离(记为distance_1)
  • 计算当前dataframe最后一个点与下一个dataframe最后一个点的距离(记为distance_2)
  • 如果distance_1 > distance_2,则反转下一个dataframe的行顺序

我的伪代码如下:

compare<-function(data_frame,next_dataframe){
    
     distance_1<-geosphere::distGeo(p1=data_frame[nrow(data_frame),],p2=next_dataframe[first(next_dataframe),])
     distance_2<-geosphere::distGeo(p1=data_frame[nrow(data_frame),],p2=next_dataframe[nrow(next_dataframe),])
     if (distance_1>distance_2)
        next_dataframe<-as.data.frame(next_dataframe, 2, rev)
 }
 lapply(x, function(x)compare(x,x+1)) 

期望结果如下(其中第3个dataframe的行被反转):

# > trace
# [[1]]
# counter          X          Y L1  id    ref
# 1:       1 12.0165469 47.9982008  1 533 EBE 20
# 2:       2 12.0164185 47.9981120  1 533 EBE 20
# 3:       3 12.0163478 47.9980425  1 533 EBE 20
# 4:       4 12.0162577 47.9979528  1 533 EBE 20
# 5:       5 12.0161357 47.9978117  1 533 EBE 20
# 6:       6 12.0160016 47.9976360  1 533 EBE 20
# 
# [[2]]
# counter          X          Y L1   id     ref
# 1:       7 11.8333238 48.1269572  2 4665 St 2081
# 2:       8 11.8330935 48.1267962  2 4665 St 2081
# 
# [[3]]
# counter          X          Y L1   id     ref
# 2:      10 11.8335776 48.1270632  3 4666 St 2081
# 1:       9 11.8338450 48.1271528  3 4666 St 2081

请问能否不使用外部包,仅用Base R实现该需求?列表的可复现代码如下:

trace<-list(
  structure(
    list(
      counter = 1:6,
      X = c(
        12.0165469,
        12.0164185,
        12.0163478,
        12.0162577,
        12.0161357,
        12.0160016
      ),
      Y = c(
        47.9982007999381,
        47.9981119999382,
        47.9980424999382,
        47.9979527999382,
        47.9978116999382,
        47.9976359999383
      ),
      L1 = c(1, 1, 1, 1, 1, 1),
      id = c(533, 533,
             533, 533, 533, 533),
      ref = c("EBE 20", "EBE 20", "EBE 20", "EBE 20",
              "EBE 20", "EBE 20")
    ),
    row.names = c(NA,-6L),
    class = c("data.table",
              "data.frame")
  ),
  structure(
    list(
      counter = 7:8,
      X = c(11.8333238, 11.8330935),
      Y = c(48.1269571999072, 48.1267961999073),
      L1 = c(2, 2),
      id = c(4665, 4665),
      ref = c("St 2081", "St 2081")
    ),
    row.names = c(NA,-2L),
    class = c("data.table", "data.frame")
  ),
  structure(
    list(
      counter = 9:10,
      X = c(11.833845, 11.8335776),
      Y = c(48.1271527999072, 48.1270631999072),
      L1 = c(3, 3),
      id = c(4666, 4666),
      ref = c("St 2081", "St 2081")
    ),
    row.names = c(NA,-2L),
    class = c("data.table", "data.frame")
  )
)

Base R实现方案

完全可以用Base R实现,核心是先实现球面距离计算逻辑替代外部包函数,再遍历列表处理每个后续dataframe:

1. 实现Base R版球面距离计算函数

用Haversine公式实现WGS84坐标系下的大圆距离计算(和geosphere::distGeo逻辑一致):

# 计算两点间的球面距离(单位:米)
dist_sphere <- function(p1, p2) {
  # 提取经纬度(X为经度,Y为纬度)
  lon1 <- p1[["X"]]
  lat1 <- p1[["Y"]]
  lon2 <- p2[["X"]]
  lat2 <- p2[["Y"]]
  
  # 转为弧度
  lon1 <- lon1 * pi / 180
  lat1 <- lat1 * pi / 180
  lon2 <- lon2 * pi / 180
  lat2 <- lat2 * pi / 180
  
  # Haversine公式计算大圆距离
  dlon <- lon2 - lon1
  dlat <- lat2 - lat1
  a <- sin(dlat/2)^2 + cos(lat1) * cos(lat2) * sin(dlon/2)^2
  c <- 2 * atan2(sqrt(a), sqrt(1-a))
  r <- 6378137 # WGS84地球半径(米)
  return(r * c)
}

2. 遍历列表处理dataframe

用for循环遍历列表(因为需要修改后续元素,lapply不适合这种有状态的迭代):

# 遍历列表,处理每个后续dataframe
for (i in seq_along(trace)[-length(trace)]) {
  current_df <- trace[[i]]
  next_df <- trace[[i+1]]
  
  # 获取需要计算距离的点
  last_current <- current_df[nrow(current_df), c("X", "Y")]
  first_next <- next_df[1, c("X", "Y")]
  last_next <- next_df[nrow(next_df), c("X", "Y")]
  
  # 计算两个距离
  distance_1 <- dist_sphere(last_current, first_next)
  distance_2 <- dist_sphere(last_current, last_next)
  
  # 判断是否反转下一个dataframe的行顺序
  if (distance_1 > distance_2) {
    trace[[i+1]] <- next_df[nrow(next_df):1, ]
  }
}

3. 验证结果

运行代码后查看trace[[3]],可以看到行已按需求反转:

trace[[3]]
#    counter        X        Y L1   id     ref
# 2:      10 11.83358 48.12706  3 4666 St 2081
# 1:       9 11.83385 48.12715  3 4666 St 2081

内容的提问来源于stack exchange,提问作者Andreas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:32:01