reactive environment within reactiveValues (RShiny) - shiny

I am trying to build a matrix with reactive dimensions that I want to update using an observeEvent expression. My idea was the following:
First, I am creating a reactiveValues object with a matrix of dimensions input$length (--> reactive) and input 0. Then I am using observeEvent with actionButton to trigger an update in the matrix. This entails updating a specific cell in the matrix, indicated by a reactive index vector (ind()), with a reactive value (value()).
I understand the problem: within mat = ... I cannot use another reactive expression, however I don't have an alternative solution and would very much appreciate any input on this.
Thanks in advance!
Kind regards,
Julian
ui <- fluidPage(
numericInput("length", "Dimensions of the matrix", value = 5),
numericInput("a", "value for a", value = 2),
numericInput("b", "value for b", value = 2),
numericInput("ind1", "value for index vector 1", value = 1),
numericInput("ind2", "value for index vector 2", value = 1),
actionButton("go", "Update"),
tableOutput("matrix")
)
server <- function(input, output) {
### Calculate the value that will be used for the update
value <- reactive(
mean(rbeta(100, input$a, input$b))
)
### Create a reactive index vector used to determine the position of the cell in the matrix
ind <- reactive(
c(input$ind1, input$ind2)
)
### Create reactiveValues matrix with dimensions specified in length
beta.matrix <- reactiveValues(
mat = matrix(0, input$length, input$length)
)
### Update matrix at positon ind with new value
observeEvent(input$go, {
beta.matrix$mat[ind()[1], ind()[2]] <- value()
}
)
### Render matrix
output$matrix <- renderTable({
mat <- beta.matrix$mat
mat
})
}
# Run the application
shinyApp(ui = ui, server = server)

I think you'll need two reactive "stages" here.
initialise an empty matrix when the dimensions change
react on changes to the content of the matrix
Please check the following:
library(shiny)
ui <- fluidPage(
numericInput("length", "Dimensions of the matrix", value = 5),
numericInput("a", "value for a", value = 2),
numericInput("b", "value for b", value = 2),
numericInput("ind1", "value for index vector 1", value = 1),
numericInput("ind2", "value for index vector 2", value = 1),
actionButton("go", "Update"),
tableOutput("matrix")
)
server <- function(input, output) {
### Calculate the value that will be used for the update
value <- reactive(mean(rbeta(100, input$a, input$b)))
### Create a reactive index vector used to determine the position of the cell in the matrix
ind <- reactive(c(input$ind1, input$ind2))
beta.matrix <- reactiveValues(mat = NULL)
beta.matrix.ini <- reactive({
mat = matrix(0, input$length, input$length)
})
observe({
beta.matrix$mat <- beta.matrix.ini()
})
### Update matrix at positon ind with new value
observeEvent(input$go, {
beta.matrix$mat[ind()[1], ind()[2]] <- value()
})
### Render matrix
output$matrix <- renderTable({
mat <- beta.matrix$mat
mat
})
}
# Run the application
shinyApp(ui = ui, server = server)

Related

ShinyApp - reactive inferno

I have two input variables, and changing one will cause the change of the other one.
Further to that, if the value of input is outside limits it should default to min (if below) or max (if above) value.
All works fine as long as up and down arrows are being used.
The moment I am typing value 1 in Input1 it goes crazy.
Same if I am deleting Input2, even before I am typing anything...
I am aware that it must have something to do with reactive values, but can not fix it...
Any suggestion will be very much appreciated!
library(shiny)
ui <- fluidPage(
fluidRow(
uiOutput("Input1"),
numericInput("Input2", "Input 2",
min = 50, max = 150,
value = 100, step = 1)),
tableOutput("result")
)
#########################################################
server <- function(input, output, session) {
global <- reactiveValues(numVal = 10, numMin = 5, numMax = 15)
numVal <- reactive({
if(!is.null(input$Input1)){
if(input$Input1 < global$numMin) return(global$numMin)
if(input$Input1 > global$numMax) return(global$numMax)
return(input$Input1)
}else{
return(global$numVal)
}
})
output$Input1 <- renderUI(numericInput("Input1", "Input 1",
min = global$numMin, max = global$numMax,
value = numVal(), step = 0.1))
# when Input1 change, update Input2
observeEvent(input$Input1, {
updateNumericInput(session = session,
"Input2",
value = format(round(input$Input1*10, 0), nsmall = 0))
})
# when Input2 change, update Input1
observeEvent(input$Input2, {
updateNumericInput(session = session,
"Input1",
value = format(round(input$Input2*0.1, 1), nsmall = 1))
})
inputdata <- reactive({
data <- data.frame(Coef = as.numeric(input$Input1))
data
})
output$result <- renderTable({
data = inputdata()
resultTable = as.character(round((data$Coef + 10)*100, digits=2))
resultTable
})
}
#########################################################
shinyApp(ui, server)
You are on the brink of getting into a race condition:
Input 1 changes Input 2 changes Input 1 changes Input 2...
So foremost you should reconsider your design. You can use debounce / throttle to avoid some of the race consition by telling Shiny not too fire too quickly and as the updates are bijective you may achieve what you want, but I would really think about your design b/c these circle dependencies are almost never a good idea.
Having said that here is a solution which behaves better (N.B. I removed the dynamic rendering of the second input element as it has nothing to do wiht the problem at hand). It is not perfect, b/c you will eventually end up in a racing condition, but you can soften this situation by playing w/ the debouncing factors.
library(shiny)
ui <- fluidPage(
fluidRow(
numericInput("Input1", "Input 1",
min = 5, max = 15, value = 10, step = .1),
numericInput("Input2", "Input 2",
min = 50, max = 150,
value = 100, step = 1)),
tableOutput("result")
)
server <- function(input, output, session) {
## debounce both input, i.e. they are firing onyl if no change within 1sec happens
## c.f. ?debounce
getI1 <- reactive(input$Input1) %>%
debounce(1000)
getI2 <- reactive(input$Input2) %>%
debounce(1000)
observeEvent(input$Input1, {
updateNumericInput(session = session,
"Input2",
value = format(round(getI1() * 10, 0), nsmall = 0))
})
observeEvent(input$Input2, {
updateNumericInput(session = session,
"Input1",
value = format(round(getI2() * 0.1, 1), nsmall = 1))
})
inputdata <- reactive({
data <- data.frame(Coef = as.numeric(input$Input1))
data
})
output$result <- renderTable({
data = inputdata()
resultTable = as.character(round((data$Coef + 10)*100, digits=2))
resultTable
})
}
shinyApp(ui, server)

How to provide metadata values from server to ui method in shiny

I'm very new to shiny and butting against something there must be a pattern for, but have had no luck Googling.
I have a simple shiny file (app.R below) that returns a plot for n observations. In addition to making the plot available to the ui method, I'd like to pass it n to help it size the plot to my liking
library(shiny)
server <- function(input, output) {
output$distPlot <- renderPlot({
firstNames <- c("Bob", "Jane", "Bob")
lastNames <- c("Builder", "Gorillas", "Weave")
ages <- c(25, 26, 27)
df <- data.frame(firstNames, lastNames, ages)
# I want to pass this to `ui`
numFirstNames <- length(unique(df$firstNames))
mapping <- aes(x = firstNames, y = ages)
ggplot(df, mapping) + geom_violin() + coord_flip()
})
}
getHeightInPx <- function(numFirstNames) {
paste(100 * numFirstNames, "px", sep="")
}
ui <- fluidPage(
# I want height to be a function of numFirstNames as calculated in the server definition
mainPanel(plotOutput("distPlot", height = getHeightInPx(2)))
)
shinyApp(ui, server)
Since ui isn't a function and doesn't have direct access to output (to me it's getting to distPlot by framework magic) how can I get to data prepared in server to help layout the page?
Thanks
https://github.com/rstudio/shiny/issues/650 tipped me off that you could use the height parameter in renderPlot. So I combined that with some use of reactive and observe to call a function that returns the plot AND gives me the number of elements to use.
I hardly get reactive and observe, so while it seems to work I would not be surprised to hear that I am abusing them or that there's an easier way.
library(shiny)
renderDistPlot <- function(input) {
firstNames <- c("Bob", "Jane", "Bob", "Carol")
lastNames <- c("Builder", "Gorillas", "Weave", "Xmasing")
ages <- c(25, 26, 27, 23)
df <- data.frame(firstNames, lastNames, ages)
# I want to pass this to `ui`
numFirstNames <- length(unique(df$firstNames))
mapping <- aes(x = firstNames, y = ages)
plot <- ggplot(df, mapping) + geom_violin() + coord_flip()
list(Plot = plot, NumFirstNames = numFirstNames)
}
server <- function(input, output) {
renderDistPloatResult <- reactive(renderDistPlot(input))
observe(output$distPlot <- renderPlot(renderDistPloatResult()$Plot, height = renderDistPloatResult()$NumFirstNames * 100))
}
ui <- fluidPage(
# I want height to be a function of numFirstNames as calculated in the server definition
mainPanel(plotOutput("distPlot"))
)
shinyApp(ui, server)

To write a csv after the the modaldialogue is closed in shiny

I want to perform some actions like writing in a dataframe post i close the modaldialogue. Consider below example.
obs8<-observe({ req(input$Continue) if(input$password3 > 0 & input$password4 > 0 & (input$password3==input$password4)==TRUE & (is.validpw(input$password3))==TRUE & (is.validpw(input$password4))==TRUE){
showModal(modalDialog(
title=tags$h4(tags$strong("Password Changed Successfully")),
easyClose=FALSE,
footer=modalButton("Close")
))
I am trying to execute below code post the if condition is true and modal is displayed but no luck.
PASSWORD$Passord <- as.character(PASSWORD$Passord)
PASSWORD$Passord[PASSWORD$Passord==pwd] <- input$password3
PASSWORD$Passord <- as.factor(PASSWORD$Passord)
write.csv(PASSWORD,"<PATH>",row.names=FALSE)
I rewrote it as pure Shiny without all the password stuff and it works fine:
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
obs8<-observe({
req(input$bins)
if(input$bins > 40){
showModal(modalDialog(
title=tags$h4(tags$strong("Password Changed Successfully")),
easyClose=FALSE,
footer=modalButton("Close")
))
write.csv("1, 2, 1, 2", "<PATH>", row.names = FALSE)
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
There's something wrong with the other stuff you're doing, but I can't tell what it is without a reproducible example

How to save previous interaction using reactiveValues

I am developing a table which does some calculations each time that an action button is used. One column of my table depends on its previous value,
C_new <- C_old + B_new - A_new
For instance, if A=4, B=A+2 and C= C(-1) + B - A my expected results are
A B C
1 2 3
4 6 5
I have tried to save the previous value of column C using reactiveValue, as mentioned in How to “remember” information from the previous iteration when using reactiveTimer in Shiny?, but it doesn't work. I don't know where I am getting wrong.
Here is my code
library(shiny)
ui <- fluidPage(
sidebarPanel(textInput("c1","Example"),
actionButton("update", "Update Table")),
mainPanel(tableOutput("example"))
)
server <- function(input, output) {
C_old <- reactive(x=3)
values <- reactiveValues(df = data.frame(A=1, B=2, C=3))
newEntry <- observeEvent(input$update,{
A_new <- as.numeric(input$c1)
B_new <- A_new + 2
C_new <- isolate (C_old$x + B_new - A_new)
C_old$x <<- C_new
new <- data.frame(A=A_new,B=B_new, C=C_new)
# attach the new line to the old data frame here:
values$df <- rbind(values$df, new)
})
# Print the content of values$df
output$example <- renderTable({
return(values$df)
})
}
shinyApp(ui = ui, server = server)
Important to know, observeEvents (similar to observes) don't have outputs. You just observe a change and do something in their body, but nothing is supposed to be returned (this is different to reactive({ }), which also observes changes but has return values), useful link.
Isolate is not needed in observeEvent , because nothing triggers an update except for input$update (this is different to observe and reactive, where all changeable items in the body trigger an update).
Below is the solution to your problem. I used reactiveVal which stores one single updateable value (see ?reactiveVal for help). After clicking the action button, I retrieve the old table by calling values(), calculate all new values (beware, I need to use tail to get only the last C value) and attach the result to the old value before storing the extended table into values by calling values(new_df):
library(shiny)
ui <- fluidPage(
sidebarPanel(numericInput("c1","Example", 0),
actionButton("update", "Update Table")),
mainPanel(tableOutput("example"))
)
server <- function(input, output) {
# stores the current data frame, called by values() and set by values(new_data_table)
values <- reactiveVal(data.frame(A=1, B=2, C=3))
# update values table on button click
observeEvent(input$update,{
old_values <- values()
A_new <- input$c1
B_new <- A_new + 2
C_new <- tail(old_values$C, 1) + B_new - A_new # tail to get the last C value
new_values <- data.frame(A=A_new, B=B_new, C=C_new)
# attach the new line to the old data frame here:
new_df <- rbind(old_values, new_values)
#store the result in values variable
values(new_df)
})
# Print the content of values$df
output$example <- renderTable({
return(values())
})
}
shinyApp(ui = ui, server = server)

Making a function interactive in shiny

A txt.file data contaning a matrix of integers with 5 integers for each column and 1000 rows.
So if we press
data
we get this output
96520
69850
...
36884
We can get a random row by this
getnumbers <- sample(data,1, replace=FALSE)
By getting a random row in data the task is to enter the next row (by press a,b,c,d,e) and check if it's correct. So if we have the kth entry in data we want to get the k+1 entry in data by pressing the digits and see if it's correct.
check <- function(a,b,c,d,e){
if( identical( data[k+1] , c(a,b,c,d,e)) == TRUE ) {
return("Correct") }
else{return("Not correct")}
How can I implement this R code in Shiny so I can make it interactive using ubuntu ?
Hopefully I understood your question correctly but here's how you could do it:
library(shiny)
data <- matrix(round(runif(5*3)),ncol=3)
ui <- shinyUI(fluidPage(
fluidRow(
column(6, h4("Randomly Selected Row [k]")),
column(6, h4("Nex Row [k+1]"))
),
fluidRow(
column(6, textOutput("selRow")),
column(6, textOutput("nxtRow"))
),
fluidRow(
column(8, textInput("guessStr","Gues row: ")),
column(4, actionButton("guess","guess"))
),
textOutput("guessRes")
))
server <- shinyServer(function(input, output, session) {
# Make the current rownumber a reactive
r.num <<- 0
makeReactiveBinding('r.num')
# If rownumber changes update UI
observe({
if(is.null(r.num)) return(NULL)
output$selRow <- renderPrint({data[r.num,]})
output$nxtRow <- renderPrint({data[r.num+1,]})
})
# Get a row number by random, can't select last row
randomRow <- function(){
r.num <<- sample(1:nrow(data)-1, 1)
}
# If user presses guess button
observeEvent(input$guess, {
# I convert to numerical but this can be modified to work with characters to
input.str <- as.numeric(strsplit(input$guessStr,',')[[1]])
msg <- sprintf("You guessed that the next row is: %s",input$guessStr)
if( identical(data[r.num+1,], input.str)){
msg <- paste(msg," , this was correct!")
}
else{
msg <- paste(msg," , this was wrong")
}
output$guessRes <- renderPrint({msg})
})
# Initiate the guessing by randmozing a row
randomRow()
})
shinyApp(ui = ui, server = server)