Dynamically change plots based on user input in Shiny - r

I'm trying to create a shiny app that generates plots based on the user selection of a subset of a loaded dataframe. For example, I have the following dataset:
library(shiny)
library(data.table)
df <- rbind(
data.table( cat = rep('X', 40), grp = rep(LETTERS[1:4], each=10), x = rep(1:10, times=4), y = rnorm(40) ),
data.table( cat = rep('Y', 30), grp = rep(LETTERS[1:3], each=10), x = rep(1:10, times=3), y = rnorm(30) ),
data.table( cat = rep('Z', 20), grp = rep(LETTERS[4:6], each=10), x = rep(1:10, times=2), y = rnorm(20) )
)
Based on the value for cat that the user selects in the UI, I want shiny to produce charts for each value of grp. So, if the user selects 'X', then there will be 4 plots produced; if they select 'Y' there will be three, and if they select 'Z' there will be 3.
I also want to specify how each chart is generated based on the value of grp. So if grp is A,D or E I want it produce a line plot, otherwise it should produce a scatterplot (only if that grp has that value of course).
Below is the code for my (broken) shiny app:
server <- function(input, output) {
rv <- reactiveValues(
i = NULL,
df = NULL
)
observe({ rv$i <- input$i })
observe({ rv$df <- df[cat == rv$i] })
output$test <- renderUI({
plotList <- lapply( LETTERS[1:6], function(x) plotOutput(x) )
do.call( tagList, unlist(plotList, recursive=FALSE))
})
for(i in LETTERS[1:6]){
local({
my_i <- i
output[[my_i]] <- renderPlot({
if( my_i %in% c('A','D','E')) {
with(rv$df[grp == my_i], plot(x,y, type='l'))
} else {
with(rv$df[grp == my_i], plot(x,y))
}
})
})
}
}
ui <- fluidPage(
titlePanel('Title'),
sidebarLayout(
sidebarPanel(
helpText('Select the Category you would like to view.'),
selectInput('i', 'Category', c('X','Y','Z'), selectize=TRUE)
),
mainPanel(
uiOutput('test')
)
)
)
shinyApp(ui, server)

A reproducible example can be found at the bottom.
A few hints:
1) Using reactive contexts:
In your for Loop at the bottom of the Server Code you are using the reactive variable rv, so you will have to run the Code in a reactive Content. So wrap it in observe().
2) Create a list of Outputs:
If I am not mistaken you used some of the Code in this answer: dynamically add plots to web page using shiny.
It is a good starting Point. For the part of the taglist it might be easier to simplify to:
output$test <- renderUI({
lapply(unique(rv$df$grp), plotOutput)
})
You can also add tagList(), but it is not necessary here,...
3) Correcting the sample data:
You might want to update the df variable:
data.table(cat = rep('Z', 20), grp = rep(LETTERS[4:6], each=10),
x = rep(1:10, times=2), y = rnorm(20) )
Here your have three letters, so you might change it to LETTERS[5:6] or update the other numbers.
Full reproducible example:
library(shiny)
library(data.table)
df <- rbind(
data.table( cat = rep('X', 40), grp = rep(LETTERS[1:4], each=10), x = rep(1:10, times=4), y = rnorm(40) ),
data.table( cat = rep('Y', 30), grp = rep(LETTERS[1:3], each=10), x = rep(1:10, times=3), y = rnorm(30) ),
data.table( cat = rep('Z', 30), grp = rep(LETTERS[4:6], each=10), x = rep(1:10, times=3), y = rnorm(30) )
)
server <- function(input, output) {
rv <- reactiveValues(
i = NULL,
df = NULL
)
observe({ rv$i <- input$i })
observe({ rv$df <- df[cat == rv$i] })
observe({
for(letter in unique(rv$df$grp)){
local({
let <- letter
output[[let]] <- renderPlot({
if( let %in% c('A','D','E')) {
with(rv$df[grp == let], plot(x, y, type='l'))
} else {
with(rv$df[grp == let], plot(x,y))
}
})
})
}
})
output$test <- renderUI({
lapply(unique(rv$df$grp), plotOutput)
})
}
ui <- fluidPage(
titlePanel('Title'),
sidebarLayout(
sidebarPanel(
helpText('Select the Category you would like to view.'),
selectInput('i', 'Category', c('X','Y','Z'), selectize=TRUE)
),
mainPanel(
uiOutput('test')
)
)
)
shinyApp(ui, server)

Related

R shiny foreach instead of double for loop

I have a fairly large simulation, that I currently run in Shiny using double for loop and it takes very long. I read about possibility of using foreach, but it does not work out, whatever I try, I and up in errors. Maybe some can spot the error and help me correct it?
app.R that runs (albeit very slowly (on real data) here with example data for reprex
require(shiny)
require(tidyverse)
require(foreach)
require(doMC)
registerDoMC()
options(cores = detectCores())
df <- data.frame(a=rnorm(n=26), b=1:26, c=100:125)
calc <- function(let=0.5, var1=0.1, var2=0.5){
df%>%
mutate(p1=ifelse(a<let,var1,0))%>%
mutate(p2=ifelse(a<let, var2,2))%>%
summarise(mean_b=mean(b*p1),
mean_c=mean(c*p2))
}
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Example"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput(inputId="selected_let",
label="LET",
value=0.5,
min=0,
max=1,
step=0.1),
submitButton("CALCULATE")
),
# Show a plot of the generated distribution
mainPanel(
h1(paste0("Table1")),
tableOutput("table_1"),
h1(paste0("Table2")),
tableOutput("table_2")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
data <- reactive({
data <- data.frame()
for (i in seq(0,1,by=0.1)) {
for (j in seq(0,1,by=0.1)) {
tmp <- calc(let = input$selected_let, var1 = i, var2 = j)
tmp_df <- data.frame(var1=i,
var2=j,
mean_b=tmp$mean_b,
mean_c=tmp$mean_c)
data <- rbind(data, tmp_df)
}
}
return(data)
})
output$table_1 <- renderTable({
data()%>%
select(var1,var2,mean_b)%>%
spread(var2, mean_b)
})
output$table_2 <- renderTable({
data()%>%
select(var1,var2,mean_c)%>%
spread(var2, mean_c)
})
}
# Run the application
shinyApp(ui = ui, server = server)
My goal was to change the data <-... part with foreach package and as my PC runs on UNIX I use the doMC.
to be replaced with:
data <- reactive({
foreach(i=rep(seq(0,1,by=0.1),each=11),
j=rep(seq(0,1,by=0.1),times=11),
.combine="rbind") %dopar% {
val <- calc(let=input$selected_let,
var1=i,
var2=j)
data.frame(var1=i,
var2=j,
mean_b=tmp$mean_b,
mean_c=tmp$mean_c)
}
})
But this ends up in permanent errors:
I tried to out require(dplyr) in the the server part, but that did not help either.
Any suggestions for solutions?
As stand alone, the foreach part runs well with let=0.5 as input, given its not in reactive
foreach(i=rep(seq(0,1,by=0.1),each=11),
j=rep(seq(0,1,by=0.1),times=11),
.combine="rbind") %dopar% {
val <- calc(let=0.5,
var1=i,
var2=j)
data.frame(var1=i,
var2=j,
mean_b=tmp$mean_b,
mean_c=tmp$mean_c)
}
Here is a way to avoid the double for-loop using library(data.table):
library(shiny)
library(data.table)
set.seed(0)
DF <- data.frame(a = rnorm(n = 26), b = 1:26, c = 100:125)
setDT(DF)
DT <- setDT(expand.grid(var1 = seq(0, 1, by = 0.1), var2 = seq(0, 1, by = 0.1)))
setorder(DT, var1, var2)
calc <- function(DF, let = 0.5, var1 = 0.1, var2 = 0.5) {
DF[, c("mean_b", "mean_c") := .(b * fifelse(a < let, var1, 0), c * fifelse(a < let, var2, 2))]
as.list(colMeans(DF[, .(mean_b, mean_c)]))
}
ui <- fluidPage(titlePanel("Example"),
sidebarLayout(
sidebarPanel(
sliderInput(
inputId = "selected_let",
label = "LET",
value = 0.5,
min = 0,
max = 1,
step = 0.1
),
submitButton("CALCULATE")
),
mainPanel(
h1(paste0("Table1")),
tableOutput("table_1"),
h1(paste0("Table2")),
tableOutput("table_2")
)
))
server <- function(input, output) {
data <- reactive({
DT[, c("mean_b", "mean_c") := calc(DF, let = input$selected_let, var1 = var1, var2 = var2), by = seq_len(NROW(DT))]
})
output$table_1 <- renderTable({
dcast(data(), var1 ~ var2, value.var = "mean_b")
})
output$table_2 <- renderTable({
dcast(data(), var1 ~ var2, value.var = "mean_c")
})
}
shinyApp(ui = ui, server = server)
Here you can find a benchmark taking into account dplyr and data.table (among others).

In R Shiny, how to read additional user inputs into a function and plot the results?

The below "MWE code 1" works as intended. It interpolates the values the user inputs into the matrix (id = input2) over the slider input periods (id = input1). Additional scenarios are generated with the click of the single action button which triggers a modal (for later purposes). For illustrative purposes, each scenario is linearly adjusted by a random variable.
I'm trying to adapt the above where additional user inputs into the matrix (always in column groupings of 2, for the 2 values to interpolate) are automatically added to the results function and plotted, without clicking the action button.
The below "MWE code 2" is my beginning of this implementation, and I end at my current knowledge. (Note the input matrix which expands in groups of 2 columns, and the elimination of the runif() inflator since presumably each added scenario will be different). How can I modify MWE code 2 to accomplish this? I am stuck.
MWE code 1:
library(shiny)
library(tidyverse)
library(ggplot2)
library(shinyMatrix)
interpol <- function(a,b){ # a = periods, b = matrix inputs
c <- rep(NA,a)
c[1] <- b[1]
c[a] <- b[2]
c <- approx(seq_along(c)[!is.na(c)],c[!is.na(c)],seq_along(c))$y # this interpolates
return(c)}
ui <- fluidPage(
sliderInput('input1','Periods to interpolate (input1):',min=2,max=10,value=10),
matrixInput("input2",
label = "Values to interpolate (input2):",
value = matrix(c(1,5),1,2,dimnames = list(NULL,c("Value 1","Value 2"))),
rows = list(names = FALSE),
class = "numeric"),
actionButton("add", "Add scenario"),
plotOutput("plot")
)
server <- function(input, output, session) {
results <- function(){interpol(req(input$input1),req(input$input2))}
numScenarios <- reactiveValues(numS=1)
observeEvent(input$add, {showModal(modalDialog(footer = modalButton("Close")))
numScenarios$numS <- (numScenarios$numS+1)})
output$plot <- renderPlot({
req(input$input1,input$input2)
v <- lapply(1:numScenarios$numS,
function(i) tibble(Scenario=i,X=1:input$input1,Y=runif(1)+results())
) %>% bind_rows()
v %>% ggplot() + geom_line(aes(x=X, y=Y, colour=as.factor(Scenario)))
})
}
shinyApp(ui, server)
MWE code 2:
library(shiny)
library(tidyverse)
library(ggplot2)
library(shinyMatrix)
interpol <- function(a,b){ # a = periods, b = matrix inputs
c <- rep(NA,a)
c[1] <- b[1]
c[a] <- b[2]
c <- approx(seq_along(c)[!is.na(c)],c[!is.na(c)],seq_along(c))$y # this interpolates
return(c)}
ui <- fluidPage(
sliderInput('input1','Periods to interpolate (input1):',min=2,max=10,value=10),
matrixInput("input2",
label = "Values to interpolate (input2) where first row lists scenario number:",
value = matrix(c(1,5),1,2,dimnames = list(NULL,c("Value 1","Value 2"))),
cols = list(extend = TRUE, delta = 2, delete = TRUE, names = TRUE,
editableNames = FALSE, multiheader=TRUE),
rows = list(names = FALSE),
class = "numeric"),
actionButton("add", "Add scenario"),
plotOutput("plot")
)
server <- function(input, output, session) {
results <- function(){interpol(req(input$input1),req(input$input2))}
numScenarios <- reactiveValues(numS=1)
observeEvent(input$add, {showModal(modalDialog(footer = modalButton("Close")))
numScenarios$numS <- (numScenarios$numS+1)})
output$plot <- renderPlot({
req(input$input1,input$input2)
v <- lapply(1:numScenarios$numS,
function(i) tibble(Scenario=i,X=1:input$input1,Y=results())
) %>% bind_rows()
v %>% ggplot() + geom_line(aes(x=X, y=Y, colour=as.factor(Scenario)))
})
observe({
req(input$input2)
mm <- input$input2
colnames(mm) <- trunc(1:ncol(mm)/2)+1
isolate(updateMatrixInput(session, "input2", mm))
})
}
shinyApp(ui, server)
See explanatory images below:
Edit: I'd suggest using a row-based matrixInput. This makes your life much easier, as you don't have to reshape the matrix before passing it to your custom function etc.
Please check the following:
library(shiny)
library(shinyMatrix)
library(dplyr)
library(ggplot2)
interpol <- function(a, b) {
# a = periods, b = matrix inputs
c <- rep(NA, a)
c[1] <- b[1]
c[a] <- b[2]
c <- approx(seq_along(c)[!is.na(c)], c[!is.na(c)], seq_along(c))$y # this interpolates
return(c)
}
ui <- fluidPage(
titlePanel("myMatrixInput"),
sidebarLayout(
sidebarPanel(
matrixInput(
"myMatrixInput",
label = "Values to interpolate (myMatrixInput) where first row lists scenario number:",
value = matrix(c(10, 1, 5), 1, 3, dimnames = list("Scenario 1", c("Periods", "Value 1", "Value 2"))),
cols = list(
extend = FALSE,
names = TRUE,
editableNames = FALSE
),
rows = list(names = TRUE,
delete = TRUE,
extend = TRUE,
delta = 1),
class = "numeric"
),
actionButton("add", "Add scenario")
),
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output, session) {
sanitizedMat <- reactiveVal()
observeEvent(input$myMatrixInput, {
if(any(rownames(input$myMatrixInput) == "")){
tmpMatrix <- input$myMatrixInput
rownames(tmpMatrix) <- paste("Scenario", seq_len(nrow(input$myMatrixInput)))
updateMatrixInput(session, inputId = "myMatrixInput", value = tmpMatrix)
}
sanitizedMat(na.omit(input$myMatrixInput))
})
plotData <- reactive({
req(dim(sanitizedMat())[1] >= 1)
lapply(seq_len(nrow(sanitizedMat())),
function(i){
tibble(
Scenario = rownames(sanitizedMat())[i],
X = seq_len(sanitizedMat()[i, 1]),
Y = interpol(sanitizedMat()[i, 1], sanitizedMat()[i, 2:3])
)
}) %>% bind_rows()
})
output$plot <- renderPlot({
req(nrow(plotData()) > 0)
plotData() %>% ggplot() + geom_line(aes(
x = X,
y = Y,
colour = as.factor(Scenario)
))
})
}
shinyApp(ui, server)
Initial Answer
There is no need to calulate the numScenarios as they are defined by the dimensions of your matrix. The same applies to the modal you'll add later - just monitor the dimensions of the data to change the plot - no matter which input changes the reactive dataset.
As a general advice I'd recommend working with data.frames in long format instead of a matrix to prepare plots (using e.g. ggplot or plotly). See my answer here for an example.
Please check the following:
library(shiny)
library(shinyMatrix)
library(dplyr)
library(ggplot2)
interpol <- function(a, b) {
# a = periods, b = matrix inputs
c <- rep(NA, a)
c[1] <- b[1]
c[a] <- b[2]
c <- approx(seq_along(c)[!is.na(c)], c[!is.na(c)], seq_along(c))$y # this interpolates
return(c)
}
ui <- fluidPage(
sliderInput(
'mySliderInput',
'Periods to interpolate (mySliderInput):',
min = 2,
max = 10,
value = 10
),
matrixInput(
"myMatrixInput",
label = "Values to interpolate (myMatrixInput):",
value = matrix(c(1, 5), 1, 2, dimnames = list(NULL, c("Value 1", "Value 2"))),
cols = list(
extend = TRUE,
delta = 2,
delete = TRUE
),
rows = list(names = FALSE),
class = "numeric"
),
actionButton("add", "Add scenario"),
plotOutput("plot")
)
server <- function(input, output, session) {
observeEvent(input$add, {
showModal(modalDialog(footer = modalButton("Close")))
})
plotData <- reactive({
req(dim(input$myMatrixInput)[2] >= 2)
# req(dim(input$myMatrixInput)[2]%%2 == 0)
req(input$mySliderInput)
if(as.logical(dim(input$myMatrixInput)[2]%%2)){
myVector <- head(as.vector(input$myMatrixInput), -1)
} else {
myVector <- as.vector(input$myMatrixInput)
}
myMatrix <- matrix(myVector, ncol = 2)
lapply(seq_len(length(myVector)/2),
function(i){
tibble(
Scenario = i,
X = seq_len(input$mySliderInput),
Y = interpol(req(input$mySliderInput), req(myMatrix[i,]))
)
}) %>% bind_rows()
})
output$plot <- renderPlot({
req(nrow(plotData()) > 0)
plotData() %>% ggplot() + geom_line(aes(
x = X,
y = Y,
colour = as.factor(Scenario)
))
})
}
shinyApp(ui, server)
The above Edit works BEAUTIFULLY. Wow. Now the below simple edit of your edit simply pull the periods to interpolate out of the input matrix and back into a single slider input since in the full model this is meant for, modeled periods have to be the same for all input variables. However your 3 column matrix inputs also help me on another matter so THANK YOU. Also, I removed the "Add scenarios" action button since it is no longer needed with the automatically expanding input matrix. I sure learned a lot with this.
Edit of your edit:
ui <- fluidPage(
titlePanel("myMatrixInput"),
sidebarLayout(
sidebarPanel(
sliderInput('periods','Periods to interpolate:',min=2,max=10,value=10),
matrixInput(
"myMatrixInput",
label = "Values to interpolate (myMatrixInput):",
value = matrix(c(1, 5), 1, 2, dimnames = list("Scenario 1", c("Value 1", "Value 2"))),
cols = list(extend = FALSE,
names = TRUE,
editableNames = FALSE),
rows = list(names = TRUE,
delete = TRUE,
extend = TRUE,
delta = 1),
class = "numeric"
),
),
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output, session) {
sanitizedMat <- reactiveVal()
observeEvent(input$myMatrixInput, {
if(any(rownames(input$myMatrixInput) == "")){
tmpMatrix <- input$myMatrixInput
rownames(tmpMatrix) <- paste("Scenario", seq_len(nrow(input$myMatrixInput)))
updateMatrixInput(session, inputId = "myMatrixInput", value = tmpMatrix)
}
sanitizedMat(na.omit(input$myMatrixInput))
})
plotData <- reactive({
req(dim(sanitizedMat())[1] >= 1)
lapply(seq_len(nrow(sanitizedMat())),
function(i){
tibble(
Scenario = rownames(sanitizedMat())[i],
X = 1:input$periods,
Y = interpol(input$periods, sanitizedMat()[i, 1:2])
)
}) %>% bind_rows()
})
output$plot <- renderPlot({
req(nrow(plotData()) > 0)
plotData() %>% ggplot() + geom_line(aes(
x = X,
y = Y,
colour = as.factor(Scenario)
))
})
}
shinyApp(ui, server)

Shiny dataframe column selection and plotting

I am trying to create a Shiny interactive web app but I have a rendering problem linked to a plot. My goal is to upload a csv file, have the possibility to select the column to plot (on y axis) and have the possibility to select the data range on which plot data. Here there is my code:
library(dplyr)
library(shiny)
library(networkD3)
library(igraph)
library(ggplot2)
db = as.data.frame(read.csv("./util_df.csv"))
EmotionalDB = as.data.frame(read.csv("./MainDB.csv"))
my_list = as.list(names(EmotionalDB))
my_list = my_list[c(2, 3, 4, 5, 6, 7, 8, 9, 10, 11)]
ui <- fluidPage(
column(12, wellPanel(
dateRangeInput('dateRangeEmotions',
label = 'Filter emotions by date',
start = as.Date('2019-05-20') ,
end = as.Date('2019-05-26')
)
)),
selectInput("data1",
label = "Choose an Emotion",
choices = my_list
),
plotOutput("Emotions")
)
server <- function(input, output, session) {
output$Emotions <- renderPlot({
x <- EmotionalDB$Date
y <- EmotionalDB$Anger
plot(main="Emotions", x, y, type="l", xlim=c(input$dateRangeEmotions[1],input$dateRangeEmotions[2]), xaxt = "n")
axis.Date(side = 1, at = x, format = "%Y-%m-%d")
})
}
shinyApp(ui = ui, server = server)
Unfortunately there is no output in the plot. Here an image:
Here an example of my dataset:
"Date","Anger","Anticipation","Disgust","Fear","Joy","Negative","Positive","Sadness","Surprise","Trust"
"2019-05-20",12521,14652,2687,5164,13085,18309,23214,12882,12091,18623
"2019-05-21",13073,14988,3170,5773,13191,18988,24747,12973,12005,19435
"2019-05-22",15085,18608,3428,6475,16354,22671,30028,15765,15347,23680
"2019-05-23",23586,32597,5092,10084,24827,34827,44475,24468,23021,35440
"2019-05-24",61955,74395,10963,19597,65097,88223,104236,67361,59611,86375
"2019-05-25",19017,23540,4170,8595,19640,29740,34746,21793,18817,27907
"2019-05-26",9379,11909,1849,4535,10046,14791,17525,10757,9306,14095
Can anyone help me?
A couple suggestions to try:
Make sure your Date variable in EmotionalDB is of Date type.
EmotionalDB$Date <- as.Date(EmotionalDB$Date)
Try using reactive to subset your data based on the input selections of emotion and the 2 dates.
server <- function(input, output, session) {
selectedData <- reactive({
subset(EmotionalDB[ , c("Date", input$data1)], Date >= input$dateRangeEmotions[1] & Date <= input$dateRangeEmotions[2])
})
output$Emotions <- renderPlot({
sd <- selectedData()
plot(sd, main="Emotions", type="l", xaxt = "n")
axis.Date(side = 1, at = sd$Date, format = "%Y-%m-%d")
})
}

if else in shiny reactive

I have a rather simple problem but can not figure out why it is not working
library(shiny)
library(leaflet)
pts <- data.frame(
id = letters[seq(from = 1, to = 10)],
x = rnorm(10, mean = -93.625),
y = rnorm(10, mean = 42.0285),
stringsAsFactors = F
)
# Define UI
ui <- fluidPage(uiOutput('Select'))
server <- function(input, output, session) {
pts
output$Select <- renderUI({
Range <- sort(unique(pts$id))
selectInput("dataselect",
"select",
choices = Range,
selected = 'a')
})
mydata <- reactive({
if (input$dataselect != 'a') {
data <- pts[pts$id == input$dataselect,]
}
else
{
data <- pts
}
})
observe(print(mydata()))
}
shinyApp(ui = ui, server = server)
I basically try to subset my data set if anything else than 'a' is selected with the selected value. If 'a' is selected I want the whole df returned.
Just run into
Warning: Error in if: argument is of length zero [No stack trace
available]
You need to to not run mydata() if input$dataselect is not available, that can be done by inserting: req(input$dataselect)
As shown below:
library(shiny)
library(leaflet)
pts <- data.frame(
id = letters[seq(from = 1, to = 10)],
x = rnorm(10, mean = -93.625),
y = rnorm(10, mean = 42.0285),
stringsAsFactors = F
)
# Define UI
ui <- fluidPage(uiOutput('Select'))
server <- function(input, output, session) {
output$Select <- renderUI({
Range <- sort(unique(pts$id))
selectInput("dataselect",
"select",
choices = Range,
selected = 'a')
})
mydata <- reactive({
req(input$dataselect)
if (input$dataselect != 'a') {
data <- pts[pts$id == input$dataselect,]
}
else
{
data <- pts
}
})
observe(print(mydata()))
}
shinyApp(ui = ui, server = server)

Dynamic number of highcharter plots

I'm following this example to plot multiple graphs depending of different parameters (as data frame columns). So the case is that the number of plots to do will vary each day.
I have modified the code to use Highcharter to get javascript charts instead of basic plots but it doesn't work.
Also I would like to know what I have to add to this code to plots charts in 2,3 or 4 columns?
Thanks
ui.R
fluidPage(
# Application title
titlePanel("Hello World!"),
# Show a plot
fluidRow(
column(width = 6,
highchartOutput("hcontainer", height = "400px")
)
)
)
server.R
get_plot_output_list <- function() {
plot_output_list <- lapply(1:NCOL(df), FUN = function(i) {
plot_output_object <- highchartOutput("hcontainer")
plot_output_object <- renderHighchart({
hc <- highchart() %>%
hc_add_serie(name = "df name", data = df)
return(hc)
})
})
do.call(tagList, plot_output_list) # needed to display properly.
return(plot_output_list)
}
observe({
output$hcontainer <- renderUI({ get_plot_output_list() })
#output$hcontainer <- renderHighchart({ get_plot_output_list() })
})
Hi you can try this solution, it do not use the same function as you but one by #jenesaisquoi (found here), this function create several plots and handle the layout correctly :
# Packages
library("highcharter")
library("shiny")
# data
df <- data.frame(
var1 = rnorm(10),
var2 = rnorm(10),
var3 = rnorm(10),
var4 = rnorm(10),
var5 = rnorm(10),
var6 = rnorm(10),
var7 = rnorm(10)
)
# Fun by #jenesaisquoi (modified with highchartOutput)
makePlotContainers <- function(n, ncol=2, prefix="plot", height=100, width="100%", ...) {
## Validate inputs
validateCssUnit(width)
validateCssUnit(height)
## Construct plotOutputs
lst <- lapply(seq.int(n), function(i)
highchartOutput(sprintf('%s%g', prefix, i), height=height, width=width))
## Make columns
lst <- lapply(split(lst, (seq.int(n)-1)%/%ncol), function(x) column(12/ncol, x))
do.call(tagList, lst)
}
You can use #jenesaisquoi's function directly in the ui, and use lapply in the server for define as many outputs as the number of cols :
# App
ui <- fluidPage(
# Application title
titlePanel("Hello World!"),
# Show plots
makePlotContainers(n = ncol(df), ncol = 3, prefix = "hcontainer", height = "400px")
)
server <- function(input, output) {
lapply(
X = seq_len(ncol(df)),
FUN = function(i) {
output[[paste0("hcontainer", i)]] <- renderHighchart({
highchart() %>%
hc_add_serie(name = paste("df name", i), data = df[[i]])
})
}
)
}
shinyApp(ui = ui, server = server)

Resources