R函数内xtabs公式调用时动态变量识别报错问题
问题说明
使用data.table封装分组计算均值、生成交叉表的函数时,传入动态生成的列名触发如下报错:
Error in model.frame.default(formula = mean_val_by_var ~ cat, data = dat) : variable lengths differ (found for 'cat') Called from: model.frame.default(formula = mean_val_by_var ~ cat, data = dat)
原始测试代码如下:
library(data.table) dat <- data.table(type=c("V","X","Y","Z","V","X","Y","Z"), cat=c("A", "B", "A", "B","C", "D","C", "D"), val=c(2,2,3,4,5,6,7,6) ) myfun <- function(dat, by_var, value="val") { mean_val_by_var <- paste0("mean_val_by_", by_var) print(mean_val_by_var) # [1] "mean_val_by_cat" print(value) # [1] "val" print(by_var) # [1] "cat" dat[, (mean_val_by_var) := mean(get(value)), by=c(by_var)] xtabs(mean_val_by_var ~ cat, dat) } myfun(dat, by_var="cat")
报错原因
- R的公式对象不会自动解析环境中的字符串变量作为数据列名:
mean_val_by_var是存储了目标列名的长度为1的字符向量,直接写在公式左侧时,R不会到dat中匹配同名列,只会将这个长度1的向量和右侧长度为8的cat列做对齐,最终触发长度不匹配错误。 - 原函数交叉表的分组列硬编码为
cat,传入其他分组变量时函数无法适配,通用性差。
修复方案
核心是通过reformulate()动态生成公式对象,让R正确识别字符串对应的列名,同时把硬编码的分组列替换为参数传入的变量名,修复后的代码如下:
myfun <- function(dat, by_var, value="val") { mean_val_by_var <- paste0("mean_val_by_", by_var) # 分组计算均值,原data.table赋值逻辑正确无需修改 dat[, (mean_val_by_var) := mean(get(value)), by = by_var] # 动态构建xtabs所需公式,自动识别字符串对应的列名 tab_formula <- reformulate(termlabels = by_var, response = mean_val_by_var) xtabs(formula = tab_formula, data = dat) } # 测试运行 myfun(dat, by_var="cat")
运行输出符合预期:
cat A B C D 2.5 3.0 6.0 6.0
注:你注释中写的A=5、B=6、C=12、D=12是分组求和的结果,如果需要求和把代码里的mean()替换为sum()即可。
如果不想用reformulate(),也可以手动拼接字符串后转为公式对象,效果完全一致:
tab_formula <- as.formula(paste(mean_val_by_var, "~", by_var))
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

