为何knitr无法识别子文档中定义的ggplot2自定义转换函数?
RMarkdown父子文档编译问题:自定义ggplot转换函数找不到的原因与解决
问题背景
我使用RMarkdown父子文档结构,通过子文档的YAML配置实现点击子文档的RStudio「Knit」按钮时编译父文档。同目录下有两个文件:
parent.Rmd
--- title: "Test" output: pdf_document --- ```{r include=FALSE} knitr::opts_chunk$set(echo = FALSE) library(ggplot2)
### `child.Rmd` ```r --- knit: (\(input,...) rmarkdown::render('parent.Rmd',...)) editor_options: chunk_output_type: console --- ```{r} # Create simple plot data.frame( x = seq(.01,.99,length.out=100), y = seq(.01,.99,length.out=100) ) |> ggplot(aes(x=x,y=y)) + geom_point() -> gg1
source(textConnection( r"[ neglog_trans <- function(base=10){ trans <- \(x) -logb(x,base=base) inv <- \(x) base^(-x) scales::trans_new("neglog", transform = trans, inverse = inv) } ]" ))
neglog_trans <- function(base=10){ trans <- \(x) -logb(x,base=base) inv <- \(x) base^(-x) scales::trans_new("neglog", transform = trans, inverse = inv) }
# Create plot adding custom transformation in scales gg1 + scale_x_continuous( transform = "neglog" ) + scale_y_continuous( transform = "neglog" )
## 错误现象 点击子文档的「Knit」按钮时,出现以下错误:
Error in as.transform():
! Could not find any function named transform_neglog() orneglog_trans()
## 可正常编译的操作 - 不使用自定义转换函数; - 点击`parent.Rmd`的「Knit」按钮; - 将子文档中`source()`代码块设为`eval=TRUE`,函数定义块设为`eval=FALSE`后点击子文档「Knit」。 ## 无法编译的操作 - 直接点击`child.Rmd`的「Knit」按钮; - 在R控制台执行`(\(input,...) rmarkdown::render('parent.Rmd',...))()`; - 用`callr::r()`在独立进程执行上述渲染代码。 ## 环境信息 ```r > xfun::session_info() R version 4.4.2 (2024-10-31) Platform: aarch64-apple-darwin20 Running under: macOS Sequoia 15.5 Locale: en_US.UTF-8 / en_US.UTF-8 / en_US.UTF-8 / C / en_US.UTF-8 / en_US.UTF-8 Package version: base64enc_0.1.3 bslib_0.9.0 cachem_1.1.0 callr_3.7.6 cli_3.6.5 compiler_4.4.2 digest_0.6.37 dplyr_1.1.4 evaluate_1.0.3 farver_2.1.2 fastmap_1.2.0 fontawesome_0.5.3 fs_1.6.6 generics_0.1.4 ggplot2_3.5.2 glue_1.8.0 graphics_4.4.2 grDevices_4.4.2 grid_4.4.2 gtable_0.3.6 here_1.0.1 highr_0.11 htmltools_0.5.8.1 isoband_0.2.7 jquerylib_0.1.4 jsonlite_2.0.0 knitr_1.50 labeling_0.4.3 lattice_0.22.7 lifecycle_1.0.4 magrittr_2.0.3 MASS_7.3.65 Matrix_1.7.3 memoise_2.0.1 methods_4.4.2 mgcv_1.9.3 mime_0.13 nlme_3.1.168 pillar_1.10.2 pkgconfig_2.0.3 processx_3.8.6 ps_1.9.1 R6_2.6.1 rappdirs_0.3.3 RColorBrewer_1.1-3 renv_1.1.4 rlang_1.1.6 rmarkdown_2.29 rprojroot_2.0.4 sass_0.4.10 scales_1.4.0 splines_4.4.2 stats_4.4.2 tibble_3.3.0 tidyselect_1.2.1 tinytex_0.57 tools_4.4.2 utf8_1.2.6 utils_4.4.2 vctrs_0.6.5 viridisLite_0.4.2 withr_3.0.2 xfun_0.52 yaml_2.3.10
> xfun::session_info('knitr') R version 4.4.2 (2024-10-31) Platform: aarch64-apple-darwin20 Running under: macOS Sequoia 15.5 Locale: en_US.UTF-8 / en_US.UTF-8 / en_US.UTF-8 / C / en_US.UTF-8 / en_US.UTF-8 Package version: evaluate_1.0.3 graphics_4.4.2 grDevices_4.4.2 highr_0.11 knitr_1.50 methods_4.4.2 stats_4.4.2 tools_4.4.2 utils_4.4.2 xfun_0.52 yaml_2.3.10
> xfun::session_info('rmarkdown') R version 4.4.2 (2024-10-31) Platform: aarch64-apple-darwin20 Running under: macOS Sequoia 15.5 Locale: en_US.UTF-8 / en_US.UTF-8 / en_US.UTF-8 / C / en_US.UTF-8 / en_US.UTF-8 Package version: base64enc_0.1.3 bslib_0.9.0 cachem_1.1.0 cli_3.6.5 digest_0.6.37 evaluate_1.0.3 fastmap_1.2.0 fontawesome_0.5.3 fs_1.6.6 glue_1.8.0 graphics_4.4.2 grDevices_4.4.2 highr_0.11 htmltools_0.5.8.1 jquerylib_0.1.4 jsonlite_2.0.0 knitr_1.50 lifecycle_1.0.4 memoise_2.0.1 methods_4.4.2 mime_0.13 R6_2.6.1 rappdirs_0.3.3 rlang_1.1.6 rmarkdown_2.29 sass_0.4.10 stats_4.4.2 tinytex_0.57 tools_4.4.2 utils_4.4.2 xfun_0.52 yaml_2.3.10 Pandoc version: 3.4
原因分析
问题核心在于函数作用域与ggplot的transform参数查找逻辑:
- 当点击子文档「Knit」时,子文档YAML中的knit函数会调用
rmarkdown::render('parent.Rmd'),此时父文档的渲染环境是该匿名函数的局部环境,而非全局环境; - ggplot的
scale_*_continuous参数transform="neglog"会触发scales::as.transform(),该函数会在全局环境中查找neglog_trans或transform_neglog函数; - 子文档中直接定义的
neglog_trans函数存在于父文档的局部渲染环境中,无法被全局环境的查找逻辑定位到; - 点击父文档「Knit」时,渲染环境为全局环境,函数定义直接存在于全局,因此能被找到;而
source(textConnection(...))会默认在全局环境执行代码,所以也能正常工作。
解决方法
方法1:直接传入转换对象而非字符串
修改子文档中绘制图形的代码块,将transform="neglog"改为直接传入neglog_trans()生成的转换对象,跳过全局查找:
```{r} # Create plot adding custom transformation in scales gg1 + scale_x_continuous( transform = neglog_trans() ) + scale_y_continuous( transform = neglog_trans() )
### 方法2:将函数定义到全局环境 在子文档的函数定义块中,将函数显式赋值到全局环境: ```r ```{r eval=TRUE} neglog_trans <- function(base=10){ trans <- \(x) -logb(x,base=base) inv <- \(x) base^(-x) scales::trans_new("neglog", transform = trans, inverse = inv) } # 将函数赋值到全局环境 assign("neglog_trans", neglog_trans, envir = .GlobalEnv)
### 方法3:在父文档中定义函数 将`neglog_trans`函数的定义移到父文档的代码块中,确保无论从父还是子文档触发编译,函数都存在于渲染环境中: ```r # parent.Rmd中的include=FALSE代码块修改为: ```{r include=FALSE} knitr::opts_chunk$set(echo = FALSE) library(ggplot2) neglog_trans <- function(base=10){ trans <- \(x) -logb(x,base=base) inv <- \(x) base^(-x) scales::trans_new("neglog", transform = trans, inverse = inv) }
内容的提问来源于stack exchange,提问作者richarddmorey
相关产品推荐
相关产品推荐

