Peter Miksza
Peter Miksza

Reputation: 409

Using a dataframe built within a Shiny server for two outputs

I'm working on a simple Shiny app to visualize sources of variation in ANOVA (total, between, within). Basically, I'd like for users to input "group n", "means", and "sds" for a three-group oneway ANOVA scenario - then, the app generates a dataset to create a plot and a corresponding ANOVA table.

I've not been able to figure out how to have both, the plot and the ANOVA table update when the user changes the input parameters. Most of my attempts have resulted in a plot that updates but a table that doesn't.

The closest I've come to an actual solution is the "hack-y/cheating" approach below in which the same dataset is generated twice. However, this seems obviously unnecessary. I'm assuming the solution has something to do with creating a "reactive" dataset in the server function that can be drawn on for multiple outputs. This seems like it should be a fairly simple thing to do in principle. However, I've not been able to piece together tutorials/materials online to figure out how to do it. Any help with this would be appreciated.

CODE:

# Visualizing partitioning variance for oneway ANOVA

library(shiny)

ui <- fluidPage(
   titlePanel("Partitioning Variance in a Oneway ANOVA"),

   sidebarLayout(
      sidebarPanel(
        sliderInput("N", "n for each group:",
                     min = 2, max = 50, value = 25),
        sliderInput("M1", "Mean for Control Group:",
                    min = 1, max = 100, value = 55),
        sliderInput("SD1", "SD for Control Group:",
                    min = 1, max = 20, value = 10),
        sliderInput("M2", "Mean for Treatment Group One:",
                    min = 1, max = 100, value = 55),
        sliderInput("SD2", "SD for Treatment Group One:",
                    min = 1, max = 20, value = 10),
        sliderInput("M3", "Mean for Treatment Group Two:",
               min = 1, max = 100, value = 55),
        sliderInput("SD3", "SD for Treatment Group Two:",
                    min = 1, max = 20, value = 10)
),
      mainPanel(
         plotOutput("varPlot"),
         verbatimTextOutput("anovaTable")
      )
   )
)

server <- function(input, output) {

   output$varPlot <- renderPlot({
     set.seed(1976)
     X1 <- rnorm(input$N, input$M1, input$SD1)
     X2 <- rnorm(input$N, input$M2, input$SD2)
     X3 <- rnorm(input$N, input$M3, input$SD3)
     datOutcome = data.frame(X1, X2, X3)
     library(tidyr)
     dat <- gather(datOutcome, group, outcome)
     dat$group <- factor(dat$group, levels = c("X1", "X2", "X3"), 
                         labels = c("Control", "Treatment One", "Treatment Two"))
     # attach the data
     attach(dat)

     library(car)
     mod <- Anova(lm(outcome ~ group, data = dat), type = "III")

     # make the stripcharts by group
     stripchart(outcome ~ group, method = "jitter", jitter = 0.05, 
                vertical = TRUE, pch = 1, col = "blue", 
                group.names = c("Control", "Treatment One", "Treatment Two"),
                xlim = c(.5,4.75),
                ylim = c((min(dat$outcome) - 5), (max(dat$outcome) + 5)),
                ylab = "Outcome Value",
                main = paste("Group n =", input$N, 
                             "\nRed = total variation, Blue = within groups variation, Green indicates between groups variation"))

     # label group means
     text(1.3, mean(X1), 
          paste("Control \nmean =", format(round(mean(X1), 2), nsmall = 2)), 
          col = "darkgreen", cex = .9)
     text(2.3, mean(X2), 
          paste("Treatment One \nmean =", format(round(mean(X2), 2), nsmall = 2)), 
          col = "darkgreen", cex = .9)
     text(3.3, mean(X3), 
          paste("Treatment Two\n mean =", format(round(mean(X3), 2), nsmall = 2)), 
          col = "darkgreen", cex = .9)

     # add diamonds to indicate the means for each group
     points(1, mean(X1), pch = 18, cex = 2, col = "darkgreen")
     points(2, mean(X2), pch = 18, cex = 2, col = "darkgreen")
     points(3, mean(X3), pch = 18, cex = 2, col = "darkgreen")

     # plot a stripchart for the grand mean
     stripchart( outcome, method="jitter" , jitter=0.05 , 
                 vertical=TRUE , pch=1 , col="red" , 
                 at = 4, add = TRUE, 
                 xlim=c(.5,3.75))

     # label grand mean and add dimaond to indicate mean
     text(4.3, mean(outcome), 
          paste("Grand \nmean =", format(round(mean(outcome), 2), nsmall = 2)), 
          col = "red", cex = .9)
     points(4, mean(outcome), pch = 18, cex = 2)

   })


   output$anovaTable <- renderPrint( {
     set.seed(1976)
     X1 <- rnorm(input$N, input$M1, input$SD1)
     X2 <- rnorm(input$N, input$M2, input$SD2)
     X3 <- rnorm(input$N, input$M3, input$SD3)
     datOutcome = data.frame(X1, X2, X3)
     library(tidyr)
     dat <- gather(datOutcome, group, outcome)
     dat$group <- factor(dat$group, levels = c("X1", "X2", "X3"), 
                         labels = c("Control", "Treatment One", "Treatment Two"))

     A <- Anova(aov(outcome ~ group, data = dat), type = "III")
     A
   }) 
}

shinyApp(ui = ui, server = server)

Upvotes: 1

Views: 400

Answers (1)

guna
guna

Reputation: 1208

This can be solved using the "reactive" paradigm

library(shiny)
library(tidyr)

ui <- fluidPage(
  titlePanel("Partitioning Variance in a Oneway ANOVA"),

  sidebarLayout(
    sidebarPanel(
      sliderInput("N", "n for each group:",
                  min = 2, max = 50, value = 25),
      sliderInput("M1", "Mean for Control Group:",
                  min = 1, max = 100, value = 55),
      sliderInput("SD1", "SD for Control Group:",
                  min = 1, max = 20, value = 10),
      sliderInput("M2", "Mean for Treatment Group One:",
                  min = 1, max = 100, value = 55),
      sliderInput("SD2", "SD for Treatment Group One:",
                  min = 1, max = 20, value = 10),
      sliderInput("M3", "Mean for Treatment Group Two:",
                  min = 1, max = 100, value = 55),
      sliderInput("SD3", "SD for Treatment Group Two:",
                  min = 1, max = 20, value = 10)
    ),
    mainPanel(
      plotOutput("varPlot"),
      verbatimTextOutput("anovaTable")
    )
  )
)

server <- function(input, output) {

  myReactiveDat <- reactive({
    if(is.null(input$N)){
      return(NULL)
    }
    set.seed(1976)
    X1 <- rnorm(input$N, input$M1, input$SD1)
    X2 <- rnorm(input$N, input$M2, input$SD2)
    X3 <- rnorm(input$N, input$M3, input$SD3)
    datOutcome = data.frame(X1, X2, X3)
    dat <- gather(datOutcome, group, outcome)
    dat$group <- factor(dat$group, levels = c("X1", "X2", "X3"), 
                        labels = c("Control", "Treatment One", "Treatment Two"))
    res <- list(dat=dat, X1=X1, X2=X2, X3=X3)
  })

  output$varPlot <- renderPlot({
    res <- myReactiveDat()
    if(is.null(res)){
      return()
    }

    # attach the data
    dat <- res$dat
    attach(dat)

    library(car)
    mod <- Anova(lm(outcome ~ group, data = dat), type = "III")

    # make the stripcharts by group
    stripchart(outcome ~ group, method = "jitter", jitter = 0.05, 
               vertical = TRUE, pch = 1, col = "blue", 
               group.names = c("Control", "Treatment One", "Treatment Two"),
               xlim = c(.5,4.75),
               ylim = c((min(dat$outcome) - 5), (max(dat$outcome) + 5)),
               ylab = "Outcome Value",
               main = paste("Group n =", input$N, 
                            "\nRed = total variation, Blue = within groups variation, Green indicates between groups variation"))

    # label group means
    text(1.3, mean(res$X1), 
         paste("Control \nmean =", format(round(mean(res$X1), 2), nsmall = 2)), 
         col = "darkgreen", cex = .9)
    text(2.3, mean(res$X2), 
         paste("Treatment One \nmean =", format(round(mean(res$X2), 2), nsmall = 2)), 
         col = "darkgreen", cex = .9)
    text(3.3, mean(res$X3), 
         paste("Treatment Two\n mean =", format(round(mean(res$X3), 2), nsmall = 2)), 
         col = "darkgreen", cex = .9)

    # add diamonds to indicate the means for each group
    points(1, mean(res$X1), pch = 18, cex = 2, col = "darkgreen")
    points(2, mean(res$X2), pch = 18, cex = 2, col = "darkgreen")
    points(3, mean(res$X3), pch = 18, cex = 2, col = "darkgreen")

    # plot a stripchart for the grand mean
    stripchart( outcome, method="jitter" , jitter=0.05 , 
                vertical=TRUE , pch=1 , col="red" , 
                at = 4, add = TRUE, 
                xlim=c(.5,3.75))

    # label grand mean and add dimaond to indicate mean
    text(4.3, mean(outcome), 
         paste("Grand \nmean =", format(round(mean(outcome), 2), nsmall = 2)), 
         col = "red", cex = .9)
    points(4, mean(outcome), pch = 18, cex = 2)

  })


  output$anovaTable <- renderPrint( {
    res <- myReactiveDat()
    if(is.null(res)){
      return()
    }
    A <- Anova(aov(outcome ~ group, data = res$dat), type = "III")
    A
  }) 
}

shinyApp(ui = ui, server = server)

Upvotes: 1

Related Questions