You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R中janitor包tabyl()生成累积交叉表adorn_ns报错问题

问题说明

我近期重启数月前的旧项目做调整,项目中使用janitor包生成带累积百分比的交叉表,此前可正常运行的相同代码,如今执行直接返回错误。
复现代码如下:

library(tidyverse)
library(janitor)
library(scales)

tb <- tibble(
    survey = c(1, 3, 4, 4, 2, 1, 4, 1, 2, 2),
    diagnosis = factor(c("yes", "yes", "yes", "no", "yes", "yes", "no", "no", "yes", "yes"))
)
  
tb %>% 
    tabyl(survey, diagnosis) %>% 
    mutate(cum_no = percent(cumsum(no)/sum(no), accuracy = 0.1)) %>% 
    mutate(cum_yes = percent(cumsum(yes)/sum(yes), accuracy = 0.1)) %>% 
    adorn_totals(where = "both", ... = c(yes, no)) %>% 
    adorn_percentages(denominator = "col", na.rm = TRUE, ... = c(yes, no, Total)) %>%
    adorn_pct_formatting() %>%
    adorn_ns(position = "front", ... = c(yes, no, Total))
#> Error in adorn_ns(., position = "front", ... = c(yes, no, Total)): argument "ns" cannot be null; if not calling adorn_ns() on a data.frame of class "tabyl", pass your own value for ns

以上复现片段生成于2022-06-03,基于reprex包v2.0.1制作

经初步排查,问题出在计算cum_no、cum_yes两个累积百分比变量的mutate步骤:删除这两行mutate代码后,整个表处理流程可正常运行。
判断adorn_ns会从tabyl类的core属性中读取频数N值,初始tabyl对象结构包含完整的类属性:

> str(tabyl(tb, survey, diagnosis))
Classes ‘tabyl’ and 'data.frame':   4 obs. of  3 variables:
 $ survey: num  1 2 3 4
 $ no    : num  1 0 0 2
 $ yes   : num  2 3 1 1
 - attr(*, "core")='data.frame':    4 obs. of  3 variables:
  ..$ survey: num [1:4] 1 2 3 4
  ..$ no    : num [1:4] 1 0 0 2
  ..$ yes   : num [1:4] 2 3 1 1
 - attr(*, "tabyl_type")= chr "two_way"
 - attr(*, "var_names")=List of 2
  ..$ row: chr "survey"
  ..$ col: chr "diagnosis"

但执行mutate新增累积百分比列后,对象的core属性已丢失:

Classes ‘tabyl’ and 'data.frame':   4 obs. of  5 variables:
 $ survey : num  1 2 3 4
 $ no     : num  1 0 0 2
 $ yes    : num  2 3 1 1
 $ cum_no : chr  "33.3%" "33.3%" "33.3%" "100.0%"
 $ cum_yes: chr  "28.6%" "71.4%" "85.7%" "100.0%"

目前不清楚该问题的正确处理方式,也疑惑为何此前代码可正常运行、现在却失效,怀疑是janitor包版本更新导致相关逻辑变动。


问题原因与解决方案

你的判断是对的,报错确实来自janitor包版本迭代的逻辑变动:

  • janitor 2.0.0及之后版本调整了tabyl对象的属性保留规则,用dplyr::mutate()给tabyl新增列时,不会自动同步更新存储原始频数的core属性,后续adorn_*系列函数检测到core属性和当前数据结构不匹配,会直接丢弃不合法的core属性,最终adorn_ns()找不到原始频数存储位置,就会抛出对应的报错。
  • 旧版本janitor对tabyl属性的校验逻辑更宽松,mutate新增列时不会丢失core属性,因此旧代码可以正常运行。

最稳妥的处理方式是调整代码执行顺序,把自定义累积百分比列的计算放到所有adorn_*标准处理流程结束之后,避免自定义列干扰tabyl的核心属性:

tb %>% 
  # 先走janitor标准交叉表处理流程
  tabyl(survey, diagnosis) %>% 
  adorn_totals(where = "both", ... = c(yes, no)) %>% 
  adorn_percentages(denominator = "col", na.rm = TRUE, ... = c(yes, no, Total)) %>%
  adorn_pct_formatting() %>%
  adorn_ns(position = "front", ... = c(yes, no, Total)) %>%
  # 所有内置格式处理完成后,再计算自定义累积列
  mutate(
    # 从"频数 (百分比)"格式的单元格中提取原始频数值计算累积占比
    cum_no = percent(cumsum(as.numeric(str_extract(no, "^\\d+")))/sum(as.numeric(str_extract(no, "^\\d+"))), accuracy = 0.1),
    cum_yes = percent(cumsum(as.numeric(str_extract(yes, "^\\d+")))/sum(as.numeric(str_extract(yes, "^\\d+"))), accuracy = 0.1)
  )

如果坚持要在流程早期添加累积列,也可以在mutate操作后手动重写core属性,但这种方式容易在后续adorn步骤触发其他属性不匹配问题,不推荐使用。


内容的提问来源于stack exchange,提问作者Ntgllr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 10:12:22