对称tibble重排序:如何用tidyverse对齐首列值与列名顺序
问题描述
我希望将输出结果的首列值(本质为行名)与其余列的列名顺序保持一致,此前找到的两种解决方案在自定义函数中均无法生效,请问是否有对应的tidyverse方案可以解决该问题?
原有代码
foo <- function(data, study_id, ...){ study_id <- rlang::ensym(study_id) cat_mod <- rlang::ensyms(...) purrr::map(cat_mod, ~ { studies_cats <- data %>% dplyr::group_by(!!study_id, !!.x) %>% dplyr::summarise(effects = n(), .groups = 'drop') nm1 <- rlang::as_string(.x) cat_names <- paste0(nm1, c(".x", ".y")) studies_cats <- studies_cats %>% dplyr::inner_join(studies_cats, by = rlang::as_string(study_id)) %>% dplyr::group_by(!!!rlang::syms(cat_names)) %>% dplyr::summarise( studies = n(), effects = sum(effects.x), .groups = 'drop') %>% dplyr::mutate(n = paste0(studies, " (", effects, ")") ) studies_cats %>% dplyr::select(-studies, -effects) %>% tidyr::pivot_wider(names_from = cat_names[2], values_from = n) %>% dplyr::rename_with(~nm1, cat_names[1]) })} # 使用示例(可以看到列名顺序为`0,1,10,2,3`,但首列值顺序为`0,1,2,3,10`,二者不一致) d <- read.csv("https://raw.githubusercontent.com/rnorouzian/s/main/w7_smd_raw.csv") foo(d, study, error.type) # 原输出结果: # error.type `0` `1` `10` `2` `3` # <fct> <chr> <chr> <chr> <chr> <chr> #1 0 27 (189) 1 (6) 1 (2) NA NA #2 1 1 (18) 16 (118) 2 (10) 2 (6) 2 (6) #3 2 NA 2 (6) NA 6 (33) 2 (6) #4 3 NA 2 (6) NA 2 (6) 5 (27) #5 10 1 (2) 2 (22) 6 (48) NA NA
解决方案
出现顺序不一致的核心原因是列名和首列值默认按字符规则排序,数值10的字符排序会排在2之前,只需要在原函数末尾新增3行代码,统一列和行的排序规则即可,修改后的完整函数如下:
foo <- function(data, study_id, ...){ study_id <- rlang::ensym(study_id) cat_mod <- rlang::ensyms(...) purrr::map(cat_mod, ~ { studies_cats <- data %>% dplyr::group_by(!!study_id, !!.x) %>% dplyr::summarise(effects = n(), .groups = 'drop') nm1 <- rlang::as_string(.x) cat_names <- paste0(nm1, c(".x", ".y")) studies_cats <- studies_cats %>% dplyr::inner_join(studies_cats, by = rlang::as_string(study_id)) %>% dplyr::group_by(!!!rlang::syms(cat_names)) %>% dplyr::summarise( studies = n(), effects = sum(effects.x), .groups = 'drop') %>% dplyr::mutate(n = paste0(studies, " (", effects, ")") ) studies_cats %>% dplyr::select(-studies, -effects) %>% tidyr::pivot_wider(names_from = cat_names[2], values_from = n) %>% dplyr::rename_with(~nm1, cat_names[1]) %>% # 按数值顺序调整列的排列顺序 dplyr::relocate(!!nm1, all_of(sort(as.numeric(colnames(.)[-1])))) %>% # 将首列转换为有序因子,层级和列名顺序保持一致 dplyr::mutate(!!nm1 := factor(!!rlang::sym(nm1), levels = sort(as.numeric(colnames(.)[-1])))) %>% # 按首列顺序排序行 dplyr::arrange(!!rlang::sym(nm1)) }) }
测试效果
运行测试代码后输出结果如下,列顺序和首列值顺序均为0,1,2,3,10,符合预期:
# error.type `0` `1` `2` `3` `10` # <fct> <chr> <chr> <chr> <chr> <chr> #1 0 27 (189) 1 (6) NA NA 1 (2) #2 1 1 (18) 16 (118) 2 (6) 2 (6) 2 (10) #3 2 NA 2 (6) 6 (33) 2 (6) NA #4 3 NA 2 (6) 2 (6) 5 (27) NA #5 10 1 (2) 2 (22) NA NA 6 (48)
内容的提问来源于stack exchange,提问作者rnorouzian
相关产品推荐
相关产品推荐

