Reputation: 409
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
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