在 Shiny 中使用 checkboxGroupInput 以 plot_grid 从一个 tibble 中制作可选择的图

Use checkboxGroupInput in Shiny to make selectable plots from one tibble with plot_grid

我正在尝试制作一个 Shiny 应用程序,该应用程序将采用上传的 CSV 文件并将其转换为 tibble,然后制作一系列具有相同 X 但对 Y 数据使用不同列的图,每个图一个。我希望用户能够使用复选框 select 他们想要显示哪些图并使用 plot_grid.

绘制结果

到目前为止,我设法让脚本按照我想要的方式渲染绘图,如果我手动命名它们,则可以从 plot_grid 即时绘制它们。我在将 checkboxGroupInput 输出作为 plot_grid 的输入时遇到问题,返回的特征向量不能用作 grob 对象。这是相关代码:

ui <- fluidPage(
titlePanel("Title"),
sidebarLayout(
    sidebarPanel(
                 #Select which plots will be displayed
                 checkboxGroupInput(inputId = "whichPlot",
                                    label = "Select data to plot",
                                    choices = c("Temperature" = "tempChart()",
                                                "Pressure" = "pressureChart()",
                                                "Dissolved Oxygen" = "airsat()",
                                                "pH" = "phChart()",
                                                "Air flow" = "airChart()",
                                                "Oxygen flow" = "O2Chart()"),
                                   selected = "Temperature"),
                 #Select time scale
                 selectInput("timeScale",
                              "Choose time scale to plot",
                              choices = c("Minutes",
                                          "Hours",
                                          "Days"),
                             selected = "Minutes"),
                 #Apply button to make graphs
                 actionButton("DoIt", "Plot data"),
                 br(),
                
    ),
                
    mainPanel( 
              plotOutput("plot"),
#the outputs below are not necessary, I just use them to see if I'm going in the right direction
              textOutput("tableTitle"),
              tableOutput("table"),
              textOutput("selection"),
              tableOutput("dataSummary")
              
              
              ) 
    )
)   

server <- function(input, output) {

    #A dummy tibble that I use for testing. will be replaced by Load button
   dataDF <- tibble(date = c("01/01/2021","01/01/2021","01/01/2021","01/01/2021"), time = c("10:51:02","10:52:02","10:53:02","10:54:02"),
                temp = c(20.000,22.000,23.000,24.000), pressure = c(50.000,50.000,50.000,50.000),
                pH = c(7.000, 7.230, 7.100, 7.040), pO2 = c(100.000, 90.000, 80.000, 70.000),
                airflow = c(1.000,1.000,1.000,1.000), O2flow = c(1.000,1.000,1.000,1.000), user = c("user","user","user","user"),
                level = c(3,3,3,3), acid = c("00:00:00","00:00:00","00:00:00","00:00:00"), base =c("00:00:00","00:00:00","00:00:00","00:00:00"),
                mins = c(0,2,3,4), hrs = c(0,60,180,360), dys = c(0,15,25,35))
   output$table <- renderTable(head(dataDF))

   #This changes the X axis scale and works well
        colsel <- reactive({
    switch(input$timeScale,
                      "Minutes" = 13,
                      "Hours" = 14,
                      "Days" = 15)
        })
           dataT <- reactive({
              df <-dataDF[, 3:8]
              df$runTime = pull(dataDF, colsel())
              df
           })


     #A control table output to make sure tibble transformation worked (it works!)
output$dataSummary <- renderTable({
                    head(dataT())})

     #Create a list of plots to be drawn from the checkboxes
plots <- reactive({
        paste(input$whichPlot, sep = ",")
     })

#save all the plots to individual objects to be chosen from later
  
     airChart <- reactive({
        ggplot(dataT(), aes(x = runTime, y = airflow))+
             geom_line(size = 1, color = "#00B388")+
             labs(x = paste("Runtime in",input$timeScale), y = "Air flow [L/min]")+
             scale_x_continuous(breaks = breaks_extended(n = 10))+
             ylim(0, 1)
     })

     tempChart <- reactive({
        ggplot (dataT(), aes(runTime, temp))+
             geom_line(size = 1, color = "#00B388")+
             labs(x = paste("Runtime in",input$timeScale), y = expression(paste("Temperature [", degree, "C]")))+
             scale_x_continuous(breaks = breaks_extended(n = 10))+
             ylim(15, 45)
     })

     airsat <- reactive({
        ggplot(dataT(), aes(runTime, pO2))+
             geom_line(size = 1, color = "#00B388")+
             labs(x = paste("Runtime in",input$timeScale), y = "%O2 saturation")+
             scale_x_continuous(breaks = breaks_extended(n = 10))+
             ylim(80, 100)
     })


     phChart <- reactive({
        ggplot(dataT(), aes(runTime, pH))+
             geom_line(size = 1, color = "#00B388")+
             labs(x = paste("Runtime in",input$timeScale), y = "pH")+
             scale_x_continuous(breaks = breaks_extended(n = 10))+
             ylim(1,15)
     })


     O2Chart <- reactive({
        ggplot(dataT(), aes(runTime, O2flow))+
             geom_line(size = 1, color = "#00B388")+
             labs(x = paste("Runtime in",input$timeScale), y = "O2 flow [L/min]")+
             scale_x_continuous(breaks = breaks_extended(n = 10))+
             ylim(0,10)
     })


     pressureChart <- reactive({
        ggplot(dataT(), aes(runTime, pressure))+
             geom_line(size = 1, color = "#00B388")+
             labs(x = paste("Runtime in",input$timeScale), y = "Pressure [mbar]")+
             scale_x_continuous(breaks = breaks_extended(n = 10))+
             ylim(0,220)
     })

#Plot my charts
observeEvent(input$DoIt,{
       output$plot <- renderPlot({
               (plot_grid(plots(), ncol = 2, labels = "auto"))
})

})  
}
shinyApp(ui = ui, server = server)

当我尝试使用它时,出现错误

Warning in as_grob.default(plot) : Cannot convert object of class character into a grob. Warning in grid.echo.recordedplot(dl, newpage, prefix) : No graphics to replay

如果我将最后一行替换为

output$plot <- renderPlot({
               (plot_grid(tempChart(), airsat(), O2Chart(), pressureChart(), ncol = 2, labels = "auto"))

它工作得很好。我不确定是否有办法绕过角色来解决问题,或者我是否让它变得不必要地太困难了。我用 if (我不完全理解)查看了其他解决方案,但我认为他们不会在这里提供帮助。第一次接触Shiny,请不要太苛刻

检查这是否有帮助

library(shiny)
library(tidyverse)

ui <- fluidPage(
  checkboxGroupInput("grp", "Select", choices = NULL),
  plotOutput("plot")
)

server <- function(input, output, session) {
  
  tibble(x = 1:10,
         y1 = sample(1:10),
         y2 = sample(1:10),
         y3 = sample(1:10),
         y4 = sample(1:10))  %>% 
    pivot_longer(-x) -> df

  observe({
    updateCheckboxGroupInput(session, "grp", "Select",
                             choices = unique(df$name),
                             selected = unique(df$name)[1])
  })
  
  output$plot <- renderPlot({
    df %>% 
      filter(name == req(input$grp)) %>% 
      ggplot(aes(x, value)) +
      geom_col() +
      facet_wrap(~name, ncol = 1)
  })
}

shinyApp(ui, server)

我会在这里使用不同的策略。您可以将它们全部存储在一个列表中,而不是将每个图单独存储在 reactive 中。在这里,我使用了一个 reactiveValues 对象,该对象通过 observeEvent 进行更新。 (原则上,您甚至可以使用一个简单的列表来存储绘图,因为在您的情况下,反应性来自 observeEvent。使用 reactiveValues 允许您在 cowplot 之外使用具有反应性的单个绘图。)

然后您可以使用 input$whichPlot 来索引地块列表。此外,将 output$plot <- renderPlot 放在 observeEvent 中通常不被认为是好的做法,因为 renderPlot 本身已经具有反应性。

为了仅在按下 input$DoIt 时更新剧情,我使用了全新 shiny 1.6.0.

中的 bindEvent
library(shiny)
library(cowplot)
library(ggplot2)
library(scales)
library(dplyr)

ui <- fluidPage(
  titlePanel("Title"),
  sidebarLayout(
    sidebarPanel(
      #Select which plots will be displayed
      checkboxGroupInput(inputId = "whichPlot",
                         label = "Select data to plot",
                         choices = c("Temperature" = "temperature",
                                     "Pressure" = "pressure",
                                     "Dissolved Oxygen" = "dissolved_oxygen",
                                     "pH" = "ph",
                                     "Air flow" = "air_flow",
                                     "Oxygen flow" = "oxygen_flow"),
                         selected = "Temperature"),
      #Select time scale
      selectInput("timeScale",
                  "Choose time scale to plot",
                  choices = c("Minutes",
                              "Hours",
                              "Days"),
                  selected = "Minutes"),
      #Apply button to make graphs
      actionButton("DoIt", "Plot data"),
      br(),
      
    ),
    
    mainPanel( 
      plotOutput("plot"),
      #the outputs below are not necessary, I just use them to see if I'm going in the right direction
      textOutput("tableTitle"),
      tableOutput("table"),
      textOutput("selection"),
      tableOutput("dataSummary")
      
      
    ) 
  )
)   

server <- function(input, output) {
  
  #A dummy tibble that I use for testing. will be replaced by Load button
  dataDF <- tibble(date = c("01/01/2021","01/01/2021","01/01/2021","01/01/2021"), time = c("10:51:02","10:52:02","10:53:02","10:54:02"),
                   temp = c(20.000,22.000,23.000,24.000), pressure = c(50.000,50.000,50.000,50.000),
                   pH = c(7.000, 7.230, 7.100, 7.040), pO2 = c(100.000, 90.000, 80.000, 70.000),
                   airflow = c(1.000,1.000,1.000,1.000), O2flow = c(1.000,1.000,1.000,1.000), user = c("user","user","user","user"),
                   level = c(3,3,3,3), acid = c("00:00:00","00:00:00","00:00:00","00:00:00"), base =c("00:00:00","00:00:00","00:00:00","00:00:00"),
                   mins = c(0,2,3,4), hrs = c(0,60,180,360), dys = c(0,15,25,35))
  output$table <- renderTable(head(dataDF))
  
  #This changes the X axis scale and works well
  colsel <- reactive({
    switch(input$timeScale,
           "Minutes" = 13,
           "Hours" = 14,
           "Days" = 15)
  })
  dataT <- reactive({
    df <-dataDF[, 3:8]
    df$runTime = pull(dataDF, colsel())
    df
  })
  
  
  #A control table output to make sure tibble transformation worked (it works!)
  output$dataSummary <- renderTable({
    head(dataT())})
  
  # initialise reactiveValues object
  plots <- reactiveValues(
    temperature = NULL,
    pressure = NULL,
    dissolved_oxygen = NULL,
    ph = NULL,
    air_flow = NULL,
    oxygen_flow = NULL
  )
  
  # the plots only change when dataT or input$timeScale changes
  observeEvent(c(dataT(), input$timeScale), {
    plots$temperature <- ggplot (dataT(), aes(runTime, temp))+
      geom_line(size = 1, color = "#00B388")+
      labs(x = paste("Runtime in",input$timeScale), y = expression(paste("Temperature [", degree, "C]")))+
      scale_x_continuous(breaks = breaks_extended(n = 10))+
      ylim(15, 45)
    
    plots$pressure <- ggplot(dataT(), aes(runTime, pressure))+
      geom_line(size = 1, color = "#00B388")+
      labs(x = paste("Runtime in",input$timeScale), y = "Pressure [mbar]")+
      scale_x_continuous(breaks = breaks_extended(n = 10))+
      ylim(0,220)
    
    plots$dissolved_oxygen <- ggplot(dataT(), aes(runTime, pO2))+
      geom_line(size = 1, color = "#00B388")+
      labs(x = paste("Runtime in",input$timeScale), y = "%O2 saturation")+
      scale_x_continuous(breaks = breaks_extended(n = 10))+
      ylim(80, 100)
    
    plots$ph <- ggplot(dataT(), aes(runTime, pH))+
      geom_line(size = 1, color = "#00B388")+
      labs(x = paste("Runtime in",input$timeScale), y = "pH")+
      scale_x_continuous(breaks = breaks_extended(n = 10))+
      ylim(1,15)
    
    plots$air_flow <- ggplot(dataT(), aes(x = runTime, y = airflow))+
      geom_line(size = 1, color = "#00B388")+
      labs(x = paste("Runtime in",input$timeScale), y = "Air flow [L/min]")+
      scale_x_continuous(breaks = breaks_extended(n = 10))+
      ylim(0, 1)
    
    plots$oxygen_flow <- ggplot(dataT(), aes(runTime, O2flow))+
      geom_line(size = 1, color = "#00B388")+
      labs(x = paste("Runtime in",input$timeScale), y = "O2 flow [L/min]")+
      scale_x_continuous(breaks = breaks_extended(n = 10))+
      ylim(0,10)
  })
  
  output$plot <- renderPlot({
    premade_plots <- reactiveValuesToList(plots)
    do.call("plot_grid", c(premade_plots[input$whichPlot],
            ncol = 2, labels = "auto"))
  }) %>% 
    bindEvent(input$DoIt) 
}
shinyApp(ui = ui, server = server)