如何在R语言中获取距离矩阵每行的最小值、第二小值及对应点ID
先回顾下你的距离矩阵头部数据:
head(distances)
2914 2915 2924 2953 2954 2988 2989 3183 3184 3341
317_1 11116.25 10534.133 12759.250 2418.834 8386.288 9123.886 9573.037 4396.882 9179.407 3420.459
317_2 10827.64 10003.469 12307.238 1996.878 7994.617 8543.298 9006.385 4795.352 9363.853 3804.140
317_3 13544.06 10036.287 7341.391 3079.408 2918.336 7298.468 8283.268 9861.902 14234.768 8875.232
317_4 13495.06 9859.046 7102.862 3271.923 2744.106 7081.206 8082.261 10058.709 14362.919 9069.278
317_5 14652.07 11234.219 7378.551 3642.721 2782.711 8468.613 9469.082 10257.441 14899.174 9292.621
317_6 14796.52 11265.946 7108.030 3893.178 2511.896 8458.536 9476.372 10530.603 15150.529 9564.130
你已经通过这段代码实现了每行最小值、对应点ID和聚类ID的提取:
result <- t(sapply(seq(nrow(distances)), function(z) { j <- which.min(distances[z,]) c(rownames(distances)[z], colnames(distances)[j], distances[z,j]) })) min_dist1 <- as.data.frame(result) colnames(min_dist1) <- c("cluster_id", "point_id", "min_dist") # min_dist单位为米
现在要新增第二小距离和对应点ID的列,这里给你两种实用的解决方案:
方案一:基于排序的直观实现
这个方法逻辑清晰,把每行数据转成带列名的小数据框,排序后直接提取前两位结果,后续要扩展到前N小值也很方便:
result <- t(sapply(seq(nrow(distances)), function(z) { # 将当前行转换为带列名的数据框,方便关联点ID和距离 row_df <- data.frame( point_id = colnames(distances), dist = as.numeric(distances[z,]), stringsAsFactors = FALSE ) # 按距离升序排序 sorted_row <- row_df[order(row_df$dist), ] # 提取所需的聚类ID、最小/第二小的点ID和距离 c( rownames(distances)[z], sorted_row$point_id[1], sorted_row$dist[1], sorted_row$point_id[2], sorted_row$dist[2] ) })) # 转换为数据框并设置列名 final_result <- as.data.frame(result, stringsAsFactors = FALSE) colnames(final_result) <- c( "cluster_id", "min_point_id", "min_dist", "second_min_point_id", "second_min_dist" ) # 把距离列转为数值型(因为sapply返回的是字符矩阵) final_result$min_dist <- as.numeric(final_result$min_dist) final_result$second_min_dist <- as.numeric(final_result$second_min_dist)
方案二:基于索引的高效实现
如果追求更高效率,可以直接操作向量索引,避免数据框转换的开销:
result <- t(sapply(seq(nrow(distances)), function(z) { row_vals <- as.numeric(distances[z,]) # 获取按距离升序排列的索引 sorted_indices <- order(row_vals) # 提取最小和第二小值对应的索引 min_idx <- sorted_indices[1] second_min_idx <- sorted_indices[2] # 组装结果 c( rownames(distances)[z], colnames(distances)[min_idx], row_vals[min_idx], colnames(distances)[second_min_idx], row_vals[second_min_idx] ) })) final_result <- as.data.frame(result, stringsAsFactors = FALSE) colnames(final_result) <- c( "cluster_id", "min_point_id", "min_dist", "second_min_point_id", "second_min_dist" ) # 批量转换距离列为数值型 final_result[, c("min_dist", "second_min_dist")] <- lapply( final_result[, c("min_dist", "second_min_dist")], as.numeric )
注意事项
- 一定要把距离列转为数值型:因为
sapply返回的是字符矩阵,直接转成数据框后距离会是字符类型,后续统计计算会出错。 - 如果矩阵中存在重复的最小值,
which.min只会返回第一个出现的索引,而排序方案会保留排序后的顺序;如果需要处理重复值场景,可以根据需求调整提取逻辑(比如先筛选所有最小值,再取剩余值中的最小)。
内容的提问来源于stack exchange,提问作者mto23

