In running the reactive code below, the user can input a variable "Y" into the first matrix rendered at the top (generated by function matInputBase()
) to run a simple scenario where that variable Y appears in only one period in the output, with period set by the slider input for time window "W". The user can optionally input into the following two input matrixes (both generated by function matInputFlex()
), additional variable Y's into other specified time periods ("X") so long as they fit into overall time window W. Function matInputFlex()
allows the user to run more detailed scenarios than the basic matInputBase()
. Note how an input into matInputBase()
flows directly into the first row and second column of each matInputFlex()
, as shown in the image below. This part works correctly.
However, if the user has input a scenario into one of the matInputFlex()
matrixes, and then decides to change any one of the values in matInputBase()
, then both of the matInputFlex()
matrixes are reset. I don't want both of the matInputFlex()
matrixes reset, I would like only the matInputFlex()
matrix directly affected by the change to the matInputBase()
matrix to be reset. I would like to preserve the matInputFlex()
values unaffected by a change to its corresponding parent matInputBase()
value. So, for if example, if I have built up a matInputFlex()
scenario for Var_1 and then I change a matInputBase()
value for Var_2, as shown in the second half of the image below, then only the matInputFlex()
values for Var_2 should reset and not the matInputFlex()
values for Var_1. How do I delink these reactive tables, so that Var_1 and Var_2 can process independently?
I have played around with isolate()
and observeEvent()
for input$base_input
, but these changes stopped the flow of inputs from matInputBase()
to matInputFlex()
.
Image of how the App works:
Code:
library(shiny)
library(shinyMatrix)
matInputBase <- function(name) {
matrixInput(name,
value = matrix(c(1,2), 2, 1, dimnames = list(c("Var_1", "Var_2"), NULL)),
rows = list(extend = FALSE, names = TRUE),
cols = list(extend = FALSE, names = FALSE, editableNames = FALSE),
class = "numeric"
)
}
matInputFlex <- function(name, x,y) {
matrixInput(
name,
value = matrix(c(x, y), 1, 2, dimnames = list(NULL, c("X", "Y"))),
rows = list(extend = TRUE, names = FALSE),
cols = list(extend = TRUE, delta = 0, names = TRUE, editableNames = FALSE),
class = "numeric"
)
}
matStretch <- function(mat, time_window, col_name) {
mat[, 1] <- pmin(mat[, 1], time_window)
df <- data.frame(matrix(nrow = time_window, ncol = 1, dimnames = list(NULL, col_name)))
df[, col_name] <- ifelse(seq_along(df[, 1]) %in% mat[, 1], mat[match(seq_along(df[, 1]), mat[, 1]), 2], 0)
return(df)
}
ui <- fluidPage(
sliderInput("periods","Time window (W):", min = 1, max = 10, value = 10),
h5(strong("Var (Y) over time window:")),
matInputBase("base_input"),
uiOutput("Vectors"),
tableOutput("table2")
)
server <- function(input, output, session) {
base_var_1 <- reactive(input$base_input[1,1])
base_var_2 <- reactive(input$base_input[2,1])
output$Vectors <- renderUI({
tagList(
h5(strong("Adjust Var_1 (Y) at time X:")),
matInputFlex("var_1_input", input$periods, base_var_1()),
h5(strong("Adjust Var_2 (Y) at time X:")),
matInputFlex("var_2_input", input$periods, base_var_2())
)
})
output$table2 <- renderTable(
cbind(
matStretch(input$var_1_input, input$periods, "Var_1"),
matStretch(input$var_2_input, input$periods, "Var_2")
)
)
}
shinyApp(ui, server)
Edited input$periods
event handling from base_input()
to isolate(base_input())
.
The code below separates output$Vectors
into two separate renderUI
variables in the server, tracks the previous value of input$base_input
to determine which row has changed (if any), and uses observeEvent
for more explicit event handling.
It appears to handle matInputBase
and matInputFlex
events correctly, and it will reset both matInputFlex
if input$periods
changes.
library(shiny)
library(shinyMatrix)
matInputBase <- function(name) {
matrixInput(name,
value = matrix(c(1,2), 2, 1, dimnames = list(c("Var_1", "Var_2"), NULL)),
rows = list(extend = FALSE, names = TRUE),
cols = list(extend = FALSE, names = FALSE, editableNames = FALSE),
class = "numeric"
)
}
matInputFlex <- function(name, x,y) {
matrixInput(
name,
value = matrix(c(x, y), 1, 2, dimnames = list(NULL, c("X", "Y"))),
rows = list(extend = TRUE, names = FALSE),
cols = list(extend = TRUE, delta = 0, names = TRUE, editableNames = FALSE),
class = "numeric"
)
}
matStretch <- function(mat, time_window, col_name) {
mat[, 1] <- pmin(mat[, 1], time_window)
df <- data.frame(matrix(nrow = time_window, ncol = 1, dimnames = list(NULL, col_name)))
df[, col_name] <- ifelse(seq_along(df[, 1]) %in% mat[, 1], mat[match(seq_along(df[, 1]), mat[, 1]), 2], 0)
return(df)
}
ui <- fluidPage(
sliderInput("periods","Time window (W):", min = 1, max = 10, value = 10),
h5(strong("Var (Y) over time window:")),
matInputBase("base_input"),
# Modified to bring the h5 elements into the UI.
tagList(
h5(strong("Adjust Var_1 (Y) at time X:")),
uiOutput("Vectors1"),
h5(strong("Adjust Var_2 (Y) at time X:")),
uiOutput("Vectors2")
),
tableOutput("table2")
)
server <- function(input, output, session) {
# Reactive variable storage
base_input <- reactive(input$base_input)
prev <- reactiveValues(
dat = matrix(c(1,2), 2, 1, dimnames = list(c("Var_1", "Var_2"), NULL))
)
# Separate out the handling of input$base_input.
observeEvent(
input$base_input,
{
if (prev$dat[1,1] != base_input()[1,1]){
output$Vectors1 <- renderUI({
matInputFlex("var_1_input", input$periods, base_input()[1,1])
})
}
if (prev$dat[2,1] != base_input()[2,1]){
output$Vectors2 <- renderUI({
matInputFlex("var_2_input", input$periods, base_input()[2,1])
})
}
# Save the current value for testing the next event.
prev$dat <- base_input()
})
# Deal with input$periods events separately.
observeEvent(
input$periods,
{
output$Vectors1 <- renderUI({
matInputFlex("var_1_input", input$periods, isolate(base_input())[1,1])
})
output$Vectors2 <- renderUI({
matInputFlex("var_2_input", input$periods, isolate(base_input())[2,1])
})
})
output$table2 <- renderTable(
cbind(
matStretch(input$var_1_input, input$periods, "Var_1"),
matStretch(input$var_2_input, input$periods, "Var_2")
)
)
}
shinyApp(ui, server)