为什么 ggplotly 在 rmarkdown 中的工作方式与 ggplot 不同

Why does ggplotly does not work in rmarkdown the same way ggplot does

我想使用 ggplotly 因为它的副作用与 ggplot 甚至 graphics 相同。我的意思是,当我 knitr::knitrmarkdown::render 一个 Rmd 文档时,我希望 print(obj) 其中 obj 是一个 ggplotly objcet 在报告中,而不是案例.

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")

输出

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

最佳解决方案: 我知道您特别询问不 return 绘图对象,但我只想指出 return将其与结果一起使用可提供最干净、最优雅的解决方案:

---
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
```

但是,如果您绝对不能 return 从函数中绘制对象,您还有其他选择。

选项 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 基本上是在幕后"Option 2"。它将 html 小部件(例如 plotly 对象)呈现为临时 .html 文件,然后组合这些临时文件以生成最终文档。它在函数内部失败的原因是当执行离开函数范围时临时文件被删除。 (您可以通过使用 tempfile() 而不是持久的 "temp.html" 来自己复制它;iframe 对象将在最终文档中显示 "file not found" 错误。)可能有一种方法可以修改 knitr hooks 以保留临时文件,但这超出了我的知识范围。最后,基本 ggplot 对象没有这个问题的原因是它们的输出进入绘图设备,该设备在调用帧中持续存在。