如何编写通用tidyverse代码对列数可变的DataFrame排序?
动态列数的DataFrame通用排序方案
针对列数可变(比如新增categ_3、points_3这类列)的情况,用tidyverse的准引用功能就能实现无需手动修改的通用排序,完全匹配你需要的排序规则:先按total降序,再按所有categ_*列升序,最后按所有points_*列降序。
核心实现思路
- 自动提取所有以
categ_开头的列名,作为升序排序项 - 自动生成所有
points_*列的desc(列名)表达式,作为降序排序项 - 用
!!!操作符把这些动态生成的参数注入到arrange()中
通用代码示例
library(tidyverse) # 测试用数据(包含3个categ和3个points列) set.seed(1) nrows <- 20 df <- tibble( other_text = sample(letters, nrows, replace = TRUE), categ_1 = sample(c("A", "B"), nrows, replace = TRUE), categ_2 = sample(c("A", "B"), nrows, replace = TRUE), categ_3 = sample(c("A", "B"), nrows, replace = TRUE), points_1 = sample(20:25, nrows, replace = TRUE), points_2 = sample(20:25, nrows, replace = TRUE), points_3 = sample(20:25, nrows, replace = TRUE) ) %>% rowwise() %>% mutate(total = sum(c_across(starts_with("points_")))) %>% ungroup() # 通用排序代码,新增categ/points列无需修改这里 df_sorted <- df %>% arrange( desc(total), # 注入所有categ_*列(升序) !!!syms(str_subset(names(.), "^categ_")), # 注入所有points_*列的降序表达式 !!!map(str_subset(names(.), "^points_"), ~expr(desc(!!sym(.x)))) )
代码细节说明
str_subset(names(.), "^categ_"):用正则匹配提取所有categ_开头的列名syms():把列名字符串转换成tidyverse能识别的符号对象map(..., ~expr(desc(!!sym(.x)))):对每个points列生成desc(列名)的排序表达式!!!:把列表里的元素逐个展开成arrange()的独立参数,相当于手动写categ_1, categ_2, categ_3或者desc(points_1), ...
封装成可复用函数
如果经常需要用这个逻辑,直接封装成函数更方便:
arrange_dynamic <- function(data) { categ_cols <- str_subset(names(data), "^categ_") points_cols <- str_subset(names(data), "^points_") data %>% arrange( desc(total), !!!syms(categ_cols), !!!map(points_cols, ~expr(desc(!!sym(.x)))) ) } # 调用函数排序 df_sorted <- arrange_dynamic(df)
这样不管后续新增多少个categ_*或points_*列,只要列名规则不变,排序代码都能自动适配,不用手动调整。
内容的提问来源于stack exchange,提问作者crestor
相关产品推荐
相关产品推荐

