如何将 Shiny 应用程序中的表格和绘图作为参数传递给 R Markdown?

Posted

技术标签:

【中文标题】如何将 Shiny 应用程序中的表格和绘图作为参数传递给 R Markdown?【英文标题】:How to pass table and plot in Shiny app as parameters to R Markdown? 【发布时间】:2020-01-08 03:55:55 【问题描述】:

在这款 Shiny 应用中,用户可以上传 .csv 文件,以表格形式获取结果并进行绘图。我希望能够将结果下载为 PDF 文档。

输入文件

#I created the input .csv file to be used in the app from diamonds data.frame
library(ggplot2)
df <-  diamonds[1:5000, ]
head(df)
write.csv(df, "df.csv")

应用程序

library(tidyverse)
library(shiny)
library(rmarkdown)
library(knitr)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(fileInput("file","Upload your file"), 
                 width =2),
    mainPanel(
      width = 10,
      downloadButton("report", "Download report"),
      tableOutput("table"),
      tags$br(),
      tags$hr(),
      plotOutput("plot1"), 
      tags$br(),
      tags$hr(),
      plotOutput("plot2")
    )
  )
)

server <- function(input,output)

  data <- reactive(
    file1 <- input$file
    if(is.null(file1))return() 
    read.csv(file1$datapath, header=TRUE, sep=',')
    )


  output$table <- renderTable(
    if (is.null(data()))  return() 

      df <- data() %>% 
      dplyr::select(cut, color, price) %>% 
      dplyr::group_by(cut, color) %>% 
      dplyr::summarise_all(funs(min(.), mean(.), median(.),max(.),sd(.), n() ))  
  )  

  table_rmd <- reactive(
    df <- data() %>% 
      dplyr::select(cut, color, price) %>% 
      dplyr::group_by(cut, color) %>% 
      dplyr::summarise_all(funs(min(.), mean(.), median(.),max(.),sd(.), n() )) 
  )

  output$plot1 <- renderPlot(
    if (is.null(data()))  return() 

    ggplot(data(), aes (x =carat, y = price, col = color))+
      geom_point()+
      facet_wrap(~cut)
    
  )

  plot_rmd <- reactive(
   chart <- ggplot(data(), aes (x =carat, y = price, col = color))+
      geom_point()+
      facet_wrap(~cut)
   chart
  
  )

    #https://shiny.rstudio.com/articles/generating-reports.html
    output$report <- downloadHandler(
      filename = "report.pdf",
      content = function(file) 
        tempReport <- file.path(tempdir(), "report.Rmd")
        file.copy("report.Rmd", tempReport, overwrite = TRUE)

        params <- list(table1 = table_rmd(),
                       plot1 = plot_rmd())

        rmarkdown::render(tempReport, output_file = file,
                          params = params,
                          envir = new.env(parent = globalenv())
        )
      
    )
  


shinyApp(ui=ui, server = server)

report.Rmd

---
title: "Dynamic report"
output: pdf_document

params:
  table1: NA
  plot1: NA

---


This is the firs plot 

```r
params$plot1
```

This is the first table

```r
kable(params$table1)
```

我尝试了不同的方法将表格和绘图从 Shiny 作为参数传递给 R Markdown,但没有一个奏效。

非常感谢您提出解决此问题的建议。

更新

我已经尝试过@BigDataScientist 的回答,但我得到了这个错误

"C:/Program Files/RStudio/bin/pandoc/pandoc" +RTS -K512m -RTS report.utf8.md --to latex --from markdown+autolink_bare_uris+ascii_identifiers+tex_math_single_backslash --output pandoc20e043232760.tex - -template "C:\PROGRA~1\R\R-35~1.2\library\RMARKD~1\rmd\latex\DEFAUL~3.TEX" --highlight-style tango --pdf-engine pdflatex --可变图形=是--变量“几何:边距= 1in”--变量“紧凑标题:是” 警告:错误:无法编译 C:\Users\user\AppData\Local\Temp\RtmpYvWn8M\file20e042326267.tex。有关调试提示,请参阅https://yihui.name/tinytex/r/#debugging。 [没有可用的堆栈跟踪]

这里是sessionInfo()

> sessionInfo()
R version 3.5.2 (2018-12-20)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

Matrix products: default

locale:
[1] LC_COLLATE=English_New Zealand.1252  LC_CTYPE=English_New Zealand.1252    LC_MONETARY=English_New Zealand.1252 LC_NUMERIC=C                        
[5] LC_TIME=English_New Zealand.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] bindrcpp_0.2.2  forcats_0.3.0   stringr_1.4.0   dplyr_0.7.8     purrr_0.2.5     readr_1.3.1     tidyr_0.8.2     tibble_2.0.1    tidyverse_1.2.1 ggplot2_3.1.0  
[11] shiny_1.2.0    

loaded via a namespace (and not attached):
 [1] tinytex_0.15.2   tidyselect_0.2.5 xfun_0.9         haven_2.0.0      lattice_0.20-38  colorspace_1.4-0 generics_0.0.2   htmltools_0.3.6  yaml_2.2.0      
[10] utf8_1.1.4       rlang_0.4.0      later_0.8.0      pillar_1.3.1     glue_1.3.0       withr_2.1.2      readxl_1.2.0     modelr_0.1.2     bindr_0.1.1     
[19] plyr_1.8.4       cellranger_1.1.0 munsell_0.5.0    gtable_0.2.0     rvest_0.3.2      evaluate_0.12    labeling_0.3     knitr_1.21       httpuv_1.4.5.1  
[28] fansi_0.4.0      broom_0.5.1      Rcpp_1.0.0       xtable_1.8-3     promises_1.0.1   scales_1.0.0     backports_1.1.3  jsonlite_1.6     mime_0.6        
[37] hms_0.4.2        digest_0.6.18    stringi_1.2.4    grid_3.5.2       cli_1.0.1        tools_3.5.2      magrittr_1.5     lazyeval_0.2.1   crayon_1.3.4    
[46] pkgconfig_2.0.2  xml2_1.2.0       rsconnect_0.8.13 lubridate_1.7.4  assertthat_0.2.0 rmarkdown_1.11   httr_1.4.0       rstudioapi_0.9.0 R6_2.3.0        
[55] nlme_3.1-137     compiler_3.5.2 

【问题讨论】:

【参考方案1】:

您可以将 R 对象作为params 列表的一部分传递给参数化的Rmd。这是常规交互式会话中的示例(无闪亮)。 report.Rmd 与问题中的相同。 dfpl 由一些 R 脚本生成,可在您的环境中使用。然后您可以将它们包装在list 中并将它们作为params 传递给render

library(rmarkdown)
library(ggplot2)

df <- head(iris)
pl <- ggplot(iris, aes(x = Sepal.Width)) + geom_histogram(color = "white")

render(
  input = "report.Rmd",
  params = list("table1" = df, "plot1" = pl),
  output_file = "rendered-from-session.pdf"
)

rendered-from-session.pdf的截图

这是否是一个好的策略是另一个问题。如果制作表格和绘图的代码对于appRmd 是相同的,那么最好将其放在一个由appRmd 提供的单独脚本中。这样,当您想要更改表格/绘图代码时,您只需要编辑一个文档。在这种情况下,您会将参数传递给绘图函数,而不是绘图本身。这样做的缺点是,如果您有许多不同的图,需要许多不同的参数来跟踪。或者如果计算/绘图需要很长时间,所以你不想做两次。

回到您的 Shiny 应用。事实上,您的应用程序代码适用,就像通过 Shiny 一样。我可以制作 pdf(即使 table_rmd() 反应式没有明确返回 df)。所以很可能是 pandoc 或 latex 没有弄清楚临时文件/文件夹在哪里的问题。由于您仍在测试,我会尝试保存到已知位置而不是 tempdir,以查看这是否不是某种权限问题。

您可以像这样修改您的处理程序。删除对tempdir 的调用,并将report.Rmd 的完整路径提供给render。您也可以尝试为output_file 提供完整路径。第一种情况应该可以正常工作。在第二种情况下("PATH/TO/OUTPUT" 而不是 file 传递给 output_file),浏览器可能会给您一个 download 错误,但 pdf 仍应使用您提供的文件名在后台呈现。

  output$report <- downloadHandler(
    filename = "report.pdf",
    content = function(file) 
      # tempReport <- file.path(tempdir(), "report.Rmd")
      # file.copy("report.Rmd", tempReport, overwrite = TRUE)

      params <- list(table1 = table_rmd(),
                     plot1 = plot_rmd())

      rmarkdown::render(input = "PATH/TO/report.Rmd", 
                        output_file = file,
                        params = params,
                        envir = new.env(parent = globalenv())
      )
    
  )

这至少可以确认您的代码正在运行,tempdir() 除外。如果您有选择,请在 linux 机器或 mac 上试用您的应用程序。

【讨论】:

非常感谢您的时间和帮助。我尝试渲染pdf(没有Shiny)并且效果很好。但是,将临时目录更改为完整路径仍会导致与问题更新中所示相同的错误。 该应用在另一台 Linux 机器上运行。我没有Windows机器来测试它。我看到您使用的是pdflatex。你可以试试其他引擎,也许是xelatexlualatex?但这似乎不太可能是原因,因为您可以从交互式会话中进行渲染。那tempdir 的内容呢?它有正确扩展名的文件吗?最后,正如这里所讨论的:yihui.name/tinytex/r/#debugging,您可以将此options(tinytex.verbose = TRUE) 添加到您的report.Rmd 中的代码块中,以查看完整日志中的乳胶错误。 谢谢。我只是简单地卸载了TinyTex 并安装了MiKTex,现在一切正常。【参考方案2】:

不确定是否可以将表格和绘图作为参数传入/传给 rmarkdown。 (也请在此处考虑 teofil 的回答)。

所有可以被 yaml::yaml.load() 解析的标准 R 类型都可以 包含为参数,包括字符、数字、整数和 逻辑类型。

来源:https://bookdown.org/yihui/rmarkdown/params-declare.html

所以你有两个选择。

将相关数据作为参数传递,并将生成绘图/表格的代码包含在降价模板中(此处为:report.Rmd)。 如果出于某种原因,有人坚持在闪亮的一面定义绘图/表格的代码,您可以将此代码作为字符发送并使用 eval(parse(text = params$...)) 在 rmarkdown 一面对其进行评估。

我可以想象你不想在闪亮 + rmarkdown 中两次指定绘图/表格的代码,但我想你必须在这两个选项之间进行选择,第一个可能更干净。 (但如果其他人有其他想法,请随时将问题留空)。

可重复的示例:(包括两个选项的示例 - 基于您的代码)

report.Rmd

---
title: "Dynamic report"
output: pdf_document
params:
  plotData: NA
  tableData: NA
  plotCode: NA
---

```r
params$tableData
```

```r
eval(parse(text = params$plotCode))
```


```r
library(ggplot2)
ggplot(params$plotData, aes (x = carat, y = price, col = color)) +
  geom_point() +
  facet_wrap(~cut)
```

app.R

library(shiny)
library(ggplot2)

df <-  diamonds[1:5000, ]
head(df)
write.csv(df, "df.csv")

#setwd("....") #be sure to be in same directory
shinyApp(
  ui = fluidPage(
    sliderInput(inputId = "slider", label = "Slider", min = 1, max = 100, value = 50),
    fileInput(inputId = "file", label = "Upload your file"),
    downloadButton(outputId = "report", label = "Generate report")
  ),
  server = function(input, output) 
    data <- reactive(
      file1 <- input$file
      if(is.null(file1))return()
      read.csv(file1$datapath, header = TRUE, sep = ',')
    )

    table_rmd <- reactive(
      data() %>% 
        dplyr::select(cut, color, price) %>% 
        dplyr::group_by(cut, color) %>% 
        dplyr::summarise_all(funs(min(.), mean(.), median(.),max(.),sd(.), n() )) 
    )


    output$report <- downloadHandler(
      filename = "report.pdf",
      content = function(file) 

        tempReport <- file.path(tempdir(), "report.Rmd")
        file.copy("report.Rmd", tempReport, overwrite = TRUE)

        params <- list(plotData = data(), tableData = table_rmd(), plotCode = "plot(1)")

        rmarkdown::render(tempReport, output_file = file,
                          params = params,
                          envir = new.env(parent = globalenv())
        )
      
    )
  
)

【讨论】:

感谢您的时间和帮助。我收到一条错误消息。我将添加为更新,因为评论太长了 hmm 再次测试并在 ubuntu 上为我工作。无法发现某事。从错误消息中。我假设您可以将“正常” .Rmd 渲染为 pdf 文档? 我通常将 .Rmd 渲染为 pdf 没有问题。我不明白是什么导致了这个错误。 好的,我会看看我是否可以在一台带有 pandoc 的 Windows 机器上进行测试。

以上是关于如何将 Shiny 应用程序中的表格和绘图作为参数传递给 R Markdown?的主要内容,如果未能解决你的问题,请参考以下文章

如何将更新的列名从 HandsOnTable 传递到 Shiny 中的绘图?

在 R 中的 Shiny 中部署 dataTableOutput 和绘图; “整理” dataTableOutput

将反应输入传递到 R Shiny 中的绘图轴

将 R Shiny Page 导出为 PDF

使用基于 selectInput 的动态参数将绘图从 rmarkdown 复制到 Shiny 的问题

R/Shiny App 将绘图写入 RStudio 中的绘图视图而不是 Shiny UI