在函数中使用flextable的merge_at、hline等函数遇样式异常问题
问题:flextable封装函数后样式仅应用到最后一行条目
尝试程序化设置flextable表格样式时,使用merge_at()、hline()等函数遇到异常:无论指定哪一行,样式仅会应用到目标列的最后一个条目。直接循环处理的iris_a样式符合预期,但将循环封装成函数后的iris_b出现异常,无论location参数设置为何值,结果都一致。
示例代码
# setup library(tidyverse) library(officer) library(flextable) thin_border <- fp_border(color = "gray") setosa <- iris %>% group_split(Species) %>% .[[1]] %>% slice(1:2) versicolor <- iris %>% group_split(Species) %>% .[[2]] %>% slice(1:2) iris_a <- bind_rows(setosa, versicolor) %>% mutate(spec = case_when( Species == "setosa" ~ "set", Species == "versicolor" ~ "versi" )) %>% flextable() iris_b <- iris_a specs <- c("setosa", "versicolor") # actual transformation for (k in specs) { iris_a<- iris_a%>% hline(., i = ~ Species == k, j = 1:5, border = thin_border) } for(k in specs){ iris_a <- iris_a %>% merge_at(., i = ~ Species == k, j = ~spec) } iris_a %>% fix_border_issues() # success, a nice table; now I wrap the loops into functions merge_cells <- function(flex, location){ for(k in location){ flex <- flex %>% merge_at(., i = ~ Species == k, j = ~spec) } flex } add_vlines <- function(flex, location){ for (k in location) { flex <- flex %>% hline(., i = ~ Species == k, j = 1:5, border = thin_border) } flex } iris_b <- iris_b %>% add_vlines(., "set") %>% merge_cells(., "set") iris_b %>% fix_border_issues()
错误原因:公式的延迟求值问题
- flextable中使用公式(如
~ Species == k)时,公式的评估是延迟的:函数执行完毕返回flextable对象后,才会实际评估公式中的条件。 - 函数中的局部变量
k在函数执行结束后会被销毁,若为循环场景,k会保留最后一次循环的值。此时flextable评估公式时,无法正确获取k的预期值,导致所有样式操作都指向最后一次循环匹配的行(或根本匹配不到行)。 - 额外问题:调用函数时传入的
"set"是spec列的值,但函数中用的是Species == k,而Species列的值是"setosa"/"versicolor",条件完全不匹配,这也会导致样式无法正确应用。
解决方案:改用行索引而非公式,或动态构造公式
方案1:直接计算行索引(推荐)
在函数中,通过表格的数据集直接计算符合条件的行号,避免使用延迟求值的公式:
merge_cells <- function(flex, location){ for(k in location){ # 直接获取匹配Species的行索引 target_rows <- which(flex$dataset$Species == k) flex <- flex %>% merge_at(i = target_rows, j = ~spec) } flex } add_vlines <- function(flex, location){ for (k in location) { target_rows <- which(flex$dataset$Species == k) flex <- flex %>% hline(i = target_rows, j = 1:5, border = thin_border) } flex } # 调用时传入Species列的取值,而非spec列的值 iris_b <- iris_b %>% add_vlines("setosa") %>% merge_cells("setosa") iris_b %>% fix_border_issues()
方案2:动态构造公式
如果坚持使用公式,可以用rlang包动态构造包含k实际值的公式,避免延迟求值问题:
library(rlang) merge_cells <- function(flex, location){ for(k in location){ # 动态构造公式:Species == "k的实际值" cond_expr <- expr(Species == !!k) flex <- flex %>% merge_at(i = cond_expr, j = ~spec) } flex } add_vlines <- function(flex, location){ for (k in location) { cond_expr <- expr(Species == !!k) flex <- flex %>% hline(i = cond_expr, j = 1:5, border = thin_border) } flex } # 同样传入Species列的取值 iris_b <- iris_b %>% add_vlines("setosa") %>% merge_cells("setosa") iris_b %>% fix_border_issues()
内容的提问来源于stack exchange,提问作者setseed13
相关产品推荐
相关产品推荐

