You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多模块Shiny应用生成Rmarkdown HTML/PDF报告的解决方案咨询

问题描述

我构建了一个包含四个模块的可运行Shiny应用:

  • 第一个模块实现CSV数据集上传功能
  • 第二、三个模块基于上传的数据绘制图表
  • 第四个模块需要关联Rmd文件生成分析报告

在普通Shiny应用中,可以直接把绘图的响应式函数写入report.Rmd来渲染HTML/PDF报告,但这种方法在模块化Shiny架构中无法直接生效。请问如何实现基于多个模块内容生成HTML或PDF格式的Rmarkdown报告?

解决方案

在模块化Shiny架构中,要让Rmarkdown报告获取各模块的内容,核心是让模块暴露需要共享的响应式对象,并在渲染报告时将这些对象作为参数传递给Rmd文件。具体步骤如下:

1. 修改绘图模块,返回响应式绘图对象

原来的绘图模块仅在内部生成绘图但未对外返回,导致报告模块无法获取。需要修改两个绘图模块的server函数,让它们返回各自的响应式绘图对象。

修改first_page_Server:

first_page_Server <- function(id, df) {
  stopifnot(is.reactive(df))
  moduleServer(
    id,
    function(input, output, session) {
      observeEvent(df(), {
        updateSelectInput(session,
                          inputId = "xcol", label = "X Variable",
                          choices = names(df()), selected = names(df())
        )
        updateSelectInput(session,
                          inputId = "ycol", label = "Y Variable",
                          choices = names(df()), selected = names(df())[2]
        )
      })
      
      # 定义响应式绘图对象
      graph_2 <- reactive({
        ggplot(df(), aes(.data[[input$xcol]], .data[[input$ycol]])) +
          geom_point()
      })
      
      output$MyPlot <- renderPlot({
        graph_2()
      })
      
      # 返回响应式绘图对象
      return(graph_2)
    }
  )
}

修改mod_ggplot_server:

mod_ggplot_server <- function(id, df){
  stopifnot(is.reactive(df))
  moduleServer( id, function(input, output, session){
    ns <- session$ns
    
    observeEvent(df(), {
      updateSelectInput(session,inputId = "xcol_1",label = "X Variable",choices = names(df()), selected = names(df())
      )
      updateSelectInput(session,inputId = "ycol_1",label = "y Variable",choices = names(df()), selected = names(df())[2])
    })
    
    # 定义响应式绘图对象
    graph <- reactive({
      ggplot(df(), aes(.data[[input$xcol_1]], .data[[input$ycol_1]])) +
        geom_point()
    })
    
    output$plot <- renderPlot({
      graph()
    })
    
    output$plotly <- renderPlotly({
      ggplotly(graph())
    })
    
    # 返回响应式绘图对象
    return(graph)
  })
}

2. 修改报告模块,接收外部参数

修改mod_Report_server,让它接收上传的数据、两个绘图模块返回的响应式对象,以及自身的输入参数,然后在渲染Rmd时将这些参数传递进去。

修改mod_Report_server:

mod_Report_server <- function(id, df, plot1, plot2){
  stopifnot(is.reactive(df), is.reactive(plot1), is.reactive(plot2))
  moduleServer( id, function(input, output, session){
    ns <- session$ns
    
    output$report <- downloadHandler(
      filename = function() {
        paste('My_report', Sys.Date(), sep = '.', switch(
          input$format, PDF = 'pdf', HTML = 'html', Word = 'docx'
        ))
      },
      
      content = function(file) {
        src <- normalizePath('report.Rmd')
        
        withProgress(message = '报告生成中',
                     detail = '请稍候...', value = 0, {
                       for (i in 1:10) {
                         incProgress(1/10)
                         Sys.sleep(0.1)
                       }
                     })
        
        owd <- setwd(tempdir())
        on.exit(setwd(owd))
        file.copy(src, 'report.Rmd', overwrite = TRUE)
        
        # 准备传递给Rmd的参数
        params <- list(
          title = input$title,
          author = input$author,
          data = df(),
          plot1 = plot1(),
          plot2 = plot2()
        )
        
        # 渲染报告,传递参数
        library(rmarkdown)
        out <- render('report.Rmd', 
                      output_format = switch(
                        input$format,
                        PDF = pdf_document(), 
                        HTML = html_document(), 
                        Word = word_document()
                      ),
                      params = params)
        file.rename(out, file)
      }
    )
  })
}

3. 修改主server函数,传递参数给报告模块

在主server中,接收绘图模块返回的对象,然后传递给报告模块:

server <- shinyServer(function(input, output, session) {
  
  upload_data <- file_upload_Server("upload_file")
  
  # 接收第一个绘图模块返回的绘图对象
  plot_obj1 <- first_page_Server("first_page", upload_data)
  
  # 接收第二个绘图模块返回的绘图对象
  plot_obj2 <- mod_ggplot_server("ggplot_1",upload_data)
  
  # 传递数据和绘图对象给报告模块
  mod_Report_server("Report_1", upload_data, plot_obj1, plot_obj2)
  
})

4. 修改Rmarkdown文件,使用传递的参数

原来的Rmd直接调用模块内的对象,现在改为使用params来获取传递过来的数据和绘图:

---
title: "`r params$title`"
author: "`r params$author`"
output: 
  pdf_document: default
  html_document: default
  word_document: default
params:
  title: "默认报告标题"
  author: "默认作者"
  data: NULL
  plot1: NULL
  plot2: NULL
---
knitr::opts_chunk$set(echo = TRUE)
library(ggplot2)

展示上传的数据

# 显示数据集
knitr::kable(params$data, caption = "上传的数据集")

展示第一个图表

# 第一个图表
params$plot1

展示第二个图表

# 第二个图表
params$plot2
关键说明
  • 模块化架构中,模块内部的响应式对象默认是隔离的,必须通过返回值的方式暴露给外部。
  • 使用rmarkdown::render的params参数,可以将Shiny中的响应式数据和对象传递给Rmd文件,避免直接依赖模块内部的变量。
  • 传递参数时,要确保获取响应式对象的当前值(比如df()而不是df),因为Rmd渲染时需要的是具体数据而非响应式对象本身。

内容的提问来源于stack exchange,提问作者EnriqueGG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 00:36:21