R中按CENTER分组并保留原始顺序生成新排序列的问题
问题描述
我有一个数据集,需要按CENTER分组,但记录必须保留原始数据集的排序顺序(例如VUMC为第一组、KUL为第二组,该顺序需保持不变)。我熟悉SQL中的DENSE_RANK实现方式,但在R中使用基础包的rank函数和dplyr库的dense_rank函数均未得到预期结果。
尝试的代码
df_results <- read.table(text = " CENTER;FILE;HB;LineNr VUMC;NPUT_00172.MRC;12.7;1 VUMC;PHYM_00577.MRC;11.9;2 VUMC;CPNU_00625.MRC;9.2;3 KUL;UYTC_00146.MRC;10.8;4 KUL;UKFA_00766.MRC;10.7;5 LOR;RFAF_00105.MRC;12.9;6 LOR;ARFW_00961.MRC;12.4;7 KUL;HMCU_00630.MRC;8.6;8 VUMC;FPRB_00509.MRC;10.8;9 LOR;TWUA_00651.MRC;10.6;10 ", header = TRUE, sep = ";", na.strings = "") library(dplyr) df_results <- df_results %>% group_by(CENTER) %>% mutate(min_LineNr = min(LineNr), dense_linenr = rank(min_LineNr), NewSortOrder = (dense_linenr*1000 + LineNr) )
其中min(LineNr)部分符合预期,但rank返回了2.0和2.5这类异常值,单独执行mutate(rank..)也无改善;使用dense_rank则新列全为1,均不符合预期。
当前输出结果
| CENTER | FILE | HB | LineNr | min_LineNr | dense_linenr | NewSortOrder |
|---|---|---|---|---|---|---|
| VUMC | NPUT_00172.MRC | 12.7 | 1 | 1 | 2.5 | 2501 |
| VUMC | PHYM_00577.MRC | 11.9 | 2 | 1 | 2.5 | 2502 |
| VUMC | CPNU_00625.MRC | 9.2 | 3 | 1 | 2.5 | 2503 |
| KUL | UYTC_00146.MRC | 10.8 | 4 | 4 | 2 | 2004 |
| KUL | UKFA_00766.MRC | 10.7 | 5 | 4 | 2 | 2005 |
| LOR | RFAF_00105.MRC | 12.9 | 6 | 6 | 2 | 2006 |
| LOR | ARFW_00961.MRC | 12.4 | 7 | 6 | 2 | 2007 |
| KUL | HMCU_00630.MRC | 8.6 | 8 | 4 | 2 | 2008 |
| VUMC | FPRB_00509.MRC | 10.8 | 9 | 1 | 2.5 | 2509 |
| LOR | TWUA_00651.MRC | 10.6 | 10 | 6 | 2 | 2010 |
期望结果
| CENTER | FILE | HB | LineNr | NewSortOrder |
|---|---|---|---|---|
| VUMC | NPUT_00172.MRC | 12.7 | 1 | 1001 |
| VUMC | PHYM_00577.MRC | 11.9 | 2 | 1002 |
| VUMC | CPNU_00625.MRC | 9.2 | 3 | 1003 |
| VUMC | FPRB_00509.MRC | 10.8 | 9 | 1004 |
| KUL | UYTC_00146.MRC | 10.8 | 4 | 2001 |
| KUL | UKFA_00766.MRC | 10.7 | 5 | 2002 |
| KUL | HMCU_00630.MRC | 8.6 | 8 | 2003 |
| LOR | RFAF_00105.MRC | 12.9 | 6 | 3001 |
| LOR | ARFW_00961.MRC | 12.4 | 7 | 3002 |
| LOR | TWUA_00651.MRC | 10.6 | 10 | 3003 |
我的问题
- 为什么
rank和dense_rank会产生这些异常结果? - 使用哪种R代码或库能最直接地生成目标的NewSortOrder列?
解答
问题1:异常结果的原因
rank函数的问题:你在group_by(CENTER)后调用rank(min_LineNr),此时每个分组内的min_LineNr是同一个值(比如VUMC组全是1),rank默认采用"average"排序规则,当所有值相同时,会计算这些值在整个向量中的平均排名。更关键的是,你需要的是按CENTER出现的先后顺序排名,而非按min_LineNr的数值排序,所以这个逻辑本身就不符合需求。dense_rank函数的问题:分组内调用dense_rank(min_LineNr)时,每组内的min_LineNr完全相同,因此返回1是正常行为,但这不是你需要的跨分组、按出现顺序的排名。
问题2:生成目标NewSortOrder的方法
方法1:使用dplyr(最直接)
核心逻辑是先获取CENTER的首次出现顺序,分配组序号;再在每个组内生成组内序号,最后计算组序号*1000 + 组内序号:
library(dplyr) df_results <- df_results %>% # 生成CENTER的首次出现顺序排名 mutate(center_rank = match(CENTER, unique(CENTER))) %>% # 按CENTER分组,生成组内序号(保留原始顺序) group_by(CENTER) %>% mutate(group_seq = row_number(), NewSortOrder = center_rank * 1000 + group_seq) %>% ungroup() %>% # 可选:删除中间辅助列 select(-center_rank, -group_seq)
方法2:使用基础R
无需依赖dplyr,用基础R也能实现:
# 获取CENTER的唯一出现顺序 unique_centers <- unique(df_results$CENTER) df_results$center_rank <- match(df_results$CENTER, unique_centers) # 生成组内序号 df_results$group_seq <- ave(df_results$LineNr, df_results$CENTER, FUN = function(x) seq_along(x)) # 计算目标列 df_results$NewSortOrder <- df_results$center_rank * 1000 + df_results$group_seq # 可选:删除中间辅助列 df_results <- df_results[, !names(df_results) %in% c("center_rank", "group_seq")]
两种方法都能生成符合预期的NewSortOrder列,同时严格保留原始数据中CENTER的出现顺序。
内容的提问来源于stack exchange,提问作者BdR
相关产品推荐
相关产品推荐

