I want the plots (generated by plotly) to be laid out in two columns and n rows. The number of rows depends on the number of plots. For instance, the layout should be 3(row) x 2(col) if there are 5 or 6 plots. However, there are two problems with the following code. First, only one of them is repeated when we have multiple plots. Second, they are stacked on top of each other, although the column width is 6.
Here is the code:
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(selectInput(inputId = "y", label = "Y", choices = names(mtcars), selected = names(mtcars)[1], multiple = F),
selectizeInput(inputId = "xvars", label = "X", choices = names(mtcars), selected = names(mtcars)[1],
multiple = T)),
mainPanel(uiOutput("allplots"))
)
)
server <- function(input, output, session) {
output$allplots <- renderUI({
plt_list <- list()
for (x in input$xvars){
plt_list[[x]] <- renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = input$y)) + geom_point()
})
}
if (length(input$xvars) == 1) {
plottoUI <- fluidRow(column(12, plt_list[1]))
} else {
plottoUI <- fluidRow(column(6, plt_list[1:length(input$xvars)]))
}
return(plottoUI)
})
}
shinyApp(ui, server)
UPDATTE:
#lz100 seems to have resolved the main issue with the layout. Here is a further update on how to prevent one plot being repeated. I replaced the for loop (I don't know the reason why it didn't work) with lapply.
plt_list <- lapply(input$xvars, function(x){
renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = input$y)) + geom_point()
})
})
So, considering the #lz100 suggestion the final solution will be:
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(selectInput(inputId = "y", label = "Y", choices = names(mtcars), selected = names(mtcars)[1], multiple = F),
selectizeInput(inputId = "xvars", label = "X", choices = names(mtcars), selected = names(mtcars)[1],
multiple = T)),
mainPanel(uiOutput("allplots"))
)
)
server <- function(input, output, session) {
output$allplots <- renderUI({
plt_list <- list()
plt_list <- lapply(input$xvars, function(x){
renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = input$y)) + geom_point()
})
})
if (length(input$xvars) == 1) {
plottoUI <- fluidRow(column(12, plt_list[1]))
} else {
plottoUI <- fluidRow(
lapply(1:length(input$xvars), function(x) column(6, plt_list[x]))
)
}
return(plottoUI)
})
}
shinyApp(ui, server)
You need to wrap each plot with a column, not a column for all plots, see below:
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(selectInput(inputId = "y", label = "Y", choices = names(mtcars), selected = names(mtcars)[1], multiple = F),
selectizeInput(inputId = "xvars", label = "X", choices = names(mtcars), selected = names(mtcars)[1],
multiple = T)),
mainPanel(uiOutput("allplots"))
)
)
server <- function(input, output, session) {
output$allplots <- renderUI({
plt_list <- list()
for (x in input$xvars){
plt_list[[x]] <- renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = input$y)) + geom_point()
})
}
if (length(input$xvars) == 1) {
plottoUI <- fluidRow(column(12, plt_list[1]))
} else {
plottoUI <- fluidRow(
lapply(1:length(input$xvars), function(x) column(6, plt_list[x]))
)
}
return(plottoUI)
})
}
shinyApp(ui, server)
Related
I would like to allow the user to select multiple times the same choice. It implies that when the user selects some element, it should not be removed from the choices' dropdown menu.
Here is a minimal reproducible example:
library(shiny)
ui <- fluidPage(
selectInput(
inputId = "ManyDuplicated",
label = 'SelectInput',
choices = c('Hello', 'World'),
selected = NULL,
multiple = TRUE
)
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
How it is:
How I would like it to be:
What I tried and may be of help:
This code (https://github.com/rstudio/shiny/issues/2939#issuecomment-678269674) works perfectly for a single choice ("^" in the case). However, I can't make it work for more choices (c("^", "a"), for example).
library(shiny)
ui <- fluidPage(
selectInput("x", "choose", c("^" = 1), multiple = TRUE)
)
server <- function(input, output, session) {
observeEvent(input$x, {
choices <- seq_len(length(input$x)+1)
names(choices) <- rep("^", length(choices))
updateSelectInput(session, "x", choices = choices, selected = isolate(input$x))
})
}
shinyApp(ui, server)
With multiple choices it gets a bit more complicated
library(shiny)
my_choices <- c('Hello', 'World')
ui <- fluidPage(
selectInput(
inputId = "ManyDuplicated",
label = 'SelectInput',
choices = my_choices,
selected = NULL,
multiple = TRUE
)
)
server <- function(input, output, session) {
observeEvent(input$ManyDuplicated, {
selected_values <- input$ManyDuplicated
names(selected_values) <- gsub("\\..*", "", selected_values)
print( paste( "Current selection :",
paste( names(selected_values), collapse = ", ")))
number_of_items <- length(input$ManyDuplicated)
new_choices <- paste(my_choices, number_of_items + 1, sep = ".")
names(new_choices) <- my_choices
all_choices <- c(selected_values, new_choices )
updateSelectInput(session, "ManyDuplicated",
choices = all_choices,
selected = isolate(input$ManyDuplicated))
})
}
shinyApp(ui = ui, server = server)
shinyApp(
ui = fluidPage(
selectInput("choose", "Choose",
sort(c("a" = "a1", "b" = "b2")),
multiple = TRUE
)
),
server = function(input, output, session) {
old_choose = c()
old_choices = sort(c("a" = "a1", "b" = "b2"))
idx <- 2
observeEvent(input$choose, {
req(!identical(old_choose, input$choose))
addition <- base::setdiff(input$choose, old_choose)
if (length(addition) > 0) {
idx <<- idx + 1
new_nm <- names(old_choices[old_choices == addition])
new_val <- paste0(new_nm, idx)
choices <- c(old_choices, new_val)
names(choices) <- c(names(old_choices), new_nm)
}
missing <- base::setdiff(old_choose, input$choose)
if (length(missing) > 0) {
missing_idx <- which(old_choices == missing)
choices <- old_choices[-missing_idx]
}
choices <- sort(choices)
updateSelectInput(session, "choose",
choices = choices,
selected = input$choose
)
old_choose <<- input$choose
old_choices <<- choices
}, ignoreNULL = FALSE)
}
)
I want to create a r shiny app in which for each y variable one tab is produced (so tabs must be dynamically generated) and in each tab the plots are in two columns and n rows (as in the figure). However, I received the following error:
`x` must contain exactly 1 expression, not 2
In fact, I want to combine the two codes to create what I described above:
Code 1: This code creates one tab for every y variable
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "x", label = "X var", choices = names(mtcars), selected = names(mtcars)[1], multiple = F),
selectizeInput(inputId = "y", label = "Y var", choices = names(mtcars), selected = names(mtcars)[1], multiple = T)
),
mainPanel(
uiOutput("plots")
)
)
)
server <- function(input, output, session) {
output$plots <- renderUI({
plt_list <- list()
plt_list <- lapply(input$y, function(y){
renderPlot({
ggplot(mtcars, aes_string(input$x, y)) + geom_point()
})
})
names(plt_list) <- input$y
do.call(tabsetPanel, c(id='tab',lapply(input$y, function(y) {
tabPanel(
title=paste0('tab ', y),
fluidRow(column(6, plt_list[[y]]))
)
})))
})
}
shinyApp(ui, server)
Code 2: This code creates multiple plots of y vs. different x variables, and the plots are arranged in a way so that we have two columns and n rows.
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(selectInput(inputId = "y", label = "Y", choices = names(mtcars), selected = names(mtcars)[1], multiple = F),
selectizeInput(inputId = "xvars", label = "X", choices = names(mtcars), selected = names(mtcars)[1],
multiple = T)),
mainPanel(uiOutput("allplots"))
)
)
server <- function(input, output, session) {
output$allplots <- renderUI({
plt_list <- list()
plt_list <- lapply(input$xvars, function(x){
renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = input$y)) + geom_point()
})
})
if (length(input$xvars) == 1) {
plottoUI <- fluidRow(column(12, plt_list[1]))
} else {
plottoUI <- fluidRow(
lapply(1:length(input$xvars), function(x) column(6, plt_list[x]))
)
}
return(plottoUI)
})
}
shinyApp(ui, server)
Code 3: The combination of the codes above that results in the error:
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(selectizeInput(inputId = "y", label = "Y", choices = names(mtcars), selected = names(mtcars)[1:2], multiple = T),
selectizeInput(inputId = "xvars", label = "X", choices = names(mtcars), selected = names(mtcars)[1:3],
multiple = T)),
mainPanel(uiOutput("allplots"))
)
)
server <- function(input, output, session) {
output$allplots <- renderUI({
plt_list <- list()
plots <- lapply(input$y, function(y){
plt_list <- lapply(input$xvars, function(x){
renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = input$y)) + geom_point()
})
})
})
names(plots) <- input$y
plotarrange <- lapply(input$y, function(y){
if (length(input$xvars) == 1) {
plottoUI <- fluidRow(column(12, plots[[y]][1]))
} else {
plottoUI <- fluidRow(
lapply(1:length(input$xvars), function(x) column(6, plots[[y]][x]))
)
}
return(plottoUI)
})
names(plotarrange) <- input$y
do.call(tabsetPanel, c(id='tab',lapply(input$y, function(y) {
tabPanel(
title=paste0('tab ', y),
plotarrange[[y]]
)
})))
})
}
shinyApp(ui, server)
What is the source of the error and how do I resolve it ?
I found the source of the error in code 3, input$y should have been changes to y as follows
plots <- lapply(input$y, function(y){
plt_list <- lapply(input$xvars, function(x){
renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = y)) + geom_point()
})
})
})
Therefore:
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(selectizeInput(inputId = "y", label = "Y", choices = names(mtcars), selected = names(mtcars)[1:2], multiple = T),
selectizeInput(inputId = "xvars", label = "X", choices = names(mtcars), selected = names(mtcars)[1:3],
multiple = T)),
mainPanel(uiOutput("allplots"))
)
)
server <- function(input, output, session) {
rval <- reactiveValues(
plt_list = NULL
)
output$allplots <- renderUI({
plt_list <- list()
plots <- lapply(input$y, function(y){
rval$plt_list <- lapply(input$xvars, function(x){
renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = y)) + geom_point()
})
})
})
names(plots) <- input$y
plotarrange <- lapply(input$y, function(y){
if (length(input$xvars) == 1) {
plottoUI <- fluidRow(column(12, plots[[y]][1]))
} else {
plottoUI <- fluidRow(
lapply(1:length(input$xvars), function(x) column(6, plots[[y]][x]))
)
}
return(plottoUI)
})
names(plotarrange) <- input$y
do.call(tabsetPanel, c(id='tab',lapply(input$y, function(y) {
tabPanel(
title=paste0('tab ', y),
plotarrange[[y]]
)
})))
})
}
shinyApp(ui, server)
I have a shinyApp that allows the user to create a barplot and dynamically select the colors of the plot.
As you can see, this app is working fine, however, as the colors are represented by the Hex code, there is no really a way of telling which color is it until you have plot it,
hece I would like a way to improve the display of the colors selection.
In the webpage (https://ssc.wisc.edu/shiny/users/jstruck2/colorpicker/) there is an example of what I want, although there is no code.
And here you can find a RepEx of my app.
# Shiny
library(shiny)
library(shinyWidgets)
library(shinyjqui)
library(shinyjs)
library(DT)
# Data
library(readxl)
library(dplyr)
library(vcd)
# Plots
library(ggplot2)
not_sel <- "Not Selected"
ui <- navbarPage(
title = "Plotter",
windowTitle = "Plotter",
tabPanel(
"Plotter",
fluidPage(
fluidRow(
sidebarPanel(
title = "Inputs",
fileInput("xlsx_input", "Select XLSX file to import", accept = c(".xlsx")),
selectInput("num_var_1", "Variable X axis", choices = c(not_sel)),
selectInput("num_var_2", "Variable Y axis", choices = c(not_sel)),
br(),
actionButton("run_button", "Run Analysis", icon = icon("play"))
),
# Main panel
mainPanel(
tabsetPanel(
tabPanel(
"Plot",
br(),
plotOutput("plot_1"),
uiOutput("factor"),
)
)
)
)
)
)
)
################# --------------------------------------------------------------
# Server
################# --------------------------------------------------------------
server <- function(input, output){
# Dynamic selection of the data
data_input <- reactive({
#req(input$xlsx_input)
#inFile <- input$xlsx_input
#read_excel(inFile$datapath, 1)
Arthritis
})
# We update the choices available for each of the variables
observeEvent(data_input(),{
choices <- c(not_sel, names(data_input()))
updateSelectInput(inputId = "num_var_1", choices = choices)
updateSelectInput(inputId = "num_var_2", choices = choices)
})
num_var_1 <- eventReactive(input$run_button, input$num_var_1)
num_var_2 <- eventReactive(input$run_button, input$num_var_2)
# data
data_discrete_plot <- reactive({
req(data_input(), input$num_var_1, input$num_var_2)
df <- data_input()
df1 <- as.data.frame(prop.table(table(df[[input$num_var_1]], df[[input$num_var_2]]), margin = 1))
df1
})
output$factor <- renderUI({
#req(input$num_var_2,data_input())
if (is.null(input$num_var_2) | (input$num_var_2=="Not Selected")) return(NULL)
df <- data_input()
uvalues <- unique(df[[input$num_var_2]])
n <- length(uvalues)
choices <- as.list(uvalues)
myorder <- as.list(1:n)
mycolors <- list("#727CA3", "#464653", "#638CAE", "#836A61", "#889328", "#F5AF22", "#DF564B", "#6B4A8C", "#CE7EB8")
nk <- length(mycolors) ## to repeat colors when there are more bars than the number of colors
tagList(
div(br()),
div(
lapply(1:n, function(i){
k <- i %% nk
if (k==0) k=nk
pickerInput(paste0("colorvar",i),
label = paste0(uvalues[i], ": " ),
choices = list(# DisplayOrder = myorder,
FillColor = mycolors),
selected = list( i, mycolors[[k]]),
multiple = T,
options = list('max-options-group' = 1, `style` = "btn-primary"))
})
)
)
})
#observe({print(input$colorvar1)})
output$t1 <- renderDT(data_discrete_plot())
# Function for printing the plots
draw_barplot <- function(data_input) {
n <- length(unique(data_input[,"Var2"]))
val <- list()
myvaluesx <- lapply(1:n, function(i) {
input[[paste0("colorvar",i)]]
if (i==1) val <- list(input[[paste0("colorvar",i)]])
else val <- list(val,input[[paste0("colorvar",i)]])
})
print(myvaluesx)
ggplot(data = data_input, aes(x = Var1, y = Freq, fill = factor(Var2), label = round(Freq, 3))) +
geom_bar(stat = "identity") +
#scale_fill_discrete(guide = guide_legend(fill = myvaluesx, reverse = TRUE)) +
scale_fill_manual(values = unlist(myvaluesx)) +
ylim(0, 1) +
theme_bw()
}
## BarPlot -------------------------------------------------------------------
plot_1 <- eventReactive(input$run_button,{
req(data_input())
draw_barplot(data_discrete_plot())
})
output$plot_1 <- renderPlot(plot_1())
}
# Connection for the shinyApp
shinyApp(ui = ui, server = server)
I am trying to make multiple dynamic plots (one does not know how many plots will output) based on one or more selected dropdown value(s) (species in this case).
I did succeed making plots based on the dropdown. For instance, two plots are displayed if a user selected two values/species from the dropdown list, one plot is displayed if one value/species is selected.
Although the number of plots match the number of dropdown values, the plots show a duplicate if two or more dropdown values/species are selected (it only works if exactly one value is selected). Any advice would be of great help.
The below code uses the iris dataset in R.
image1
image2
library(shiny)
library(shinyWidgets)
library(ggplot2)
library(tidyverse)
library(shinydashboard)
species = c("setosa", "versicolor", "virginica")
ui <- dashboardPage(
dashboardHeader(title = "ddPCR"),
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem("General Overview", tabName = "tab1", icon = icon("dashboard"))
)
),
body <- dashboardBody(
tabItems(
tabItem(
tabName = "tab1",
uiOutput("species_dropdown"),
DT::dataTableOutput("table1"),
uiOutput("plots")
)
)
)
)
server <- function(input, output) {
output$species_dropdown <- renderUI({
pickerInput(
"var1",
"Species:",
choices = species,
options = pickerOptions(
actionsBox = T,
header = "Close",
liveSearch = T
),
multiple = T
)
})
filtered_data <- reactive({
iris %>%
filter(Species %in% input$var1)
})
output$table1 <- DT::renderDataTable({
req(input$var1)
filtered_data()
})
# Insert the right number of plot output objects into the web page
output$plots <- renderUI({
req(input$var1)
plot_output_list <- lapply(1:length(input$var1), function(i) {
plotname <- paste("plot", i, sep="")
plotOutput(plotname, height = 280, width = 250)
})
do.call(tagList, plot_output_list)
})
for (i in 1:length(species)) {
local({
plotname <- paste("plot", i, sep="")
output[[plotname]] <- renderPlot({
ggplot(filtered_data(), aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
labs(title = paste(input$var1, sep = ""), x = "Sepal Length", y = "Sepal Width")
})
})
}
}
shinyApp(ui, server)
From the comments in this app on line 34:
Need local so that each item gets its own number. Without it, the value
of i in the renderPlot() will be the same across all instances, because
of when the expression is evaluated.
So you need to assign i to a local variable otherwise it will be the same for all renderPlots, hence why your plots are identical. Also, your title should be changed to index the inputs, otherwise only the first element is used to the title argument because paste will return a list when it is passed a list, while title requires just one value.
Full app:
library(shiny)
library(shinyWidgets)
library(ggplot2)
library(tidyverse)
library(shinydashboard)
species = c("setosa", "versicolor", "virginica")
ui <- dashboardPage(
dashboardHeader(title = "ddPCR"),
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem("General Overview", tabName = "tab1", icon = icon("dashboard"))
)
),
body <- dashboardBody(
tabItems(
tabItem(
tabName = "tab1",
uiOutput("species_dropdown"),
DT::dataTableOutput("table1"),
uiOutput("plots")
)
)
)
)
server <- function(input, output) {
output$species_dropdown <- renderUI({
pickerInput(
"var1",
"Species:",
choices = species,
options = pickerOptions(
actionsBox = T,
header = "Close",
liveSearch = T
),
multiple = T
)
})
filtered_data <- reactive({
iris %>%
filter(Species %in% input$var1)
})
output$table1 <- DT::renderDataTable({
req(input$var1)
filtered_data()
})
# Insert the right number of plot output objects into the web page
output$plots <- renderUI({
req(input$var1)
plot_output_list <- lapply(1:length(input$var1), function(i) {
plotname <- paste("plot", i, sep="")
plotOutput(plotname, height = 280, width = 250)
})
do.call(tagList, plot_output_list)
})
for (i in 1:length(species)) {
local({
my_i <- i #crucial
plotname <- paste("plot", my_i, sep="") # use my_i instead of i
output[[plotname]] <- renderPlot({
ggplot(filtered_data(), aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
labs(title = paste(input$var1[my_i], sep = ""), x = "Sepal Length", y = "Sepal Width") # title needs input$var1 indexed as paste will return a list otherwise, in which case only a first element gets used for the title hence all titles are identical
})
})
}
}
shinyApp(ui, server)
I am tying to create an R shiny app and I would like to have two selectInput i.e. data set name and column name. Right now, I am able to get data set names in the first Input but I am not able to create dependent column selectIput (whose list will depend upon data set selected). Please guide.
require(shiny)
require(MASS)
a <- data(package = "MASS")[3]
b <- a$results[,3]
ui <- fluidPage(
sidebarPanel(width = 2,
selectInput(inputId = "dsname",label = "Select Dataset:",choices = c(b)),
colnames <- names("dsname"),
selectInput(inputId = "columns", label = "Choose columns",
choices = c(colnames))
)
)
server <- function(input,output) {}
shinyApp(ui <- ui, server <- server)
In order to have "responsive" elements in Shiny, you need to wrap your expressions for computing the responsive elements in reactive({...}).
You could use renderUI in your server() and uiOutput in your ui() with something like this. Here is an example I had built for using some of R's data sets (iris, mtcars, and diamonds):
library(shinythemes)
library(shiny)
library(ggplot2)
ui <- fluidPage(theme = shinytheme("superhero"),
titlePanel("Welcome to Responisve Shiny"),
sidebarLayout(
sidebarPanel(
selectInput("data", "Dataset:",
choices = c("mtcars", "iris", "diamonds")
),
uiOutput("x_axis"),
uiOutput("y_axis"),
uiOutput("color")
),
mainPanel(
plotOutput("distPlot")
)
)
)
server <- function(input, output) {
output$x_axis <- renderUI({
col_opts <- get(input$data)
selectInput("x_axis2", "X-Axis:",
choices = names(col_opts))
})
output$y_axis <- renderUI({
cols2 <- reactive({
col_opts2 <- get(input$data)
names(col_opts2)[!grepl(input$x_axis2, names(col_opts2))]
})
selectInput("y_axis2", "Y-Axis:",
choices = cols2(),
selected = "hp")
})
output$color <- renderUI({
col_opts <- get(input$data)
selectInput("color", "Color:",
choices = names(col_opts),
selected = "cyl")
})
output$distPlot <- renderPlot({
if(input$data == "mtcars"){
p <- ggplot(mtcars, aes_string(input$x_axis2, input$y_axis2, color = input$color)) +
geom_point()
}
if(input$data == "iris"){
p <- ggplot(iris, aes_string(input$x_axis2, input$y_axis2, color = input$color)) +
geom_point()
}
if(input$data == "diamonds"){
p <- ggplot(diamonds, aes_string(input$x_axis2, input$y_axis2, color = input$color)) +
geom_point()
}
print(p)
})
}
shinyApp(ui = ui, server = server)