如何将dplyr的group_by/mutate/filter/pivot_wider转为data.table实现?
使用data.table替代dplyr实现高效数据处理
我有如下数据框:
dat <- structure(list(ref_string = c("K", "Y", "Q", "C", "H", "A", "S", "Y", "L", "Y"), peptide_name = c("p47", "p666", "p506", "p356", "p598", "p458", "p448", "p117", "p232", "p464"), peptide = c("FKDHKHIDVKgrrrskrrrrTRCYHIDPHH", "FKDHKHIDVKsrgrkrrrrrTRCYHIDPHH", "FKDHKHIDVKrrrrskrgrrTRCYHIDPHH", "FKDHKHIDVKrrrgrrrrskTRCYHIDPHH", "FKDHKHIDVKrsgrrrrrkrTRCYHIDPHH", "FKDHKHIDVKrrrrrkrrgsTRCYHIDPHH", "FKDHKHIDVKrrrrrgrskrTRCYHIDPHH", "FKDHKHIDVKkrrrrsrrgrTRCYHIDPHH", "FKDHKHIDVKrkrrrrsgrrTRCYHIDPHH", "FKDHKHIDVKrrrrrksrrgTRCYHIDPHH"), status = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), class = c("rowwise_df", "tbl_df", "tbl", "data.frame" ), row.names = c(NA, -10L), groups = structure(list(.rows = structure(list( 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L), ptype = integer(0), class = c("vctrs_list_of", "vctrs_vctr", "list"))), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame")))
对应的表格展示:
# A tibble: 10 × 4 # Rowwise: ref_string peptide_name peptide status <chr> <chr> <chr> <dbl> 1 K p47 FKDHKHIDVKgrrrskrrrrTRCYHIDPHH 0 2 Y p666 FKDHKHIDVKsrgrkrrrrrTRCYHIDPHH 0 3 Q p506 FKDHKHIDVKrrrrskrgrrTRCYHIDPHH 0 4 C p356 FKDHKHIDVKrrrgrrrrskTRCYHIDPHH 0 5 H p598 FKDHKHIDVKrsgrrrrrkrTRCYHIDPHH 0 6 A p458 FKDHKHIDVKrrrrrkrrgsTRCYHIDPHH 0 7 S p448 FKDHKHIDVKrrrrrgrskrTRCYHIDPHH 0 8 Y p117 FKDHKHIDVKkrrrrsrrgrTRCYHIDPHH 0 9 L p232 FKDHKHIDVKrkrrrrsgrrTRCYHIDPHH 0 10 Y p464 FKDHKHIDVKrrrrrksrrgTRCYHIDPHH 0
原本使用dplyr的处理代码,但在大数据集上运行缓慢:
library(tidyverse) dat %>% dplyr::group_by(peptide_name, peptide) %>% mutate(tot = sum(status)) %>% filter(tot == 0) %>% pivot_wider(names_from = ref_string, values_from = status) %>% dplyr::select(-tot) %>% ungroup()
输出结果:
# A tibble: 10 × 10 peptide_name peptide K Y Q C H A S L <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 p47 FKDHKHIDVKgrrrskrrrrTRCYHIDPHH 0 NA NA NA NA NA NA NA 2 p666 FKDHKHIDVKsrgrkrrrrrTRCYHIDPHH NA 0 NA NA NA NA NA NA 3 p506 FKDHKHIDVKrrrrskrgrrTRCYHIDPHH NA NA 0 NA NA NA NA NA 4 p356 FKDHKHIDVKrrrgrrrrskTRCYHIDPHH NA NA NA 0 NA NA NA NA 5 p598 FKDHKHIDVKrsgrrrrrkrTRCYHIDPHH NA NA NA NA 0 NA NA NA 6 p458 FKDHKHIDVKrrrrrkrrgsTRCYHIDPHH NA NA NA NA NA 0 NA NA 7 p448 FKDHKHIDVKrrrrrgrskrTRCYHIDPHH NA NA NA NA NA NA 0 NA 8 p117 FKDHKHIDVKkrrrrsrrgrTRCYHIDPHH NA 0 NA NA NA NA NA NA 9 p232 FKDHKHIDVKrkrrrrsgrrTRCYHIDPHH NA NA NA NA NA NA NA 0 10 p464 FKDHKHIDVKrrrrrksrrgTRCYHIDPHH NA 0 NA NA NA NA NA NA
data.table实现方案
完整代码
library(data.table) # 转换为data.table格式 dt <- as.data.table(dat) # 1. 分组计算status的总和,按peptide_name和peptide分组 dt[, tot := sum(status), by = .(peptide_name, peptide)] # 2. 过滤出tot等于0的行 dt_filtered <- dt[tot == 0] # 3. 使用dcast实现宽表转换,对应dplyr的pivot_wider result <- dcast(dt_filtered, peptide_name + peptide ~ ref_string, value.var = "status") # 4. 删除tot列 result[, tot := NULL] # 查看最终结果 result
运行结果
peptide_name peptide K Y Q C H A S L 1: p47 FKDHKHIDVKgrrrskrrrrTRCYHIDPHH 0 NA NA NA NA NA NA NA 2: p666 FKDHKHIDVKsrgrkrrrrrTRCYHIDPHH NA 0 NA NA NA NA NA NA 3: p506 FKDHKHIDVKrrrrskrgrrTRCYHIDPHH NA NA 0 NA NA NA NA NA 4: p356 FKDHKHIDVKrrrgrrrrskTRCYHIDPHH NA NA NA 0 NA NA NA NA 5: p598 FKDHKHIDVKrsgrrrrrkrTRCYHIDPHH NA NA NA NA 0 NA NA NA 6: p458 FKDHKHIDVKrrrrrkrrgsTRCYHIDPHH NA NA NA NA NA 0 NA NA 7: p448 FKDHKHIDVKrrrrrgrskrTRCYHIDPHH NA NA NA NA NA NA 0 NA 8: p117 FKDHKHIDVKkrrrrsrrgrTRCYHIDPHH NA 0 NA NA NA NA NA NA 9: p232 FKDHKHIDVKrkrrrrsgrrTRCYHIDPHH NA NA NA NA NA NA NA 0 10: p464 FKDHKHIDVKrrrrrksrrgTRCYHIDPHH NA 0 NA NA NA NA NA NA
关键步骤解析
- 分组求和:data.table采用
[, 新列 := 计算逻辑, by = .(分组列)]的语法,这里必须和dplyr保持一致,按peptide_name和peptide双列分组 - 行过滤:直接用
dt[过滤条件]即可实现dplyr中filter的功能,语法更简洁 - 宽表转换:使用
dcast函数,格式为dcast(数据集, 标识列 ~ 列名来源列, value.var = 取值列),完美对应pivot_wider的功能 - 列删除:用
[, 列名 := NULL]可以快速删除指定列,无需额外函数
大数据集优化
如果处理超大规模数据,可以将步骤合并为链式操作,减少中间数据对象的创建,进一步提升效率:
result <- dt[, tot := sum(status), by = .(peptide_name, peptide) ][tot == 0 ][, dcast(.SD, peptide_name + peptide ~ ref_string, value.var = "status") ][, tot := NULL]
内容的提问来源于stack exchange,提问作者littleworth
相关产品推荐
相关产品推荐

