Pass reactive object as input to function in a shiny app - r

In the shiny app below I want to pass as input in the initialize_NN() function the layers() which is a reactive object. But when I try to pass it as layers() instead of layers I get an error in my console like unexpected token'layers',expected RPAREN. Any solution to this or any workaround?The initialize_NN() do not expect to give something now but I just want to know how could I pass reactive input into it.
library(shiny)
library(dplyr)
### user inter phase
ui <- fluidPage(
### App title ----
titlePanel(
#title=div(img(src="pics/IRP_NHSc.jpg", width="99%"))
title="Network App"
)
,
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
sliderInput("lay","Adjust layers",min=2,max = 6,value = 2,step=1),
sliderInput("neu","Adjust Neurons",min=10,max = 60,value = 50,step=1)
),
### Main panel for displaying outputs ----
mainPanel(
)
)
)
#### Server
server <- function(input, output, session) {
layers <- reactive({
l<-c(1, rep(input$neu, input$lay), 1)
})
initialize_NN <- function(layers()){
weights <-c()
biases <- c()
num_layers <- length(layers())-1
for (i in seq(num_layers)){
W <- xavier_init(c(layers()[i], layers()[i+1]))
weights <- c(weights, W)
biases <- c(biases, b)
}
return(list(Weights = weights, Biases = biases))
}
obj <- initialize_NN(layers=layers())
weights <- obj$Weights
biases <- obj$Biases
neural_net <- function(x_, weights, biases){
num_layers <- length(weights) + 1
H <- x_
for (l in seq(num_layers-2)){
W <- weights[[l]]
b <- biases[[l]]
H <- tf$nn$tanh(tf$add(tf$matmul(H, W), b))
}
W <- tail(weights, n=1)[1]
b <- tail(biases, n=1)[1]
Y <- tf$add(tf$matmul(H, W), b)
return(Y)
}
}
shinyApp(ui = ui, server = server)

Define function initialize_NN() with "normal" arguments:
initialize_NN <- function(layers){
weights <-c()
biases <- c()
num_layers <- length(layers)-1
for (i in seq(num_layers)){
W <- xavier_init(c(layers[i], layers[i+1]))
weights <- c(weights, W)
biases <- c(biases, b)
}
return(list(Weights = weights, Biases = biases))
}
Then wherever you call the function pass your reactive as the argument layers:
initialize_NN(layers = layers())
Edit 1:
As #jpdugo17 pointed out in the comments, it needs to be done inside a reactive context: whether a reactive(), observe(), observeEvent() etc.
For example:
r_NN <- reactive({
initialize_NN(layers = layers())
})

Related

How to make the plot reactive to user input?

In the below super-simple MWE code, I'm trying to make the plot reactive to user slider input 'periods'. I've tried various iterations of observeEvent with no luck. Can someone please help me make this reactive?
The plot is supposed to show endBal on the y-axis and periods on the x-axis.
Also, in the below line of MWE code, res <- Reduce(f,seq(periods),init=beginBal,accumulate=TRUE) is not picking up the value of periods from the slider input. Instead to make the MWE run for demo I have to manually define the periods variable in the "Define vectors" section at the top of the code (periods <- 5 for example). Any idea why this part isn't working?
library(shiny)
### Define vectors ##############################################################
beginBal <- 1000
yield_vector <- c(0.30,0.30,0.30,0.30,0.30,0.28,0.26,0.20,0.18,0.20)
npr_vector <- c(0.30,0.30,0.30,0.30,0.30,0.30,0.30,0.30,0.30,0.30)
mpr_vector <- c(0.20,0.20,0.20,0.20,0.20,0.20,0.20,0.20,0.20,0.20)
default_vector <- c(0.10,0.10,0.10,0.10,0.10,0.09,0.08,0.07,0.06,0.05)
### End define vectors ##########################################################
ui <- fluidPage(
sliderInput(inputId = "periods",
label = "Periods:",
min = 1,
max = 10,
value = 5),
plotOutput(outputId = "balancePlot")
) # close fluid page
server <- function(input,output){
### Generates data for plotting ###############################################
f <- function(x,y){x*(1+npr_vector[y]-mpr_vector[y]-default_vector[y]/12)}
res <- Reduce(f,seq(periods),init=beginBal,accumulate=TRUE)
b <- head(res,-1)
endBal <- res[-1]
### End data for plotting #####################################################
output$balancePlot <- renderPlot({plot(endBal)})
} # close server
shinyApp(ui = ui, server = server)
Try this
output$balancePlot <- renderPlot({
f <- function(x,y){x*(1+npr_vector[y]-mpr_vector[y]-default_vector[y]/12)}
res <- Reduce(f,seq(input$periods),init=beginBal,accumulate=TRUE)
b <- head(res,-1)
endBal <- res[-1]
plot(endBal)
})

Shiny - runif function inside reactive loop

hope someone can help with this one:
My code below was returning a dinamic table as I intend, but when I created the for loop to assign individually random numbers to each [k,p] position in the table all the calculations stop and I see an empty UI.
If instead of
rv$MCProbTable[[k,p]] = round(as.numeric(stats::runif(1,0,100)), 3)
I use for example
rv$MCProbTable[[k,p]] = 2
I see "2" allocated in each position in the table, what is close to what I want and shows that everything is working up to the runif application. So is it an issue with the function? Or most likely it is the reactive for loop that isn't set properly?
Thanks and hope this is an easy one!
## LIBRARIES
library(tidyverse)
library(stats)
library(data.table)
library(triangle)
library(base)
library(matrixStats)
library(ggplot2)
library(ggthemes)
library(readxl)
library(httr)
library(writexl)
library(shiny)
ExcelTemplate <- tempfile(fileext = ".xlsx")
GET(url = "https://www.openmontecarlo.com/SampleS.xlsx",write_disk(ExcelTemplate))
defaultRR <- read_xlsx(ExcelTemplate)
## DEFINE UI
ui <- fluidPage(
numericInput("ISvModels", label = h3("Select how many scenarios to run in the model:"),value=10,min=5,max=10000),
hr(),
mainPanel(
fluidRow(
p("vModels"),
textOutput("vModelsText"),
p("vtModels"),
textOutput("vtModelsText"),
p("Scenarios"),
textOutput("SnScenarios"),
h3(strong("ProbTable")),
br(),
dataTableOutput("SMCProbTable"),
hr()
)
)
)
server <- function(input, output, session) {
## CREATE DEFAULT RR AND REACTIVE VARIABLES
rv <- reactiveValues(
vModels = 10,
nScenarios = 10,
vtModelScenarios = NULL,
RRDT = data.frame(),
nRisks = 1,
MCProbTable = data.frame(),
p = 1,
k = 1
)
## PROCESS MODELS INPUT
observe({rv$vModels <- as.numeric(unlist(input$ISvModels))})
output$vModelsText <- renderPrint({unlist(rv$vModels)})
qtModels <- reactive({length(unlist(rv$vModels))})
output$qtModelsText <- renderPrint({unlist(qtModels())})
vtModels <- reactive({paste0("M",1:qtModels()," n = ",rv$vModels," scenarios")})
output$vtModelsText <- renderPrint({unlist(vtModels())})
## RR TABLE
observe({rv$nRisks <- nrow(defaultRR)})
###############################################################################################################
## CALCULATE SCENARIOS - CREATE DATA TABLES AND TEXT VECTORS FOR EACH SCENARIO
## Vectors for SINGLE scenario
observe({rv$nScenarios <- rv$vModels})
observe({rv$vtModelScenarios <- paste0("Scenario ",1:rv$nScenarios,"/",rv$vModels)})
## CREATE PROBABILITY TABLE AND CALCULATE PROBABILITIES
observe({rv$MCProbTable <- data.frame(matrix(nrow=rv$nRisks,ncol=rv$nScenarios))})
observe({rownames(rv$MCProbTable) <- rv$RRDT$ID})
observe({colnames(rv$MCProbTable) <- rv$vtModelScenarios})
observe({
for (p in 1:rv$nScenarios){
for (k in 1:rv$nRisks){
rv$MCProbTable[[k,p]] = round(as.numeric(stats::runif(1,0,100)), 3)
}
}
})
output$SMCProbTable <- renderDataTable({rv$MCProbTable})
}
# Run the app ----
shinyApp(ui = ui, server = server)
In your for loop, use isolate()
for (p in 1:rv$nScenarios){
for (k in 1:rv$nRisks){
isolate(
rv$MCProbTable[k,p] <- round(as.numeric(stats::runif(1,0,100)), 3)
)
}
}

Challenge with dynamically generated, interactive R Shiny plots (mostly functioning code)

Below is the minimum code. It works, but there is a weird problem. Here is what works:
User can select a number of plots (default is 3).
User can click in a plot and have that value represented (partly works).
Steps to reproduce the "partly works":
At launch, click in plot #3, no problem.
Click in plot #2, nothing happens.
Reduce the number of plots from 3 to 2 and then back to 3.
Click in plot #2, now it works.
Click in plot #1, nothing happens.
Reduce the number of plots from 3 to 1 and then back to 3.
Click in plot #1, now it works.
If you reload the app, and start with step 6 above, all plots are interactive as expected.
rm(list=ls())
library(shiny)
#
# Dynamic number of plots: https://stackoverflow.com/questions/26931173/shiny-r-renderplots-on-the-fly
# That can invalidate each other: https://stackoverflow.com/questions/33382525/how-to-invalidate-reactive-observer-using-code
#
ui <- (fluidPage(sidebarLayout(
sidebarPanel(
numericInput("np", "Plots:", min=0, max=10, value=3, step=1)
)
,mainPanel(
fluidRow(uiOutput("plots"))
)
)))
server <- function(input, output, session) {
val <- reactiveValues()
dum <- reactiveValues(v=0)
obs <- list()
### This is the function to break the whole data into different blocks for each page
plotInput <- reactive({
print("Reactive")
np <- input$np
for(i in 1:np) {
cx <- paste0("clk_p",i); dx <- paste0("dbl_p",i); px <- paste0("p",i)
obs[[cx]] <- observeEvent(input[[cx]], {
req(input[[cx]]); val[[px]] <- input[[cx]]$x; dum$v <- dum$v+1; print(paste("Dum",dum$v))
})
obs[[dx]] <- observeEvent(input[[dx]], {
req(input[[dx]]); val[[px]] <- NULL
})
}
return (list(np=np))
})
##### Create divs######
output$plots <- renderUI({
print("Tag plots")
pls <- list()
for(i in 1:plotInput()$np) {
pls[[i]] <- column(4,
plotOutput(paste0("p",i), height=200, width=200
,click=paste0("clk_p",i)
,dblclick=paste0("dbl_p",i))
)
}
tagList(pls)
})
observe({
print("Observe")
lapply(1:plotInput()$np, function(i){
output[[paste("p", i, sep="") ]] <- renderPlot({
print(paste("Plot",dum$v))
x <- val[[paste0("p",i)]]
x <- ifelse(is.null(x),"NA",round(x,2))
par(mar=c(2,2,2,2))
plot(x=runif(20), y=runif(20), main=i, xlim=c(0,1), ylim=c(0,1), pch=21, bg="gray", cex=1.5)
if(is.numeric(x)) abline(v=x, col="blue")
rm(x)
})
})
})
}
shinyApp(ui, server)
Here is a working version of what you're trying to do:
library(shiny)
ui <- fluidPage(
sidebarPanel(
numericInput("num", "Plots:", 3)
),
mainPanel(
uiOutput("plots")
)
)
server <- function(input, output, session) {
obs <- list()
val <- reactiveValues()
observe({
lapply(seq(input$num), function(i){
output[[paste0("plot", i) ]] <- renderPlot({
click_id <- paste0("clk_p",i);
plot(x = runif(20), y = runif(20), main=i)
if (!is.null(val[[click_id]])) {
abline(v = val[[click_id]], col = "blue")
}
})
})
})
observe({
lapply(seq(input$num), function(i){
id <- paste0("clk_p",i);
if (!is.null(obs[[id]])) {
obs[[id]]$destroy()
}
val[[id]] <- NULL
obs[[id]] <<- observeEvent(input[[id]], {
cat('clicked ', id, ' ', input[[id]]$x, '\n')
val[[id]] <- input[[id]]$x
}, ignoreInit = TRUE)
})
})
output$plots <- renderUI({
lapply(seq(input$num), function(i) {
id <- paste0("plot", i)
plotOutput(id, height=200, width=200, click=paste0("clk_p",i))
})
})
}
shinyApp(ui,server)
A few main pointers for anyone who sees this in the future:
The main issue with the original code was that all the observers were registering only for the last ID. This is a bit of an advanced concept and has to do with the way environments in R work and because they were created in a for loop. The fix for this is to use lapply() instead of a for loop to create the observers
Another issue is that obs was overwriting the observers in the list, but the previous observers still exist and can still fire, so I added logic to destroy() existing observers.
One of the most important rules in shiny is to not place side effects inside reactives (plotInput has side effects) so I rewrote the code in a way that avoids that

Clicking through plots in Shiny app

In my Shiny app, I use a for loop to make different plots and I would like the user to be able to click through each one. How would I achieve this as it currently just goes to the final plot?
library(shiny)
server <- function(input, output, session) {
# data
v <- c(9,8,7,8,9,5,6,7,4,3)
w <- c(3,4,2,3,3,3,2,3,4,5)
x <- c(1,3,4,6,2,4,6,8,6,3)
y <- c(4,5,2,4,2,1,2,5,7,8)
z <- c(5,9,8,6,4,6,8,9,6,7)
df <- data.frame(v, w, x, y, z)
# initial plot that will allow user to change parameters (haven't implemented yet)
output$plot <- renderPlot(plot(df[[1]],df[[2]]))
# wait until the button is triggered
observeEvent(input$run, {
for (i in 5){
output$plot <- renderPlot(plot(df[[1]],df[[i]], main = i))
}
})
}
ui <- fluidPage(
actionButton("run", "Generate"),
plotOutput("plot")
)
shinyApp(ui = ui, server = server)
You just need to use the variable that will maintain the count for each click:
library(shiny)
server <- function(input, output, session) {
# data
v <- c(9,8,7,8,9,5,6,7,4,3)
w <- c(3,4,2,3,3,3,2,3,4,5)
x <- c(1,3,4,6,2,4,6,8,6,3)
y <- c(4,5,2,4,2,1,2,5,7,8)
z <- c(5,9,8,6,4,6,8,9,6,7)
df <- data.frame(v, w, x, y, z)
# initial plot that will allow user to change parameters (haven't implemented yet)
output$plot <- renderPlot(plot(df[[1]],df[[2]]))
count<-0 # This is the counter which keeps track on button count
observeEvent(input$run, {
count <<- count + 1 # Increment the counter by 1 when button is click
if(count<6){
# Draw the plot if count is less than 6
output$plot <- renderPlot(plot(df[[1]],df[[count]],main = count))
}
else{
# Reset the counter if it is more than 5
count <- 0
}
})
}
ui <- fluidPage(
actionButton("run", "Generate"),
plotOutput("plot")
)
shinyApp(ui = ui, server = server)
Instead of using for loop you can try by directly using the action button click count i.e. input$run.The following code will generate the plots one by one till the click count is less than or equal to 5 and then returns to initial plot as soon the as click count exceeds 5. You can modify the else statement as per your wish.
observeEvent(input$run, {
if(input$run <= 5){
output$plot <- renderPlot(plot(df[[1]],df[[input$run]], main = input$run))
}else output$plot <- renderPlot(plot(df[[1]],df[[2]]))
})
Hope this might be helpful

R Shiny dataTableOutput don't work if using loop of reactive image

I'm using R software and EBImage package to image analysis.
I have an image with several frames that i load locally.
I want to show a dataTable output of features (compute.Features.basic). I want to bind each dataframe (each frame) to a total one. But it doen't work properly. If i do the same with a non reactive image, it works ok. So, it's something about the reactive image.
Here is an example:
## Only run examples in interactive R sessions
if (interactive()) {
shinyApp(
ui = fluidPage(
fileInput('input.image',""),
dataTableOutput("tabledata")
),
server = function(input, output) {
# Load Image
img <- reactive({
if (is.null(input$input.image))
return(NULL)
x <- readImage(input$input.image$datapath)
})
# Modified Image
img_bw <- reactive({
req( img() )
x <- img()
x <- gblur(x, sigma = 5)
x <- thresh(x, w = 15, h = 15, offset = 0.05)
x <- bwlabel(x)
})
# Create empty datagrame
dt <- data.frame()
# Calculate cell features for each frame and rbind frame-dataframe to total
data <- reactive({
for (i in 1:numberOfFrames(img())){
dt <- rbind(dt, computeFeatures.basic(img_bw()[,,i], img()[,,i]))
}
})
# Otuput
output$tabledata <- renderDataTable(data())
}
)
}
Ok, I solved simply introducing the creation of temporal dataframe into the reactive function and calling the dataframe:
## Only run examples in interactive R sessions
if (interactive()) {
shinyApp(
ui = fluidPage(
fileInput('input.image',""),
dataTableOutput("tabledata")
),
server = function(input, output) {
# Load Image
img <- reactive({
if (is.null(input$input.image))
return(NULL)
x <- readImage(input$input.image$datapath)
})
# Modified Image
img_bw <- reactive({
req( img() )
x <- img()
x <- gblur(x, sigma = 5)
x <- thresh(x, w = 15, h = 15, offset = 0.05)
x <- bwlabel(x)
})
# Calculate cell features for each frame and rbind frame-dataframe to total
data <- reactive({
# Create empty datagrame
dt <- data.frame()
# Use dt dataframe
for (i in 1:numberOfFrames(img())){
dt <- rbind(dt, computeFeatures.basic(img_bw()[,,i], img()[,,i]))
}
# Print dt dataframe
dt
})
# Otuput
output$tabledata <- renderDataTable(data())
}
)
}

Resources