为啥 ggplotly 在 rmarkdown 中不能像 ggplot 一样工作

Posted

技术标签:

【中文标题】为啥 ggplotly 在 rmarkdown 中不能像 ggplot 一样工作【英文标题】:Why does ggplotly does not work in rmarkdown the same way ggplot does为什么 ggplotly 在 rmarkdown 中不能像 ggplot 一样工作 【发布时间】:2020-03-27 09:58:22 【问题描述】:

我想使用ggplotly,因为它的副作用与ggplot 甚至graphics 一样。我的意思是当我 knitr::knitrmarkdown::render 一个 Rmd 文档时,我希望 print(obj) 其中 obj 是一个 ggplotly 对象,但事实并非如此。

谁能解释这是怎么回事? 谁能告诉我如何实现我想做的事情。我希望能够在不返回对象的情况下将 ggplotly 图绘制到函数中(我想返回图的基础数据),并且我希望代码适用于 ggplot 和 ggplotly(即使用相同的代码ggplot 或 ggplotly)

question.R 文件

#+ libs, echo = FALSE                                                                                                                                                                                                                                        
suppressMessages(                                                                                                                                                                                                                             
    library(ggplot2)                                                                                                                                                                                                                           
    library(plotly)                                                                                                                                                                                                                            
    library(rmarkdown)                                                                                                                                                                                                                         
)                                                                                                                                                                                                                                             

#+ functions decl, echo = FALSE
df <- data.frame(x = 1:5, y = 1:5)                                                                                                                                                                                                                             
f_0 <- function(df)                                                                                                                                                                                                                                                                                                                                                                                                                                      
    p <- ggplot(df, aes(x, y)) + geom_line()
    # p or plot(p) or print(p) works                                                                                                                                                                                                   
    print(p)                                                                                                                                                                                                                                   
    return(df)                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                              
f_1 <- function(df)                                                                                                                                                                                                                                                                                                                                                                                                                                      
    p <- ggplot(df, aes(x, y)) + geom_line()                                                                                                                                                                                                   
    p <- ggplotly(p)    
    # plot(p) crashes                                                                                                                                                                                                                       
    # print p does not print in report                                                                                                                                                                                                         
    print(p)                                                                                                                                                                                                                                   
    # p standalone does not work either                                                                                                                                                                                                        
    p                                                                                                                                                                                                                                          
    return(df)                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                              

#' # plots                                                                                                                                                                                                                                     
#' plot 0                                                                                                                                                                                                                                      
#+ plot_0                                                                                                                                                                                                                                      
res_0 <- f_0(df)                                                                                                                                                                                                                                 
#' plot 1                                                                                                                                                                                                                                      
#+ plot_1                                                                                                                                                                                                                                      
res_1 <- f_1(df)     

渲染此文件

rmarkdown::render("question.R")

输出

【问题讨论】:

我认为这与情节输出是htmlwidgets有关。您可能需要使用 `htmltools::taglist()'。看看here 和here。 @joshpk 所以没有一致的API? @joshpk 我把那些页面变红了,但是很乱,我不知道该怎么做 你在渲染什么,HTML? 是的,我渲染到 html 【参考方案1】:

编辑评论:作为一种风格,将计算和绘图分离成不同的函数通常是一个好主意,因为它增加了模块化,使代码更易于维护,并允许在没有参数蠕变。然后可以轻松地将各个函数的输出映射到单独的 knitr 块。

最佳解决方案:我知道您特别询问不返回绘图对象,但我只想指出,将其与结果一起返回提供了最简洁、最优雅的解决方案:

---
output: html_document
---

```r include=FALSE
library( tidyverse )
df <- data_frame( x=1:5, y=1:5 )
```

```r
f <- function(df) 
  gg <- ggplot(df, aes(x,y)) + geom_point()
  list( result=df, plot=plotly::ggplotly(gg) )

res <- f(df)
res$plot
```

但是,如果你绝对不能从函数中返回一个 plotly 对象,你有一些选择。

选项 1: 将 plotly 对象存储到父框架,提供从 knitr 块对其的访问。

```r
f1 <- function(df) 
  gg <- ggplot(df, aes(x,y)) + geom_point()
  assign("ggp", plotly::ggplotly(gg), envir=parent.frame())
  df    # NOT returning a plot

res1 <- f1(df)
ggp   # Let knitr handle the rendering naturally
```

选项 2:将绘图渲染为临时 .html,然后将其作为 iframe 导入

```r, results='asis'     # <-- note the "asis" chunk option
f2 <- function(df)

  gg <- ggplot(df, aes(x,y)) + geom_point()
  htmlwidgets::saveWidget( plotly::ggplotly(gg), "temp.html")
  print( htmltools::tags$iframe(src="temp.html", width=640, height=480) )
  df    # NOT returning a plot

res2 <- f2(df)
```

解释:如果我错了,易辉可以纠正我,但knitr基本上是在幕后做“选项2”。它将诸如 plotly 对象之类的 htmlwidgets 渲染到临时 .html 文件中,然后将这些临时文件组合起来以生成最终文档。它在函数内失败的原因是当执行离开函数范围时临时文件被删除。 (您可以通过使用tempfile() 而不是持久的"temp.html" 自己复制此内容;iframe 对象将在最终文档中显示“找不到文件”错误。)可能有一种方法可以修改 knitr 钩子以保留临时文件,但这超出了我的知识范围。最后,基本的ggplot 对象没有这个问题的原因是它们的输出进入了绘图设备,该设备在调用帧中持续存在。

【讨论】:

正如我在问题中所说的,我想在函数中绘图而不返回绘图对象 @statquant:生成与print() 内联的绘图而不是在结果旁边返回绘图对象有什么好处?我扩展了帖子以显示一些替代方案,但 return(list( results, plot )) 是一个更清洁的选择,恕我直言。 你好,我明白你的意思,但我觉得这是一种浪费,因为 plot 对象已经包含数据,对于 ggplot 你可以获得 plot$data,但是对于 ggplotly,API 是不同的,我没有知道为什么。不管怎样,谢谢你的回答,我会关闭这个

以上是关于为啥 ggplotly 在 rmarkdown 中不能像 ggplot 一样工作的主要内容,如果未能解决你的问题,请参考以下文章

在 RMarkdown 中的 HTML 模板中渲染 ggplot2 绘图

在 Rmarkdown 中动态创建选项卡不适用于 ggplot,而它适用于 plotly

为啥我不需要 results = "asis" 在 Rmarkdown 中逐字嵌入块输出?

为啥 RMarkdown 中的“Knit to pdf”会引发错误?

将图像添加到要使用 knitr 生成的 flextable 失败但在 RMarkdown 块中有效

Facetgrid 图(使用 ggplotly)在 docker 容器中显示为灰色