How to make the plot reactive to user input? - r

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)
})

Related

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)
)
}
}

Object sharing between renderPrint and renderPlot

In the following example, the R object fit is created in shiny::renderPrint but not in renderPlot. Thus plot done for print() but not plot().
In actual phase, fit is an fitted model object generated by rstan:sampling() and it takes very long time, so I won't execute it twice in both renderPrint and renderPlot. Is there any idea ? I am very beginner of Shiny.
library(shiny)
ui <- fluidPage(
mainPanel(
shiny::sliderInput("aaa",
"aaa:",
min = 1, max = 11111, value = 5),
shiny::plotOutput("plot"),
shiny::verbatimTextOutput("print") )
)
server <- function(input, output) {
output$print <- shiny::renderPrint({
fit <- input$aaa*100 # First creation of object,
# and we use it in the renderPlot.
# So, we have to create it twice even if it is exactly same??
# If possible, I won't create it
# In the renderPlot, twice.
print(fit)
})
output$plot <- shiny::renderPlot({
# The fit is again created
# If time to create fit is very long, then total time is very heavy.
# If possible, I do not want to make fit again.
fit <- input$aaa*100 #<- Redundant code or I want to remove it.
plot(1:fit)
})
}
shinyApp(ui = ui, server = server)
Edit
To avoid a duplicate code of making object, I use the following, then it goes well. Thank you #bretauv.
library(shiny)
ui <- fluidPage(
mainPanel(
shiny::sliderInput("aaa",
"aaa:",
min = 1, max = 11111, value = 5),
shiny::plotOutput("plot"),
shiny::verbatimTextOutput("print") )
)
server <- function(input, output) {
########## Avoid duplicate process ###################
test <- reactive({input$aaa*100})
#####################################################################
output$print <- shiny::renderPrint({
# fit <- input$aaa*100 # No longer required
print(test())
})
output$plot <- shiny::renderPlot({
# fit <- input$aaa*100 # No longer required
plot(1:test())
})
}
shinyApp(ui = ui, server = server)
if you want not to repeat fit, try to put the fit expression in a reactive function such as : test <- reactive({input$aaa*100}) and then call it in output functions with test()

shiny plot doesn't show up

I'm trying to make my first shiny app, in which a number of values are taken in, a number of calculations are performed (which depend on both values in the input and those in the server function), and then the outputs plotted. However, I can either no plot at all in the output (as in the sample below), or can just get the 1:1 line and not my data to show up. I'm not entirely sure where to begin troubleshooting, but I think I have problems with both making the calculations and feeding them into the plot function here. If you have any pointers it would be greatly appreciated.
Here is a simplified version of my app:
library(shiny)
require(ggplot2)
ui<-fluidPage(
sidebarLayout(
sidebarPanel(
titlePanel("mytitle"),
sliderInput(inputId= "min", label="minratio", value=0, min=0, max=0.499),
sliderInput(inputId= "max", label="maxratio", value=1, min=0.5, max=1)
),
mainPanel(
textOutput("valoutput"),
plotOutput("distPlot",width="100%"))
)
)
server<-function(input, output){
BS = function(x) {
mini=x[1]; maxi=x[2]
ratio <-seq(from=mini,to=maxi, by=0.01)
total<-30*ratio+3
res = c(ratio,total)
}
data<-reactive({as.data.frame("mini"=as.numeric(input$min), "maxi"=as.numeric(input$max))})
output$valoutput <- renderText({BS(data())[1]})
output$distplot <- renderPlot({
d1=BS(data())[1]
d2=BS(data())[2]
ggplot()+geom_abline(intercept = 0, slope=1, colour="grey50")+geom_point(aes(x=d1, y=d2))
}, height = 350, width = 600)
}
shinyApp(ui=ui, server=server)
Thanks so much!
Your BS function is not correct. Change it with this one (with as.numeric). Otherwise x[1]/x[2] will be data.frames and will throw an error in seq(). Alternatively you could also use double brackets, like x[[1]].
BS = function(x) {
mini=x[1]; maxi=x[2]
ratio <-seq(from=as.numeric(mini),to=as.numeric(maxi), by=0.01)
total<-30*ratio+3
res = c(ratio,total)
}
and in your ui your plot output name is not correct. It should be distplot not distPlot.
And you dont need to call as.data.frame, just data.frame does the right job, as you want to create a data.frame and not convert an object.
data <- reactive({
data.frame("mini"=as.numeric(input$min),
"maxi"=as.numeric(input$max))
})

Return reactive output from user-defined function in shiny

I am relatively new to shiny. I created an NBA win-probability model a few weeks ago and have been trying to create a shiny app that will generate the output from my model for which I have created a user-defined function.
In my user interface I want a place to enter numeric input value for "Home Points", "Away Points", and "Time Remaining". Once, values have been entered for these values I want the user to click an action button. After the action button is clicked I simply want the app to display the output from my function in the main panel. However, I am unable to figure out how to get this to work.
Here is my code:
library(shiny)
# Define UI for application that calculates win probability
ui <- fluidPage(
# Application title
titlePanel("Win Probability"),
# Sidebar layout with inputs and output definitions
sidebarLayout(
#sidebar panel for inputs
sidebarPanel(
#Add numeric input for home team points
numericInput(inputId = "home.pts", label = h3("Home Points"), value = 0),
#Add numeric input for away team points
numericInput(inputId = "away.pts", label = h3("Away Points"), value = 0),
#Add numeric input for time remaining in fourth quarter
numericInput(inputId = "time", label = h3("Time Remaining"), value = 0),
#Add action buttion
actionButton("goButton","Apply")),
# Show output
mainPanel(
verbatimTextOutput("win_prob")
)))
win_prob <- function(time, home.pts, away.pts) {
#calculate point difference
diff <- home.pts - away.pts
#Store intercept and betas
intercept <- 0.09564935
b_time <- 0.01087832
b_diff <- 0.5243092
b_interact <- -0.03066952
#calculate and store logit
logit <- intercept + (time*b_time) + (diff*b_diff) +
((time*diff)*b_interact)
#function to change logit to probability
logit2prob <- function(logit) {
odds <- exp(logit)
prob <- odds/(1+odds)
}
#Store probability
prob <- logit2prob(logit)
prob
}
# Define server to return win probability
server <- function(input, output) {
#Store reactive values
home.pts <- reactive({input$home.pts})
away.pts <- reactive({input$away.pts})
time <- reactive({input$time})
output$win_prob <- renderPrint({win_prob(reactive({input$home.pts}),
reactive({input$away.pts}), reactive({input$time}))})
}
# Run the application
shinyApp(ui = ui, server = server)
If someone can please help me I would greatly appreciate it!
Thank you!
Using reactivity
server <- function(input, output) {
#Store reactive values
home.pts <- reactive({input$home.pts})
away.pts <- reactive({input$away.pts})
time <- reactive({input$time})
output$win_prob <- renderPrint({win_prob(home.pts(), away.pts(), time())})
}
Using ObserveEvent
server <- function(input, output) {
data <- reactiveValues()
observeEvent(input$goButton,{
data$home.pts <- input$home.pts
data$away.pts <- input$away.pts
data$time <- input$time
})
output$win_prob <- renderPrint({
req(data$home.pts) #to delay displaying result until user press Apply
win_prob(data$home.pts,data$away.pts, data$time)})
}
Now you can see the deference between the two approaches
Well you don't need to store all your inputs in reactive values. They already update themselves. When you have an actionButton, the best way to trigger an event from the click on the button is to use observeEvent. If I understood well, I would rewrite your server function like this:
# Define server to return win probability
server <- function(input, output) {
observeEvent(input$goButton, {
output$win_prob <- renderPrint({
win_prob(input$home.pts,
input$away.pts,
input$time)
})
})
}

Plot animation in Shiny with rgl

I just started using Shiny and I'm trying to plot an "animation" using lapply or a for loop in Shiny, but I can't seem to get the correct output. When using base R, my code works.
My data is not set as a time series, but each row represents an observation in time.
Also, I'm willing to use another package (other than rgl), if necessary.
And, I'm making use of some of the code described here, including the javascript file rglwidgetaux.js .
global.R
library(rgl)
# MAIN FUNCTION
movement.points<-function(DATA,time.point,CONNECTOR){
DATA.time<-DATA[time.point,]
DATA.time<-matrix(DATA.time,c(3,4),byrow = TRUE)
x<-unlist(DATA.time[,1])
y<-unlist(DATA.time[,2])
z<-unlist(DATA.time[,3])
next3d(reuse=FALSE)
points3d(x=x,y=y,z=z,size=6,col="blue")
segments3d(x=c(x,x[CONNECTOR]),y=c(y,y[CONNECTOR]),z=c(z,z[CONNECTOR]),col="red")
Sys.sleep(0.05)
}
############################################################################
Using the function above, this works:
# INITIAL POSITION
rgl.viewpoint(userMatrix=rotationMatrix(0,2,0,0))
U <- par3d("userMatrix")
par3d(userMatrix = rotate3d(U, pi, 1,1,2))
movement.points(DATA=DATA.position,time.point=1,CONNECTOR=CONNECTOR)
# # ANIMATION (THIS IS WHAT I WANT TO RUN IN SHINY)
lapply(1:dim(DATA.position),movement.points,DATA=DATA.position,CONNECTOR=CONNECTOR)
But I can't get the "animation" (the lapply) to work in Shiny. This is what I've done:
ui.R
library(shiny)
library(rgl)
library(htmlwidgets)
library(jsonlite)
rglwgtctrl <- function(inputId, value="", nrows, ncols) {
# This code includes the javascript that we need and defines the html
tagList(
singleton(tags$head(tags$script(src = "rglwidgetaux.js"))),
tags$div(id = inputId,class = "rglWidgetAux",as.character(value))
)
}
ui <- fluidPage(
rglwgtctrl('ctrlplot3d'),
rglwidgetOutput("plot3d"),
actionButton("queryumat", "Select initial position"),
tableOutput("usermatrix"),
actionButton("regen", "Visualize sequence with new position")
,rglwidgetOutput("plot3d2")
)
server.R
source('global.R', local=TRUE)
library(shiny)
library(rgl)
library(jsonlite)
library(htmlwidgets)
options(shiny.trace=TRUE)
server <- function(input, output, session)
{
# DATA
DATA.position<-c(0.099731,-0.509277,3.092024,1,0.173340,-0.869629,3.142025,1,0.197632,-0.943848,3.099056,1,
0.099315,-0.509114,3.094403,1,0.173125,-0.868526,3.140778,1,0.196985,-0.943108,3.100157,1,
0.099075,-0.509445,3.094318,1,0.172445,-0.869610,3.138849,1,0.196448,-0.943238,3.100863,1,
0.097668,-0.508197,3.090442,1,0.172319,-0.869749,3.138942,1,0.195357,-0.943346,3.102253,1,
0.096432,-0.507724,3.087681,1,0.172151,-0.870230,3.139060,1,0.193886,-0.943752,3.103878,1,
0.095901,-0.508632,3.086148,1,0.172345,-0.870636,3.139181,1,0.193134,-0.943644,3.107753,1,
0.093076,-0.513129,3.082425,1,0.173721,-0.874329,3.139272,1,0.188041,-0.949220,3.111685,1,
0.092158,-0.513409,3.082376,1,0.173221,-0.876358,3.141781,1,0.188113,-0.949724,3.111405,1,
0.091085,-0.513667,3.082308,1,0.173626,-0.876292,3.140349,1,0.189704,-0.948493,3.108416,1,
0.089314,-0.514493,3.083489,1,0.173133,-0.876019,3.141443,1,0.189653,-0.947757,3.108083,1,
0.087756,-0.515289,3.084332,1,0.172727,-0.875819,3.141264,1,0.189452,-0.947415,3.108107,1,
0.085864,-0.515918,3.085951,1,0.172672,-0.876940,3.141271,1,0.190892,-0.946514,3.104689,1,
0.084173,-0.515356,3.087133,1,0.172681,-0.876866,3.140089,1,0.189969,-0.944275,3.100415,1,
0.065702,-0.518090,3.097703,1,0.172706,-0.876582,3.139876,1,0.189737,-0.944277,3.100796,1,
0.063853,-0.517976,3.099412,1,0.172821,-0.876308,3.139856,1,0.189682,-0.944037,3.100752,1,
0.062551,-0.518264,3.100512,1,0.172848,-0.874960,3.139102,1,0.190059,-0.942105,3.098919,1,
0.065086,-0.517151,3.098104,1,0.172814,-0.875237,3.138775,1,0.190539,-0.942204,3.098439,1,
0.064088,-0.517003,3.098001,1,0.172911,-0.874908,3.137694,1,0.190593,-0.942012,3.097417,1,
0.065648,-0.516077,3.094584,1,0.172581,-0.874648,3.137671,1,0.190480,-0.942432,3.098431,1,
0.068117,-0.516750,3.094343,1,0.172545,-0.874946,3.136352,1,0.190648,-0.942610,3.096850,1)
DATA.position<-matrix(DATA.position,c(20,12),byrow = TRUE)
CONNECTOR<-c(1,2,3)
#############################################
# THIS WORKS
# INITIAL POSITION MATRIX
observe({
input$queryumat
session$sendInputMessage("ctrlplot3d",list("cmd"="getpar3d","rglwidgetId"="plot3d"))
})
# USER POSITION MATRIX
# SELECTION
umat <-reactive({
shiny::validate(need(!is.null(input$ctrlplot3d),"User Matrix not yet queried"))
umat <- matrix(0,4,4)
jsonpar3d <- input$ctrlplot3d
if (jsonlite::validate(jsonpar3d)){
par3dout <- fromJSON(jsonpar3d)
umat <- matrix(unlist(par3dout$userMatrix),4,4) # make list into matrix
}
return(umat)
})
## SHOW POSITION
output$usermatrix <- renderTable({
umat()
})
# INITIAL IMAGE
scenegen <- reactive({
rgl.viewpoint(userMatrix=rotationMatrix(0,2,0,0))
U <- par3d("userMatrix")
par3d(userMatrix = rotate3d(U, pi, 1,1,2))
movement.points(DATA=DATA.position,time.point=1,CONNECTOR=CONNECTOR)
scene1 <- scene3d()
rgl.close() # make the app window go away
return(scene1)
})
output$plot3d <- renderRglwidget({ rglwidget(scenegen()) })
############################################################
# NOT WORKING
# Animation after selecting position
# 1st TRY
# scenegen2 <- eventReactive(input$regen,({
# par3d(userMatrix = umat())
# lapply(1:dim(DATA.position)[1],movement.points,DATA=DATA.position,CONNECTOR=CONNECTOR)
# scene2 <- scene3d()
# rgl.close() # make the app window go away
# return(scene2)
# })
# )
# output$plot3d2 <- renderRglwidget({ rglwidget(scenegen2()) })
# 2nd TRY
# output$plot3d2 <- eventReactive(input$regen,
# renderRglwidget({
# lapply(1:dim(DATA.position)[1],movement.points,DATA=DATA.position,CONNECTOR=CONNECTOR)
# scene2 <- scene3d()
# rgl.close() # make the app window go away
# return(scene2)
# })
# )
# 3rd TRY
# for (i in 1:(dim(DATA.position)[1])){
# scenegen2 <- eventReactive(input$regen,({
# par3d(userMatrix = umat())
# movement.points(DATA=DATA.position,time.point=i,CONNECTOR=CONNECTOR)
# scene2 <- scene3d()
# rgl.close() # make the app window go away
# return(scene2)
# })
# )
# output$plot3d2 <- renderRglwidget({ rglwidget(scenegen2()) })
# }
#4th TRY
observe({
input$regen
isolate({
for (i in 1:(dim(DATA.position)[1])){
par3d(userMatrix = umat())
movement.points(DATA=DATA.position,time.point=1,CONNECTOR=CONNECTOR)
scene2 <- scene3d()
rgl.close()
output$plot3d2 <- renderRglwidget({ rglwidget(scene2) })
}
})
})
}
Thanks.
I've found animations using Shiny are too slow: there's a lot of data passed from R to Javascript to show an rgl scene, and it takes too long for each frame update. You're better off using the techniques shown in the WebGL vignette based on playControl. Unfortunately these require you to precompute data for each animation frame, so aren't always available.

Resources