【问题标题】:How can I summarize reactive data from outside a render function in a Shiny app?如何在 Shiny 应用程序中汇总来自渲染函数外部的反应数据?
【发布时间】:2020-05-21 19:44:38
【问题描述】:

对于这个特别闪亮的例子,我正在尝试应用一个圆形模型并在 ggplot 和一个汇总表中显示和总结它。在尝试添加反应式“画笔绘图”功能之前,这很简单。每个数据点代表一个日期,选择图的点是能够丢弃不需要的日期。据我所知,这需要过滤和模型拟合在renderPlot 内,这会导致复杂化(无法找到数据/模型),试图在外部调用过滤后的数据和循环模型的统计输出函数和/或在另一个反应函数中。这会产生Error: object 'k_circ.lm' not found 所以我的问题是:

  1. 如何从renderPlot 函数中读取过滤后的数据 到summarytable 矩阵?
  2. 我怎样才能类似地添加来自k_circ.lm 的拟合模型值和残差?
  3. 有没有更好或更简单的方法来安排应用程序以避免这种情况?

为工作(如果格式不正确)汇总表注释掉替代代码行。

library(dplyr)           # For data manipulation
library(ggplot2)         # For drawing plots
library(shiny)           # For running the app
library(plotly)          # For data manipulation         
library(circular)        # For Circular regressions
library(gridExtra)

# Define UI ----
ui <- fluidPage(

  # App title ----
  titlePanel("Circular Brushplot Demo"),

  # Sidebar layout with input and output definitions ----
  sidebarLayout(
    sidebarPanel(
      actionButton("exclude_toggle", "Toggle points"),
      actionButton("exclude_reset", "Reset")
    ),


  # Main panel for displaying outputs ----
  mainPanel(

      #reactive plot output with point and 'brush' selection
      fluidRow(plotOutput("k", height = 400,
                          click = "k_click",
                          brush = brushOpts(
                            id = "k_brush" ))),
      plotOutput("s", height = 400)
    )
  )
)

# Define server logic 
server <- function(input, output) {

  psideg <- c(356,97,211,232,343,292,157,302,335,302,324,85,324,340,157,238,254,146,232,122,329)
  thetadeg <- c(119,162,221,259,270,29,97,292,40,313,94,45,47,108,221,270,119,248,270,45,23)

  ## Data in radians then to "circular format"
  psirad <- psideg*2*pi/360
  thetarad <- thetadeg*2*pi/360
  cpsirad <- circular(psirad)
  cthetarad <- circular(thetarad)
  cdat <- data.frame(cpsirad, cthetarad)



  ###### reactive brush plot ########
  # For storing which rows have been excluded
  vals <- reactiveValues(
    keeprows = rep(TRUE, nrow(cdat)))

  output$k <- renderPlot({
    # Plot the kept and excluded points as two separate data sets
    keep    <- cdat[ vals$keeprows, , drop = FALSE]
    exclude <- cdat[!vals$keeprows, , drop = FALSE]

    ## Fits circular model specifically for 'keeprows' of selected data
    k_circlm <- lm.circular(type = "c-c", y = keep$cthetarad, x = keep$cpsirad, order = 1)

    k_circlm

    ggplot(keep, aes(cthetarad, cpsirad)) + 
      geom_point(aes(cthetarad, cpsirad, colour = keep$Vmag, size = 5))+
      scale_colour_gradient(low ="blue", high = "red")+
      geom_smooth(method = lm, fullrange = TRUE, color = "black") +
      geom_point(data = exclude, shape = 13, size = 5, fill = NA, color = "black", alpha = 0.25) +
      annotate("text", x = min(keep$cthetarad), y = Inf, hjust = .1, vjust = 1, 
               label = paste0("p value 1 = ", round(k_circlm$p.values[1], 2)), size = 7)+
      annotate("text", x = min(keep$cthetarad), y = Inf, hjust = .1, vjust = 2.5, 
               label = paste0("p value 2 = ", round(k_circlm$p.values[2], 2)), size = 7)+
      annotate("text", x = min(keep$cthetarad), y = Inf, hjust = .1, vjust = 4, 
               label = paste0("rho = ", round(k_circlm$rho, 2)), size = 7)+
      xlab("Lighthouse Direction (radians)")+ ylab("ADCP site direction (radians)")+
      theme(axis.title.x = element_text(size = 20), axis.title.y = element_text(size = 20))
  })

  # Toggle points that are clicked
  observeEvent(input$k_click, {
    res <- nearPoints(cdat, input$k_click, allRows = TRUE)

    vals$keeprows <- xor(vals$keeprows, res$selected_)})

  # Toggle points that are brushed, when button is clicked
  observeEvent(input$exclude_toggle, {
    res <- brushedPoints(cdat, input$k_brush, allRows = TRUE)

    vals$keeprows <- xor(vals$keeprows, res$selected_)})

  # Reset all points
  observeEvent(input$exclude_reset, {
    vals$keeprows <- rep(TRUE, nrow(cdat))})

  output$s <- renderPlot({

    # Create Summary table
    summarytable <- data.frame(matrix(ncol = 4, nrow = nrow(keep)))
    colnames(summarytable) <- c(  "Psi_dir", "Theta_dir", "Fitted_values", "Residuals")

    # Un-comment lines below to read from non-reactive data for working summary table
    #summarytable$Psi_dir <- round(cdat$cpsirad, 2)
    #summarytable$Theta_dir <- round(cdat$cthetarad, 2)

    # attempting to pull from circlm within render plot
    # comment out for summarytable to work
    summarytable$Psi_dir <- round(keep$cpsirad, 2)
    summarytable$Theta_dir <- round(keep$cthetarad, 2)
    summarytable$Fitted_values <- round(k_circ.lm$fitted)
    summarytable$Residuals <- round(k_circ.lm$residuals)

    # outputing table with minimal formatting 
    summarytable <-na.omit(summarytable)
    t <- tableGrob(summarytable)
    Q <- grid.arrange(t, nrow = 1)
    Q

    }
  )
}

shinyApp(ui = ui, server = server)

【问题讨论】:

    标签: r ggplot2 shiny non-linear-regression shinyapps


    【解决方案1】:

    这里有一些想法 - 但有多种方法可以处理此问题,您可能希望在进一步处理此问题后重新构建 server 函数。

    首先,您可能需要一个reactive 表达式,它会根据vals$keeprows 更新您的模型,因为这会随着您的点击而改变。然后,您可以从绘图和数据表中访问此表达式的模型结果。

    这是一个例子:

      fit_model <- reactive({
        ## Keep and exclude based on reactive value keeprows
        keep = cdat[ vals$keeprows, , drop = FALSE]
        exclude = cdat[!vals$keeprows, , drop = FALSE]
    
        ## Fits circular model specifically for 'keeprows' of selected data
        k_circlm <- lm.circular(type = "c-c", y = keep$cthetarad, x = keep$cpsirad, order = 1)
    
        ## Returns list of items including what to keep, exclude, and model
        list(k_circlm = k_circlm, keep = keep, exclude = exclude)
      })
    

    它将返回一个list,您可以从图中访问它:

      output$k <- renderPlot({
    
        exclude <- fit_model()[["exclude"]]
        keep <- fit_model()[["keep"]]
        k_circlm <- fit_model()[["k_circlm"]]
    
        ggplot(keep, aes(cthetarad, cpsirad)) + 
        ...
    

    并且可以从您的表中访问相同的内容(尽管您拥有 renderPlot?):

      output$s <- renderPlot({
        keep = fit_model()[["keep"]]
        k_circ.lm <- fit_model()[["k_circlm"]]
    
        # Create Summary table
        summarytable <- data.frame(matrix(ncol = 4, nrow = nrow(keep)))
        ...
    

    请注意,由于表长度会随着行的保留而变化,因此您可能希望使用我上面提到的nrow(keep),而不是nrow(cdat),除非我弄错了。

    我还加载了 gridExtra 库以进行测试。

    我怀疑您可以考虑许多其他改进,但认为这可能会帮助您首先进入功能状态。

    【讨论】:

    • 感谢您的帮助,功能不错!你也是正确的,我想使用nrow(keep),所以我会相应地改变它。 renderPlot for output$s 可能不是最好的方法,但与我试图为其构建可重现演示的大型应用程序最相似……您如何建议以不同的方式呈现它?
    猜你喜欢
    • 2018-07-08
    • 2019-10-07
    • 1970-01-01
    • 2019-07-19
    • 2019-04-26
    • 2018-06-23
    • 2020-09-05
    • 2020-09-09
    • 1970-01-01
    相关资源
    最近更新 更多