R语言janitor包tabyl生成的adorn_ns表格如何按Total列降序排序
问题:janitor包tabyl生成交叉表后adorn_ns追加数值无法正确排序
问题背景
调用janitor包的tabyl函数生成交叉表后,无法对adorn_ns()追加的数值正确排序。需求是按「Total」列的频数降序排序后,传入knitr::kable()用于报表输出。
直接对tabyl对象调用arrange排序后,adorn_ns()会将N值填充到原始位置而非排序后的位置,该问题的成因是tabyl排序时其core属性未同步变更。手动设置因子顺序调整行序的方案效率太低,不适用于类别多、需要复用表格生成逻辑的场景。
示例代码与现象
基础生成代码(未排序)
library(dplyr) library(janitor) # 示例数据 var1 <- c("aaa", "bbb", "ccc", "ccc", "ddd", "ddd", "ddd", "ddd", "aaa", "ddd", "ddd", "bbb", "bbb", "ddd") sex <- c("f", "f", "m", "f", "m", "m", "f", "f", "m", "m", "f", "m", "f", "f") df <- data.frame(var1,sex) # 生成含频数、占比、合计的交叉表 tabyl(df, var1, sex) %>% adorn_totals(where = c("col", "row")) %>% adorn_percentages("col") %>% adorn_pct_formatting(digits = 0) %>% adorn_ns(position = "front")
输出结果符合预期:
| var1 | f | m | Total |
|---|---|---|---|
| aaa | 1 (12%) | 1 (17%) | 2 (14%) |
| bbb | 2 (25%) | 1 (17%) | 3 (21%) |
| ccc | 1 (12%) | 1 (17%) | 2 (14%) |
| ddd | 4 (50%) | 3 (50%) | 7 (50%) |
| Total | 8 (100%) | 6 (100%) | 14 (100%) |
预期效果(按Total降序排序)
| var1 | f | m | Total |
|---|---|---|---|
| ddd | 4 (50%) | 3 (50%) | 7 (50%) |
| bbb | 2 (25%) | 1 (17%) | 3 (21%) |
| aaa | 1 (12%) | 1 (17%) | 2 (14%) |
| ccc | 1 (12%) | 1 (17%) | 2 (14%) |
| Total | 8 (100%) | 6 (100%) | 14 (100%) |
错误尝试与现象
直接在流程中加arrange排序,会出现N值错位、百分比计算错误的问题:
df %>% tabyl(var1,sex) %>% adorn_totals(where = "col") %>% arrange(desc(Total)) %>% adorn_totals(where = "row") %>% adorn_percentages("col") %>% adorn_pct_formatting(digits = 0) %>% adorn_ns(position = "front")
错误输出:
| var1 | f | m | Total |
|---|---|---|---|
| ddd | 1 (50%) | 1 (50%) | 2 (50%) |
| bbb | 2 (25%) | 1 (17%) | 3 (21%) |
| aaa | 1 (12%) | 1 (17%) | 2 (14%) |
| ccc | 4 (12%) | 3 (17%) | 7 (14%) |
| Total | 8 (100%) | 6 (100%) | 14 (100%) |
解决方案
方案1:提前按频数重设因子顺序(推荐,复用性高)
无需手动指定因子水平,通过forcats::fct_infreq自动将分类变量调整为按出现频数降序的因子,tabyl会默认按因子顺序生成表格,从根源避免排序错位问题。
library(dplyr) library(janitor) library(forcats) # 示例数据 var1 <- c("aaa", "bbb", "ccc", "ccc", "ddd", "ddd", "ddd", "ddd", "aaa", "ddd", "ddd", "bbb", "bbb", "ddd") sex <- c("f", "f", "m", "f", "m", "m", "f", "f", "m", "m", "f", "m", "f", "f") df <- data.frame(var1,sex) # 自动将var1转换为按频数降序的因子 df$var1 <- fct_infreq(df$var1) # 原有生成流程无需调整 tabyl(df, var1, sex) %>% adorn_totals(where = c("col", "row")) %>% adorn_percentages("col") %>% adorn_pct_formatting(digits = 0) %>% adorn_ns(position = "front")
方案2:排序后手动传入自定义N值
如果不想改动原始数据的因子结构,可以在排序后提取对应顺序的频数矩阵,传入adorn_ns的ns参数,覆盖默认读取的未排序core属性值。
library(dplyr) library(janitor) # 示例数据 var1 <- c("aaa", "bbb", "ccc", "ccc", "ddd", "ddd", "ddd", "ddd", "aaa", "ddd", "ddd", "bbb", "bbb", "ddd") sex <- c("f", "f", "m", "f", "m", "m", "f", "f", "m", "m", "f", "m", "f", "f") df <- data.frame(var1,sex) # 1. 生成原始核心频数表并按Total排序 core_tab <- tabyl(df, var1, sex) %>% adorn_totals(where = "col") sorted_core <- arrange(core_tab, desc(Total)) # 2. 提取排序后的频数作为自定义N值 custom_ns <- sorted_core[, -1] # 3. 后续加工时传入自定义N值 sorted_core %>% adorn_totals(where = "row") %>% adorn_percentages("col") %>% adorn_pct_formatting(digits = 0) %>% adorn_ns(position = "front", ns = custom_ns)
两种方案都可以得到预期的排序结果,封装为函数后即可复用到不同变量的交叉表生成场景。
内容的提问来源于stack exchange,提问作者Saskia
相关产品推荐
相关产品推荐

