I'm building a small shiny app to demo some causal inference concepts and I want to split all the independent variables from a linear regression formula and show them in multiple plots.
Strangely, when I look through the list of independent variables, shiny renders the last of them twice rather than rendering the first and then the second.
Here's the UI part of my code:
ui <- fluidPage(
textInput("regression", "Regression formula", ""),
verbatimTextOutput("summary"),
plotOutput("plot1", width=fig.width, height=fig.height),
plotOutput("plot2", width=fig.width, height=fig.height),
plotOutput("plot3", width=fig.width, height=fig.height),
)
and here's where I split the formula into multiple parts and try to render all the independent variables:
server <- function(input, output) {
parse_lm <- reactive({
formula <- input$regression
if(formula == "" || grepl("~", formula) == FALSE) {
return()
}
first_split <- unlist(strsplit(formula, "[~]"))
dependent <- str_trim(first_split[1])
independent <- unlist(lapply(strsplit(first_split[2], "[+]"), str_trim))
for( i in 1:length(independent))
{
whichplot <- paste("plot", toString(i), sep="")
print(whichplot) # this prints "plot1" and "plot2"
print(independent[i]) # this prints "vs" and "am"
output[[whichplot]] <- renderPlot({
ggplot(mtcars, aes(x = .data[[dependent]])) +
geom_histogram(aes(color = as.factor(.data[[independent[i]]]), fill = as.factor(.data[[independent[i]]])),
position = "identity", bins = 30, alpha = 0.2) +
scale_color_manual(values = c("#00AFBB", "#E7B800")) +
scale_fill_manual(values = c("#00AFBB", "#E7B800"))
})
}
})
observeEvent(input$regression, {
parse_lm()
})
}
When I pass "mpg ~ vs + am" as the formula, what should happen is that I get a hist of both 'vs' and 'am' but instead I just get 'am' twice. I suspect that shiny is re-rendering the plot but I can't figure out why.
You have an issue with lazy evaluation. The since you use a for loop, you are mutating the value of i
. That value is remains unevaluated till the polts are drawn and by that time the value if i
has changed. It would be better to us something like lapply
to create a closure on the value of i
, that way it won't change.
lapply(1:length(independent), function(i) {
whichplot <- paste("plot", toString(i), sep="")
print(whichplot) # this prints "plot1" and "plot2"
print(independent[i]) # this prints "vs" and "am"
output[[whichplot]] <- renderPlot({
ggplot(mtcars, aes(x = .data[[dependent]])) +
geom_histogram(aes(color = as.factor(.data[[independent[i]]]), fill = as.factor(.data[[independent[i]]])),
position = "identity", bins = 30, alpha = 0.2) +
scale_color_manual(values = c("#00AFBB", "#E7B800")) +
scale_fill_manual(values = c("#00AFBB", "#E7B800"))
})
})