R Shiny Loop to Display Multiple Plots

Click through multiple plots within each iteration in a Shiny App

Like Gregor de Cillia said, you can use different plots.

Edit 1: Show only plot1

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)
# Make two different plots here
output$plot1 <- renderPlot(plot(df[[1]],df[[2]]))
output$plot2 <- renderPlot(plot(df[[3]],df[[1]]))

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
# Update both these plots
output$plot1 <- renderPlot(plot(df[[1]],df[[count]],main = count))
# Generate this plot but do not output
plot2 <- reactive({
renderPlot(plot(df[[3]],df[[count]],main = count))
})
}
else{
# Reset the counter if it is more than 5
count <- 0
}
})
}

ui <- fluidPage(
actionButton("run", "Generate"),
# Output both plots separately
plotOutput("plot1")
# Don't show this
# plotOutput("plot2")
)

shinyApp(ui = ui, server = server)

Edit 2: Based on updated understanding, here's how you can achieve whats required:

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$plot1 <- 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 plot if count < 6

# In every iteration, first click is an odd number,
# for which we display the first plot

if(count %% 2 != 0){
# Draw first plot if count is odd
output$plot1 <- renderPlot(plot(df[[1]],df[[count]],main = count))
}
else{
# Second click is an even number,
# so we display the second plot in the previous iteration,
# hence df[[count - 1]]

# Draw second plot if count is even
output$plot1 <- renderPlot(plot(df[[3]],df[[count-1]],main = count))
}
}

else{
# Reset the counter if it is more than 5
count <- 0
}
})
}

ui <- fluidPage(
actionButton("run", "Generate"),
# Output plot
plotOutput("plot1")
)

shinyApp(ui = ui, server = server)

how to generate output for multi-plots within a loop in shiny app?

So the problem you seem to be running into is related to delayed evaluation. The commands in renderPlot() are not executed immediately. They are captured and run when the plot is ready to be drawn. The problem is that the value of name is changing each iteration. By the time the plots are drawn (which is long after the for loop has completed), the name variable only has the last value it had in the loop.

The easiest workaround would probably be to switch from a for loop to using Map. Since Map calls functions, those functions create closures which capture the current value of name for each iteration. Try this

# sample data
set.seed(15)
cname <- letters[1:3]
data <- data.frame(
vals = rpois(10*length(cname),15),
customer = rep(cname,each=10)
)

runApp(list(server=
shinyServer(function(input, output){
Map(function(name) {
output[[name]] <- renderPlot({
barplot(data[data$customer==name,1])
})
},
cname)
})
,ui=
shinyUI(fluidPage(
plotOutput("a"),
plotOutput("b"),
plotOutput("c")
))
))

Using Shiny to create multiple plots, one of which requires parameters estimated using the data

This is a working solution. I have not attempted to use req and the like and leave that as an exercise for the reader. Why does OP's code fail?

OP is attempting to access reactive elements within functions for example in this line: n()*log(sigma)+ sum((x()-mu)^2/sigma^2) - sum(csi0(alpha*(x()-mu)/sigma)) # negative log-likelihood.

Here, the function ll attempts to access reactive object x() which is not compatible with the shiny reactivity philosophy. Similar ideas can be seen in functions dsknorm and dnorms. Converting these to reactive elements/objects fixes the issue. However, it is still necessary to use req and friends to "await" processes. Otherwise, you get the error "Missing Value where true/false is needed".

NOTE I have commented out some code from plot2 that is not following ggplot2 best practices. This is the code in question. We cannot add a data.frame object to a ggplot2 object.

 scale_color_manual(name="distribution", values=c(skewed_normal="blue", normal="red")) +
# daily_returns() %>%
# ggplot(aes(x = daily.returns)) +geom_density()+
# theme_classic() +
# labs(x = "Daily returns") +
# ggtitle(paste("Daily Returns for", ticker)) +
# scale_x_continuous(breaks = seq(min(x)-.03, max(x)+0.03, .02),
# labels = scales::percent)
   # install.packages(c("tidyquant", "timetk", "moments", "stats4"))
library(tidyquant)
library(timetk)
library(moments)
library(stats4)
library(shiny)
library(ggplot2)
library(dplyr)

stocknames <-c("AAPL","TSLA", "GME", "GOOG", "AMGN")

ui <- fluidPage(
titlePanel("Fitting a skewed normal to stock returns"),

sidebarLayout(position="left",
sidebarPanel("Choose a stock from the list",
selectInput("tick", label = "", choices=stocknames)
),
mainPanel("main panel", fluidRow(
splitLayout(style="border:1px solid silver:", cellWidths = c(400,400),
plotOutput("plot1"),
plotOutput("plot2")
)
)
)
)
)

server <- function(input, output) {
stockDatax <- reactive({
tq_get(input$tick, get="stock.prices", from ="2015-01-01")
})
daily_returns <- reactive({
stockDatax() %>%
tq_transmute(select = adjusted, # this specifies which column to select
mutate_fun = periodReturn, # This specifies what to do with that column
period = "daily")

})


plt1 <- reactive({daily_returns() %>%
ggplot(aes(x = date, y = daily.returns )) +
geom_line() +
theme_classic() +
labs(x = "Date", y = "daily returns") +
ggtitle(paste("Daily Returns for ", input$tick)) +
scale_x_date(date_breaks = "years", date_labels = "%Y") +
scale_y_continuous(breaks = seq(-0.5,0.6,0.05),
labels = scales::percent)

})
# This is where the part related to plot 2 begins and where the error(s) lie...

x<- reactive({pull(daily_returns(),2)})
n <- reactive({length(x()) })
# need this function for log-likelihood function immediately below.
csi0 <-function(x){
y<- log(2*dnorm(x))
return(y)
}

est <- reactive({
csi0 <-function(x){
y<- log(2*dnorm(x))
return(y)
}
ll<-function(mu, sigma, alpha){
n()*log(sigma)+ sum((x()-mu)^2/sigma^2) -
sum(csi0(alpha*(x()-mu)/sigma)) # negative log-likelihood
}
mle(minuslogl=ll, start=list(mu=0, sigma=1, alpha=-0.1))
})
# define likelihood function for skewed normal

# This uses mle to find the mle solutions numerically.
# est<-
# summary(est)
# extract the solutions to use them individually in what follows
m <- reactive({ unname(est()@coef[1]) })
s <- reactive({ unname(est()@coef[2]) })
a <- reactive({ unname(est()@coef[3]) })
#
# symbolic probability density function that uses the parameters just estimated above.
# dsknorm <- function(x){
# y <- 2/s*dnorm((x-m())/s())*pnorm(a()*(x-m())/s())
# return(y)
# }
# # doing the same for the normal distribution.
# MLE for normal distribution
m1 <- reactive({mean(x()) })
s1 <- reactive({sd(x()) })

ylistnormdf <- reactive(
print(sapply(grid_x(), function(x) dnorm(x, m1(), s1())))
)

ylistskdf <- reactive(
print(sapply(grid_x(),
function(x) 2/s()*dnorm((x-m())/s())*pnorm(a()*(x-m())/s()) ))
)


grid_x <- reactive({ seq(min(x())-.03, max(x())+0.03, .005) })
# ylistskdf <-reactive({sapply(grid_x(), dsknorm)})
# ylistnormdf <-reactive({sapply(grid_x(), dnorms)})
# create a dataframe to use for plotting
dataToPlot <- reactive({data.frame(grid_x(), ylistnormdf(), ylistskdf()) })
plt2 <- reactive({ggplot(data=dataToPlot(), aes(x=grid_x())) +
geom_line(aes(y=ylistskdf(),colour="skewed_normal")) +
geom_line(aes(y=ylistnormdf(),colour="normal")) +
scale_color_manual(name="distribution", values=c(skewed_normal="blue", normal="red"))
# daily_returns() %>%
# ggplot(aes(x = daily.returns)) +geom_density()+
# theme_classic() +
# labs(x = "Daily returns") +
# ggtitle(paste("Daily Returns for", ticker)) +
# scale_x_continuous(breaks = seq(min(x)-.03, max(x)+0.03, .02),
# labels = scales::percent)
})

output$plot1 <-renderPlot({plt1()})
output$plot2 <-renderPlot({plt2()})
}
shinyApp(ui, server)

Result

Sample Image

How to print multiple plots using a single renderPlot() function in shiny app?

ui

shinyUI(fluidPage(
titlePanel(title = h4("proportion graphs", align="center")), sidebarLayout( sidebarPanel( ),

mainPanel(
# create a uiOutput
uiOutput("plots")
)
)

))

Sever

shinyServer(

function(input, output) {

l<- reactive({
f<- list(`0` = structure(list(X70 = "D", X71 = "C", X72 = "C", X73 = "A", X74 = "B", X75 = "C", X76 = "D", X77 = NA_character_, X78 = "B", X79 = "D", X80 = "C", Q = 1), row.names = 32L, class = "data.frame"), `1` = structure(list(X70 = c("D", "B", "D", "D", "B", "D", "D", "D", "D", "D", "D"), X71 = c("B", "B", "C", "C", "C", NA, "D", "B", "C", "A", "C"), X72 = c("A", "A", "C", "B", "C", "C", "C", "C", "D", "B", NA), X73 = c("B", "C", "C", "B", "C", "D", "A", "B", "C", "C", NA), X74 = c("B", "A", "C", "D", "B", "D", NA, "D", "D", "D", NA), X75 = c("C", "C", "B", "C", "D", "D", "C", "A", "C", "C", "C"), X76 = c("D", "A", "D", "B", "D", "C", "D", "A", "A", "D", "B"), X77 = c("D", "C", "B", "B", "B", "C", "B", "B", "B", "B", "D"), X78 = c("B", "C", "C", "B", "A", "A", "C", "B", "A", "C", NA), X79 = c("C", "C", NA, NA, "D", "A", "A", "A", "D", "A", "D"), X80 = c("B", "A", NA, NA, "B", "C", "B", NA, "B", "C", "A"), Q = c(2, 2, 1, 1, 2, 2, 1, 1, 4, 3, 1)), row.names = c(8L, 10L, 12L, 17L, 25L, 27L, 28L, 33L, 35L, 38L, 45L), class = "data.frame"), `2` = structure(list(X70 = c("D", "D", "D", "B", "D", "C", "D", "D", "D", "D", "D", "D"), X71 = c("A", "B", "C", "C", "A", "A", "C", "B", "C", "C", "D", "B"), X72 = c("D", "C", "D", "A", "A", "C", "D", "C", NA, "D", "C", "B"), X73 = c("B", "D", "D", "C", "B", "D", "D", "D", NA, NA, "C", "A"), X74 = c("D", "C", "B", "D", "C", "B", "C", "C", "B", NA, "C", "D"), X75 = c("B", "C", "C", "C", NA, "C", "B", "C", "C", "C", "B", "C"), X76 = c("A", "D", "D", "D", NA, "D", "D", "A", "D", "D", "D", "D"), X77 = c("B", "B", "D", "B", NA, "B", "D", "B", "B", "B", "B", "B"), X78 = c("C", "D", "C", "B", NA, "D", "C", "C", "B", "D", "C", NA), X79 = c("A", "D", "D", "D", NA, "D", "A", NA, "A", "D", "B", NA), X80 = c(NA, "C", "C", "A", NA, "C", "C", NA, "B", "C", "C", NA), Q = c(2, 3, 3, 1, 3, 1, 2, 2, 1, 2, 2, 1)), row.names = c(4L, 5L, 6L, 11L, 15L, 16L, 21L, 22L, 26L, 37L, 39L, 43L), class = "data.frame"), `3` = structure(list(X70 = c("A", "A", "D", "C", "D", "D", "D", "D", NA, "D", "D", "D"), X71 = c("B", "C", "D", "D", "C", "C", "B", "C", "C", "C", "A", "D"), X72 = c("B", "C", NA, "B", "A", "C", "B", "A", "C", "C", "D", "B"), X73 = c(NA, "C", "C", "A", "D", "C", "A", "A", "D", "B", "D", "B"), X74 = c(NA, "C", "D", "B", "A", "D", NA, "D", "B", "A", "D", "A"), X75 = c(NA, "C", "B", "D", "C", "C", "C", "C", "C", "B", "C", "D"), X76 = c(NA, "D", "A", "B", "A", "D", "D", "D", "D", "D", "D", "D"), X77 = c(NA, "B", "B", "B", "C", "B", "A", "B", NA, "C", "D", "D"), X78 = c(NA, "C", "C", "B", "C", "B", "A", "C", "D", "C", "C", "C"), X79 = c(NA, "D", "D", NA, "B", "D", "A", "D", "A", "D", "D", "A"), X80 = c(NA, "C", "C", NA, "D", "C", "C", "C", "C", "C", "B", "C"), Q = c(2, 2, 2, 2, 4, 2, 4, 4, 4, 3, 3, 2)), row.names = c(2L, 13L, 14L, 18L, 19L, 20L, 29L, 30L, 34L, 36L, 41L, 44L), class = "data.frame"), `4` = structure(list(X70 = c("D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D"), X71 = c("A", NA, "A", "B", "C", "A", "A", "C", "B", "C", "C", "C"), X72 = c("B", "C", "C", "C", NA, "C", "B", "A", "C", "B", NA, "A"), X73 = c(NA, "D", "D", "D", "B", "D", "D", "D", "C", "A", "A", "C"), X74 = c("C", "A", "C", "D", "C", "C", "A", "A", "C", "D", "D", "D"), X75 = c("C", "C", "C", "C", "C", "C", "C", "C", "C", "D", "C", "C"), X76 = c("D", "D", "D", "D", "D", "D", "D", "D", "A", "D", "D", "A"), X77 = c(NA, "B", "D", "B", NA, "B", "B", "B", "C", "D", NA, "C"), X78 = c("C", "C", "C", "C", "A", "A", "C", "A", "C", "C", "C", "C"), X79 = c("D", "D", "A", "D", "D", "A", "D", "D", "A", "D", "C", "C"), X80 = c("C", "C", "C", "C", NA, "C", "C", "C", "C", "C", "C", "A"), Q = c(2, 4, 4, 3, 2, 4, 2, 4, 1, 1, 2, 4)), row.names = c(1L, 3L, 7L, 9L, 23L, 24L, 31L, 40L, 42L, 46L, 47L, 48L), class = "data.frame"))
})



u <- reactive({
u <- c("D", "B", "C", "A")
})

# reactive expression to process data
out <- reactive({
l <- l()
u <- u()

lapply(l, function(dat)
asplit(as.data.frame(t(sapply(dat, function(x)
proportions(table(factor(unlist(x), levels = u)))))), 1) ) %>%
transpose %>%
map(bind_rows, .id = 'grp')
})

# render UI
output$plots <- renderUI({

lapply(1:length(out()[-1]), function(i) {
# creates a unique ID for each plotOutput
id <- paste0("plot_", i)
plotOutput(outputId = id)

# render each plot
output[[id]] <- renderPlot({
x <- out()[[i]][-1]
matplot(x, type = "l", col = 1:4, xaxt = "n")
axis(side=1, at=1:4, labels=colnames(x))
legend("topleft", legend = colnames(x), fill = 1:4)
})

})

})

} )

R Shiny - Display multiple plots selected with checkboxGroupInput

There are several ways to to do what you want. In shiny you can use renderUI. See code below.

library(ggplot2)
library(shiny)

CountPlotFunction <- function(MyData)
{
MyPlot <- ggplot(data = MyData, aes(x = MyData)) +
geom_bar(stat = "count", aes(fill = MyData)) +
geom_text(stat = "count", aes(label = ..count..)) +
scale_x_discrete(drop = FALSE) +
scale_fill_discrete(drop = FALSE)
return(MyPlot)
}

# The data
var1 <- c("Russia","Canada","Australia","Australia","Russia","Australia","Canada","Germany","Australia","Canada","Canada")
var2 <- c("UnitedStates","France","SouthAfrica","SouthAfrica","UnitedStates","SouthAfrica","France","Norge","SouthAfrica","France","France")
var3 <- c("Brazil","Colombia","China","China","Brazil","China","Colombia","Belgium","China","Colombia","Colombia")
df <- data.frame(var1, var2, var3)

# The Shiny app
Interface <-
{
fluidPage(
sidebarPanel(
checkboxGroupInput(inputId = "Question",
label = "Choose the question",
choices = colnames(df),
selected = colnames(df)[1])),
mainPanel(
uiOutput('ui_plot')
)
)
}

Serveur <- function(input, output)
{
# gen plot containers
output$ui_plot <- renderUI({
out <- list()
if (length(input$Question)==0){return(NULL)}
for (i in 1:length(input$Question)){
out[[i]] <- plotOutput(outputId = paste0("plot",i))
}
return(out)
})

# render plots
observe({
for (i in 1:3){
local({ #because expressions are evaluated at app init
ii <- i
output[[paste0('plot',ii)]] <- renderPlot({
if ( length(input$Question) > ii-1 ){
return(CountPlotFunction(MyData = df[input$Question[[ii]]]))
}
NULL
})
})
}

})

}

shinyApp(ui = Interface, server = Serveur)

R Shiny: Adding to plot via a loop

You can use reactiveTimer to do that. I have modified the server part of your code. In the code below I have set the timer for two seconds so that the plot updates every two seconds.

  server <- function(input, output) {

autoInvalidate <- reactiveTimer(2000)
plot1 <- NULL

output$plot1 <- renderPlot({
plot1 <<- ggplot(data, aes(x=x, y=y)) + geom_point(colour="red") + theme_bw()
plot1 <<- plot1 + geom_vline(xintercept = mean(data$x), size=1.1, colour="red")
plot1
})

observeEvent(input$button,{

output$plot1 <- renderPlot({
autoInvalidate()
data$sampled <- "red"
sample.rows <- sample(data$ID, 20, replace = F)
data$sampled[sample.rows] <- "green"

plot1 <<- plot1 + geom_point(x=data$x, y=data$y, colour=data$sampled, size=2)

sample.mean.x <- mean(data$x[sample.rows])

plot1 <<- plot1 + geom_vline(xintercept = sample.mean.x, colour="green")

plot1

})
})
}

[EDIT]:

As you wanted the loop to be run only 20 times I have modified the code with the help of the answer in this link so that the reactive timer is run only till the count is 20. Here is the code that you need to add from the link:

  invalidateLaterNew <- function (millis, session = getDefaultReactiveDomain(), update = TRUE) 
{
if(update){
ctx <- shiny:::.getReactiveEnvironment()$currentContext()
shiny:::timerCallbacks$schedule(millis, function() {
if (!is.null(session) && session$isClosed()) {
return(invisible())
}
ctx$invalidate()
})
invisible()
}
}

unlockBinding("invalidateLater", as.environment("package:shiny"))
assign("invalidateLater", invalidateLaterNew, "package:shiny")

Here is the server code for it:

server <- function(input, output, session) {

count = 0
plot1 <- NULL

output$plot1 <- renderPlot({
plot1 <<- ggplot(data, aes(x=x, y=y)) + geom_point(colour="red") + theme_bw()
plot1 <<- plot1 + geom_vline(xintercept = mean(data$x), size=1.1, colour="red")
plot1
})

observeEvent(input$button,{
count <<- 0
output$plot1 <- renderPlot({

count <<- count+1
invalidateLater(1500, session, count < 20)
data$sampled <- "red"
sample.rows <- sample(data$ID, 20, replace = F)
data$sampled[sample.rows] <- "green"

plot1 <<- plot1 + geom_point(x=data$x, y=data$y, colour=data$sampled, size=2)

sample.mean.x <- mean(data$x[sample.rows])

plot1 <<- plot1 + geom_vline(xintercept = sample.mean.x, colour="green")

plot1

})
})

}


Related Topics



Leave a reply



Submit