如何在R中基于组内自定义字母表对多列分组排序?
解决自定义分组字母表排序问题
我明白你的痛点:每个选举有自己专属的排序字母表,但之前的代码要么复用了第一个组的字母表,要么报错,没法正确按每组规则排序。咱们来一步步解决这个问题。
问题根源分析
- 你的
last1/last2列是因子类型,直接转换新因子时,原有的因子水平会干扰自定义排序规则,必须先转成字符再重新编码。 - 旧版
arrange_at+funs的语法在现代dplyr里已经被across替代,而且之前的写法没有明确在组内锁定专属字母表,导致作用域混淆。
修正方案1:简洁版(先转因子再排序)
这个方法先在每个组内把姓名列转换成基于专属字母表的因子,再排序生成race_order:
library(dplyr) set.seed(124) a2000 <- sample(letters[1:9], r = F) a2001 <- sample(letters[1:9], r = F) a2002 <- sample(letters[1:9], r = F) d <- data.frame( electionid = rep(1:3, each = 3), year = rep(2000:2002, each = 3), last1 = sample(letters[1:9], r = T), last2 = sample(letters[1:9], r = T), alph = I(list(a2000,a2000,a2000,a2001,a2001,a2001,a2002,a2002,a2002)) ) last_cols <- c("last1", "last2") newd <- d %>% group_by(electionid) %>% # 把姓名列转成字符后,按当前组的字母表生成因子 mutate(across(all_of(last_cols), ~factor(as.character(.x), levels = alph[[1]]))) %>% # 按转换后的因子排序 arrange(across(all_of(last_cols))) %>% # 生成排序序号 mutate(race_order = seq(n())) %>% ungroup() %>% arrange(electionid)
修正方案2:灵活版(用group_modify处理组内逻辑)
如果需要更复杂的组内操作,group_modify能让你更清晰地控制每个组的处理流程:
newd <- d %>% group_by(electionid) %>% group_modify(function(group_data, group_info) { # 提取当前组的专属字母表 current_alph <- group_data$alph[[1]] # 按自定义规则排序并生成序号 group_data %>% mutate(across(all_of(last_cols), ~factor(as.character(.x), levels = current_alph))) %>% arrange(across(all_of(last_cols))) %>% mutate(race_order = seq(n())) }) %>% ungroup() %>% arrange(electionid)
验证结果
运行后你会得到期望的race_order:
- 选举1(字母表
a2000)中,last1 = d会排在last1 = c前面(因为d在a2000的顺序里比c靠前) - 选举2(字母表
a2001)中,last1 = c会排在last1 = h前面,完全符合你给出的预期结果
关键注意点
- 必须先把原有因子列转成字符:
as.character(.x),否则原因子的水平会覆盖自定义的排序规则 alph[[1]]在分组后会自动取当前组的第一个字母表元素(因为每个组内的alph都是重复的同一列表)- 用
across(all_of(last_cols))可以轻松扩展到更多姓名列,不用逐个修改代码
内容的提问来源于stack exchange,提问作者rivb
相关产品推荐
相关产品推荐

