R语言按列名内嵌双数值优先级排序数据框列
问题根源
原排序函数的核心逻辑是将列名中提取的所有数值求和,再按和的大小排序列,这个逻辑在区间上下限差距悬殊时会完全失效:比如[500,1000000]的数值和为1000500,远大于[1000,1500)的数值和2500,会导致大上限的小区间被错误排到大区间后面。
修正方案
调整数值处理逻辑,放弃求和排序,改为按数值在列名中出现的先后顺序设置排序优先级,同时完全保留原函数对字母后缀(如方位词)的排序兼容性,修正后的函数如下:
library(magrittr) order_cols <- function(dat) { # 提取列名中所有字母片段,按字母序生成替换索引,适配带文字后缀的场景 s_ordered <- stringi::stri_extract_all_regex(colnames(dat), "[[:alpha:]]+") %>% unlist() %>% unique() %>% sort() if (length(s_ordered) > 1) { cnames <- stringi::stri_replace_all_fixed( colnames(dat), s_ordered, seq_along(s_ordered), vectorise_all = FALSE ) } else { cnames <- colnames(dat) } # 提取列名中所有数值,不做求和操作 num_list <- cnames %>% stringi::stri_extract_all_regex("\\d+") %>% lapply(as.numeric) # 对齐所有列的数值长度,避免长度不一致报错 max_num_len <- max(vapply(num_list, length, integer(1))) num_padded <- lapply(num_list, function(x) c(x, rep(NA, max_num_len - length(x)))) num_matrix <- do.call(rbind, num_padded) # 按数值从左到右的优先级依次排序 do.call(order, as.data.frame(num_matrix)) }
效果验证
测试出错的新区间数据集
dat_I <- structure(list(`[25,250)`=3L, `[0,25)` = 5L, `[100,250)` = 43L, `[100,500)` = 0L, `[1000,1000000]` = 20L, `[1000,1500)` = 0L, `[1500,3000)` = 0L, `[25,100)` = 38L, `[25,50)` = 0L, `[250,500)` = 27L, `[3000,1000000]` = 0L, `[50,100)` = 0L, `[500,1000)` = 44L, `[500,1000000]` = 0L), row.names = "Type_A", class = "data.frame") colnames(dat_I)[order_cols(dat_I)]
运行后得到正确排序结果:
[1] "[0,25)" "[25,50)" "[25,100)" "[25,250)" "[50,100)" "[100,250)" [7] "[100,500)" "[250,500)" "[500,1000)" "[500,1000000]" "[1000,1500)" "[1000,1000000]" [13] "[1500,3000)" "[3000,1000000]"
兼容旧数据集
对原有的两个测试数据集运行函数,排序结果和原函数表现完全一致:
- 纯区间列名的旧数据集会按左端点、右端点顺序正确排列
- 带
east/north/south/west方位后缀的数据集,会先按区间排序,同区间内按方位词字母序排列。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

