I'm trying to make a simple Shiny app with the following features:
2 or 3 inputs:
input1: a selectInput
input2: a selectInput, conditional of input1
input3: a numeric value
The output is a ggplot where the labels inside of it change with the inputs.
I also want a default plot and a Reset button.
I made 3 files: ui.R, server.R and function.R. The last one is the one that makes the plot.
When input1 takes the values of "A" or "B", I get the desired output. The "Reset" button also seems to work fine. However, when I select "C" in input 1, it brings me back to the default plot: a plot whith the label "Nothing", instead of something like "C1 10".
I checked the code many times, but I can't get where is the problem.
Here is the code of my files:
# file ui.R
library(shiny)
shinyUI(fluidPage(
plotOutput("printsomething"),
selectInput(
inputId="input1",
label="input1",
choices=c("A","B","C"),
selected = NULL,
multiple = FALSE,
selectize = TRUE,
width = NULL,
size = NULL),
conditionalPanel(
condition = "input.input1 == 'A'",
selectInput("input2", "input2",
list("A1", "A2"))),
conditionalPanel(
condition = "input.input1 == 'C'",
selectInput("input2", "input2",
list("C1", "C2"))),
numericInput("num","Number",value=0,min=0),
actionButton("Run","Run"),
actionButton("Reset","Reset")
)
)
# file server.R
source("function.R")
library(shiny)
shinyServer(function(input, output) {
graph <- reactiveValues(data = NULL)
observeEvent(input$Run, {
graph$data <- printsomething(data,input$input1,input$input2,input$num)
})
observeEvent(input$Reset, {
graph$data <- printsomething("Nothing","NA","NA","NA")
})
output$printsomething <- renderPlot({
if (is.null(graph$data)) return(printsomething(data,"NA","NA","NA"))
graph$data
})
})
# file function.R
library(ggplot2)
data <- "Nothing"
printsomething <- function(data,input1=NA,input2=NA,num=0) {
if(is.na(input1)) {
data <- "Nothing"
} else if(input1=="A") {
if(input2=="A1") {
data <- paste("A1",num)
} else if(input2=="A2") {
data <- paste("A2",num)
}
} else if(input1=="B") {
data <- paste("B",num)
} else if(input1=="C") {
if(input2=="C1") {
data <- paste("C1",num)
} else if(input2=="C2") {
data <- paste("C2",num)
}
}
ggplot() +
geom_label(aes(x=1,y=1,label=data))
}
I'll really appreciate it if someone can help me. I'm new to Shiny.
Thanks.
As stated in the comment, unique inputId will make it work. There is a minor adjustment in the server side, and no change in the function printsomething. Try this
library(shiny)
ui <- shinyUI(fluidPage(
plotOutput("printsomething"),
selectInput(
inputId="input1",
label="input1",
choices=c("A","B","C"),
selected = NULL,
multiple = FALSE,
selectize = TRUE,
width = NULL,
size = NULL),
conditionalPanel(
condition = "input.input1 == 'A'",
selectInput("input2", "input2", list("A1", "A2"))),
conditionalPanel(
condition = "input.input1 == 'C'",
selectInput("input3", "input2", list("C1", "C2"))),
numericInput("num","Number",value=0,min=0),
actionButton("Run","Run"),
actionButton("Reset","Reset")
))
server <- shinyServer(function(input, output) {
graph <- reactiveValues(data = NULL)
observeEvent(input$Run, {
if (input$input1=="A"){ input2 = input$input2
}else if (input$input1=="C") input2 = input$input3
graph$data <- printsomething(data,input$input1,input2,input$num)
})
observeEvent(input$Reset, {
graph$data <- printsomething("Nothing","NA","NA","NA")
})
output$printsomething <- renderPlot({
if (is.null(graph$data)) return(printsomething(data,"NA","NA","NA"))
graph$data
})
})
shinyApp(ui, server)
Related
I want to make an app with 2 actionButtons: 1) to submit the changes before loading a selectizeInput and 2) to draw the plot.
I know how to add a spinner after clicking a actionButton but the majority of the cases is added when you want to show the plot.
However, is it possible to add a spinner without showing any plot?
In this particular case, I want to show a spinner after clicking "Submit" until the selectizeInput from the 'Selection tab' is loaded. As you can see the example that I attach, it takes a bit to load all the choices (since the file has 25000 rows).
I already have one spinner after clicking the second actionButton (Show the plot) but I need one more.
I have created an example, but for some reason the plot is not shown in the shiny app and it appears in the window from R (I don't know why but I added the plot just to show you how I put the second spinner. I want a similar one but with the first actionButton.).
library(shiny)
library(shinycssloaders)
ui <- fluidPage(
titlePanel("My app"),
sidebarLayout(
sidebarPanel(
tabsetPanel(
tabPanel("Submit",
checkboxInput("log2", "Log2 transformation", value = FALSE),
actionButton("submit", "Submit")
),
tabPanel("Selection",
br(),
selectizeInput(inputId = "numbers", label = "Choose one number:", choices=character(0)),
actionButton("show_plot", "Show the plot")
))
),
mainPanel(
conditionalPanel(
condition = "input.show_plot > 0",
style = "display: none;",
withSpinner( plotOutput("hist"),
type = 5, color = "#0dc5c1", size = 1))
)
)
)
server <- function(input, output, session) {
data <- reactive({
data = read.csv("https://people.sc.fsu.edu/~jburkardt/data/csv/hw_25000.csv")
data[,1] <- as.character(data[,1])
if(input$log2 == TRUE){
cols <- sapply(data, is.numeric)
data[cols] <- lapply(data[cols], function(x) log2(x+1))
}
return(data)
})
mylist <- reactive({
req(data())
data <- data()
data <- data[,1]
return(data)
})
# This is to generate the choices (gene list) depending on the user's input.
observeEvent(input$submit, {
updateSelectizeInput(
session = session,
inputId = "numbers",
choices = mylist(), options=list(maxOptions = length(mylist()))
)
})
v <- reactiveValues()
observeEvent(input$show_plot, {
data <- data()
v$plot <- plot(x=data[,1], y=data[,2])
})
# If the user didn't choose to see the plot, it won't appear.
output$hist <- renderPlot({
req(data())
if (is.null(v$plot)) return()
if(input$show_plot > 0){
v$plot
}
})
}
Does anyone know how to help me, please?
Thanks very much
It's a little tricky.
First of all I'd update the selectizeInput on the server side as the warning suggests:
Warning: The select input "numbers" contains a large number of
options; consider using server-side selectize for massively improved
performance. See the Details section of the ?selectizeInput help
topic.
Furthermore I switched to ggplot2 regarding the plotOutput - Please see this related post.
To show the spinner while the selectizeInput is updating choices we'll need to know how long the update takes. This information can be gathered via shiny's JS events - please also see this article.
Finally, we can show the spinner for a non-existent output, so we are able to control for how long the spinner is shown (see uiOutput("dummyid")):
library(shiny)
library(shinycssloaders)
library(ggplot2)
ui <- fluidPage(
titlePanel("My app"),
tags$script(HTML(
"
$(document).on('shiny:inputchanged', function(event) {
if (event.target.id === 'numbers') {
Shiny.setInputValue('selectizeupdate', true, {priority: 'event'});
}
});
$(document).on('shiny:updateinput', function(event) {
if (event.target.id === 'numbers') {
Shiny.setInputValue('selectizeupdate', false, {priority: 'event'});
}
});
"
)),
sidebarLayout(
sidebarPanel(
tabsetPanel(
tabPanel("Submit",
checkboxInput("log2", "Log2 transformation", value = FALSE),
actionButton("submit", "Submit")
),
tabPanel("Selection",
br(),
selectizeInput(inputId = "numbers", label = "Choose one number:", choices=NULL),
actionButton("show_plot", "Show the plot")
))
),
mainPanel(
uiOutput("plotProxy")
)
)
)
server <- function(input, output, session) {
previousEvent <- reactiveVal(FALSE)
choicesReady <- reactiveVal(FALSE)
submittingData <- reactiveVal(FALSE)
observeEvent(input$selectizeupdate, {
if(previousEvent() && input$selectizeupdate){
choicesReady(TRUE)
submittingData(FALSE)
} else {
choicesReady(FALSE)
}
previousEvent(input$selectizeupdate)
})
data <- reactive({
data = read.csv("https://people.sc.fsu.edu/~jburkardt/data/csv/hw_25000.csv")
if(input$log2 == TRUE){
cols <- sapply(data, is.numeric)
data[cols] <- lapply(data[cols], function(x) log2(x+1))
}
return(data)
})
mylist <- reactive({
req(data()[,1])
})
observeEvent(input$submit, {
submittingData(TRUE)
reactivePlotObject(NULL) # reset
updateSelectizeInput(
session = session,
inputId = "numbers",
choices = mylist(), options=list(maxOptions = length(mylist())),
server = TRUE
)
})
reactivePlotObject <- reactiveVal(NULL)
observeEvent(input$show_plot, {
reactivePlotObject(ggplot(data(), aes_string(x = names(data())[1], y = names(data())[2])) + geom_point())
})
output$hist <- renderPlot({
reactivePlotObject()
})
output$plotProxy <- renderUI({
if(submittingData() && !choicesReady()){
withSpinner(uiOutput("dummyid"), type = 5, color = "#0dc5c1", size = 1)
} else {
conditionalPanel(condition = "input.show_plot > 0", withSpinner(plotOutput("hist"), type = 5, color = "#0dc5c1", size = 1), style = "display: none;")
}
})
}
shinyApp(ui, server)
First 100 rows of your example data (dput(head(data, 100)) - your link might be offline some day):
structure(list(Index = 1:100, Height.Inches. = c(65.78331, 71.51521,
69.39874, 68.2166, 67.78781, 68.69784, 69.80204, 70.01472, 67.90265,
66.78236, 66.48769, 67.62333, 68.30248, 67.11656, 68.27967, 71.0916,
66.461, 68.64927, 71.23033, 67.13118, 67.83379, 68.87881, 63.48115,
68.42187, 67.62804, 67.20864, 70.84235, 67.49434, 66.53401, 65.44098,
69.5233, 65.8132, 67.8163, 70.59505, 71.80484, 69.20613, 66.80368,
67.65893, 67.80701, 64.04535, 68.57463, 65.18357, 69.65814, 67.96731,
65.98088, 68.67249, 66.88088, 67.69868, 69.82117, 69.08817, 69.91479,
67.33182, 70.26939, 69.10344, 65.38356, 70.18447, 70.40617, 66.54376,
66.36418, 67.537, 66.50418, 68.99958, 68.30355, 67.01255, 70.80592,
68.21951, 69.05914, 67.73103, 67.21568, 67.36763, 65.27033, 70.84278,
69.92442, 64.28508, 68.2452, 66.35708, 68.36275, 65.4769, 69.71947,
67.72554, 68.63941, 66.78405, 70.05147, 66.27848, 69.20198, 69.13481,
67.36436, 70.09297, 70.1766, 68.22556, 68.12932, 70.24256, 71.48752,
69.20477, 70.06306, 70.55703, 66.28644, 63.42577, 66.76711, 68.88741
), Weight.Pounds. = c(112.9925, 136.4873, 153.0269, 142.3354,
144.2971, 123.3024, 141.4947, 136.4623, 112.3723, 120.6672, 127.4516,
114.143, 125.6107, 122.4618, 116.0866, 139.9975, 129.5023, 142.9733,
137.9025, 124.0449, 141.2807, 143.5392, 97.90191, 129.5027, 141.8501,
129.7244, 142.4235, 131.5502, 108.3324, 113.8922, 103.3016, 120.7536,
125.7886, 136.2225, 140.1015, 128.7487, 141.7994, 121.2319, 131.3478,
106.7115, 124.3598, 124.8591, 139.6711, 137.3696, 106.4499, 128.7639,
145.6837, 116.819, 143.6215, 134.9325, 147.0219, 126.3285, 125.4839,
115.7084, 123.4892, 147.8926, 155.8987, 128.0742, 119.3701, 133.8148,
128.7325, 137.5453, 129.7604, 128.824, 135.3165, 109.6113, 142.4684,
132.749, 103.5275, 124.7299, 129.3137, 134.0175, 140.3969, 102.8351,
128.5214, 120.2991, 138.6036, 132.9574, 115.6233, 122.524, 134.6254,
121.8986, 155.3767, 128.9418, 129.1013, 139.4733, 140.8901, 131.5916,
121.1232, 131.5127, 136.5479, 141.4896, 140.6104, 112.1413, 133.457,
131.8001, 120.0285, 123.0972, 128.1432, 115.4759)), row.names = c(NA,
100L), class = "data.frame")
I am trying to create a shiny code that is able to filter a table non pre-determined number of times. When the user uploads a different (new) table, unfortunately the code breaks as I need to restart a lapply loop somehow, throwing out the previously stored column names.
I would like to create an non pre-defined filtering options for a table within Shiny. The user can select a column and filter a table choosing different categorical variables within that column. It is possible to add additional selection fields by pressing the 'Add' button.
the UI:
library(shiny)
library(shinydashboard)
library(dplyr)
ui <- shinyUI(
pageWithSidebar(
headerPanel("testing of dynamic number of selection"),
sidebarPanel(
uiOutput("buttons")),
mainPanel(
uiOutput("drops")
,tableOutput("table")
)
))
The server:
A table (test.csv) is automatically stored in a reactive values and a first searching field appears with 3 buttons (Add = to add a new searching field by reading in the colnames and a multiselect that stores the unique variables from that columns. The filtering function is activated by the Calculate button)
server<-function(input, output, session) {
###### read in test file
values<-reactiveValues(number = 1,
upload = NULL,
input = NULL)
values$upload<-read.csv("test.csv")
#just the "add" button, in this instance it shouldn't be a uiOutput
output$buttons <- renderUI({
div(
actionButton(inputId = "add", label = "Add"), actionButton(inputId = "calc", label = "Calculate"),
actionButton(inputId = "new", label = "new table")
)
})
#pressing the add button
observeEvent(input$add, {
cat("i adding a new record\n")
values$number <- values$number + 1L })
daStuff <- function(i){
inputName<-paste0("drop", i)
inputName2<-paste0("select", i)
inputText<-if(values$number>0){input[[paste0("drop",i)]]}else{F} # previously selected value for dropdown
inputSelect <- if(values$number>1){input[[paste0("select",i)]]}else{F} # previously selected value for dropdown
fluidRow(
column(6,selectInput(inputName, inputName, c(colnames(values$upload)), selected = inputText)),
column(6,selectInput(inputName2, inputName2,
na.omit(unique(as.vector(values$upload[,input[[paste0("drop",i)]]]))),
multiple=TRUE, selectize=TRUE, selected=inputSelect)) )}
output$drops<- renderUI({
lapply(seq_len(values$number), daStuff)})
By pressing the Calculate button, the uploaded table is subjected to filtering, depending on the selected unique values and shown in the output$table
observeEvent(input$calc, {
values$input<-NULL
for (i in 1:values$number){
if(!is.null(input[[paste0("select",i)]])){
if(is.null(values$input)){
values$input<- filter(values$upload,values$upload[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])}
else{
values$input<- filter(values$input,values$input[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])}
} }
if (is.null(values$input)){values$input<-values$upload}
output$table <- renderTable({values$input})
})
My problem is when I upload a new table (test2.csv), I don't know how to erase the previously stored selections (drop* and select* values) and gives back an error message.
observeEvent(input$new,{
values$upload<-read.csv("test2.csv")
})
}
shinyApp(ui=ui, server = server)
I suppose I should stop somehow the lapply loop and restart it over, so the previously stored values are replaced depending on the new selection, but I am a bit stuck on how I could achieve that.
Just in case you might still be looking for solutions, I wanted to share something that was similar and could potentially be adapted for your needs.
This uses observeEvent for all select inputs. If it detects any changes, it will update all inputs, including the possibilities for select based on drop.
In addition, when a new file is read, the selectInput for drop and select are reset to first value.
Edit: I forgot to keep selected = input[[paste0("drop",i)]] in place for the dropdown (see revised code). It seems to keep the values now when new filters are added - let me know if this is what you had in mind.
library(shiny)
library(shinydashboard)
library(dplyr)
myDataFrame <- read.csv("test.csv")
ui <- shinyUI(
pageWithSidebar(
headerPanel("Testing of dynamic number of selection"),
sidebarPanel(
fileInput("file1", "Choose file to upload", accept = ".csv"),
uiOutput("buttons")
),
mainPanel(
uiOutput("inputs"),
tableOutput("table")
)
)
)
server <- function(input, output, session) {
myInputs <- reactiveValues(rendered = c(1))
myData <- reactive({
inFile <- input$file1
if (is.null(inFile)) {
d <- myDataFrame
} else {
d <- read.csv(inFile$datapath)
}
d
})
observeEvent(lapply(paste0("drop", myInputs$rendered), function(x) input[[x]]), {
for (i in myInputs$rendered) {
updateSelectInput(session,
paste0('select', i),
choices = myData()[input[[paste0('drop', i)]]],
selected = input[[paste0("select",i)]])
}
})
output$buttons <- renderUI({
div(
actionButton(inputId = "add", label = "Add"),
actionButton(inputId = "calc", label = "Calculate")
)
})
observeEvent(input$add, {
myInputs$rendered <- c(myInputs$rendered, max(myInputs$rendered)+1)
})
observeEvent(input$calc, {
showData <- NULL
for (i in 1:length(myInputs$rendered)) {
if(!is.null(input[[paste0("select",i)]])) {
if(is.null(showData)) {
showData <- filter(myData(), myData()[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])
}
else {
showData <- filter(showData, showData[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])
}
}
}
if (is.null(showData)) { showData <- myData() }
output$table <- renderTable({showData})
})
observe({
output$inputs <- renderUI({
rows <- lapply(myInputs$rendered, function(i){
fluidRow(
column(6, selectInput(paste0('drop',i),
label = "",
choices = colnames(myData()),
selected = input[[paste0("drop",i)]])),
column(6, selectInput(paste0('select',i),
label = "",
choices = myData()[1],
multiple = TRUE,
selectize = TRUE))
)
})
do.call(shiny::tagList, rows)
})
})
}
shinyApp(ui, server)
How to show warning to user in shiny in R. The user's input is correct, but the output is not suitable to show. The aim is to remind the user only a subset data are shown due to too many. warning() is shown in console only. Thank you.
Here is a fake code to explain the question due to the original is long. There is a warning in the renderTable. it aims to check data if the data is big, only first several items will be shown.
ui.R
shinyUI(fluidPage(
titlePanel("Validation App"),
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("", "mtcars", "faithful", "iris"))
),
# Show a plot of the generated distribution
mainPanel(
tableOutput("table"),
plotOutput("plot")
)
)
))
server.R
shinyServer(function(input, output) {
data <- reactive({
validate(
need(input$data != "", "Please select a data set")
)
get(input$data, 'package:datasets')
})
output$plot <- renderPlot({
hist(data()[, 1], col = 'forestgreen', border = 'white')
})
output$table <- renderTable({
warning("Warning message.")
head(data())
})
})
Update:
I put some more work into this and made the warning panel conditional.
However it only works if I include out the textOutput("warnstat") on every page. I assume because it is not setting the javascript variable output.warnstat unless I do this.
You could just build a warning panel into your UI, and set it accordingly. Here is a simple example, but it could be more elaborate than just a verabtim print statement.
ui.r
shinyUI(fluidPage(
titlePanel("Validation App"),
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("", "mtcars", "faithful", "iris"))
),
# Show a plot of the generated distribution
mainPanel(
conditionalPanel(condition = "output.warnstat == 'Error'",
verbatimTextOutput("warnmsg")),
tableOutput("table"),
plotOutput("plot")
)
)
))
server.r
shinyServer(function(input, output) {
errstat <- reactive({
ifelse (input$data=="mtcars",T,F)
})
data <- reactive({
validate(
need(input$data != "", "Please select a data set")
)
get(input$data, 'package:datasets')
})
output$plot <- renderPlot({
hist(data()[, 1], col = 'forestgreen', border = 'white')
})
output$table <- renderTable({
warning("Warning message.")
head(data())
})
output$warnmsg <- renderPrint({
if (errstat()){
print("Warning message - blah blah blah")
print(input$data)
head(data())
} else {
print("No error")
}
})
output$warnstat <- renderText({ifelse(errstat(),"Error","No error") })
outputOptions(output, "warnstat", suspendWhenHidden=FALSE)
})
With conditional warning panel:
Without conditional warning panel:
I use this wrapping function to capture errors, warnings and messages and display them as dismissible notifications to the user.
quietly <- function(.f) {
fun <- .f %>% purrr::quietly() %>% purrr::safely()
function(...) {
res <- fun(...)
if(!is.null(res$error)) { # safely output
showNotification(res$error$message, duration = 10, type="error")
return(res$result)
}
res <- res$result # quietly output
if(!is.null(res$warnings) && length(res$warnings) > 0) {
lapply(unique(res$warnings), showNotification, duration = 10, type="warning")
}
return(res$result)
}
}
I am trying to dynamically render multiple text output from multiple text input. I tried to use this very helpfull example and this one too.
This conversation is also helpfull.
But when I try to adapt this examples on the following script, I have a problem of output update. Apparently, only the last element was read and updated. It's probably a reactivity problem but it seems to be difficult to associate reactive{()} and renderUI{()}functions.
rm(list = ls())
library(shiny)
creatDataElem <- function(ne, input) {
x1 <- lapply(1:ne, function(i) {
textInput(paste0("elemName", i),
label = h4(strong("Name of dataset element")),
value = "")
})
return(x1)
}
ui = (fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("elemNb",
"Number of elements", value = 1, min = 1,
max = 3)
,
conditionalPanel(
condition = "input.elemNb == 1",
creatDataElem(1)
),
conditionalPanel(
condition = "input.elemNb == 2",
creatDataElem(2)
),
conditionalPanel(
condition = "input.elemNb == 3",
creatDataElem(3)
)
),
mainPanel(
uiOutput("nameElem")
)
)
)
)
server = function(input, output, session) {
max_elem <- 3
# Name
output$nameElem <-renderUI({
nameElem_output_list <- lapply(1:input$elemNb, function(i) {
elemName <- paste0("elemName", i)
tags$div(class = "group-output",
verbatimTextOutput(elemName)
)
})
do.call(tagList, nameElem_output_list)
})
for (i in 1:max_elem) {
local({
force(i)
my_i <- i
elemName <- paste0("elemName", my_i)
output[[elemName]] <- renderPrint(input[[elemName]])
})
}
}
runApp(list(ui = ui, server = server))
The idea with a reactive({}) function is to add an independant object (a function in this case) like:
nameElem <- reactive({
if (input$goElem == 0) {
return()
} else {
isolate({
if (is.null(input$elemName)) {
return()
} else if (test(input$elemName)) {
return("TEST RESULT")
} else {
return(input$elemName)
}
})
}
})
and to use renderUI on this object (with an ActionButton).
So, if someone knows why the output does not return the good object...
I think one of your problems is that your creatDataElem function is such that when it is called with argument ne=3, the first and second textInput elements are created again (and their value "lost").
Anyway, I think one solution would be to create those textInput elements as an "uiOutput".
You'll find a possible solution below which (I think) does what you want.
Lise
rm(list = ls())
library(shiny)
ui = (fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("elemNb",
"Number of elements", value = 1, min = 1,
max = 3),
uiOutput("myUI")
),
mainPanel(
uiOutput("nameElem")
)
)
)
)
server = function(input, output, session) {
output$myUI=renderUI({
w=""
for (i in 1:input$elemNb){
w=paste0(w,
textInput(paste0("elemName",i),label='Name of dataset element'))
}
HTML(w)
})
output$nameElem <-renderUI({
elems=c("<div>")
for(i in 1:input$elemNb){
elems=paste(elems,"</div><div>",input[[paste0("elemName",i)]])
}
elems=paste0(elems,"</div>")
HTML(elems)
})
}
runApp(list(ui = ui, server = server))
Found a solution:
library(readr)
library(dplyr)
library(shiny)
df <- data.frame(symbol = 1:10)
uiOutput("myUI")
createUI <- function(dfID, symbol) {
div(class="flex-box",paste0(symbol, " - 10"))
}
output$myUI <- renderUI({
w <- lapply(seq_len(nrow(df)), function(i) {
createUI(i, df[i,"symbol"])
})
do.call(fluidPage,w)
})
I've got a dropdown selector and a slider scale. I want to render a plot with the drop down selector being the source of data. - I've got this part working
I simply want the slider's max value to change based on which dataset is selected.
Any suggestions?
server.R
library(shiny)
shinyServer(function(input, output) {
source("profile_plot.R")
load("test.Rdata")
output$distPlot <- renderPlot({
if(input$selection == "raw") {
plot_data <- as.matrix(obatch[1:input$probes,1:36])
} else if(input$selection == "normalised") {
plot_data <- as.matrix(eset.spike[1:input$probes,1:36])
}
plot_profile(plot_data, treatments = treatment, sep = TRUE)
})
})
ui.R
library(shiny)
shinyUI(fluidPage(
titlePanel("Profile Plot"),
sidebarLayout(
sidebarPanel(width=3,
selectInput("selection", "Choose a dataset:",
choices=c('raw', 'normalised')),
hr(),
sliderInput("probes",
"Number of probes:",
min = 2,
max = 3540,
value = 10)
),
mainPanel(
plotOutput("distPlot")
)
)
))
As #Edik noted the best way to do this would be to use an update.. type function. It looks like updateSliderInput doesnt allow control of the range so you can try using renderUI on the server side:
library(shiny)
runApp(list(
ui = bootstrapPage(
numericInput('n', 'Maximum of slider', 100),
uiOutput("slider"),
textOutput("test")
),
server = function(input, output) {
output$slider <- renderUI({
sliderInput("myslider", "Slider text", 1,
max(input$n, isolate(input$myslider)), 21)
})
output$test <- renderText({input$myslider})
}
))
Hopefully this post will help someone learning Shiny:
The information in the answers is useful conceptually and mechanically, but doesn't help the overall question.
So the most useful feature I found in the UI API is conditionalPanel() here
This means I could create a slider function for each dataset loaded and get the max value by loading in the data initially in global.R. For those that don't know, objects loaded into global.R can be referenced from ui.R.
global.R - Loads in a ggplo2 method and test data objects (eset.spike & obatch)
source("profile_plot.R")
load("test.Rdata")
server.R -
library(shiny)
library(shinyIncubator)
shinyServer(function(input, output) {
values <- reactiveValues()
datasetInput <- reactive({
switch(input$dataset,
"Raw Data" = obatch,
"Normalised Data - Pre QC" = eset.spike)
})
sepInput <- reactive({
switch(input$sep,
"Yes" = TRUE,
"No" = FALSE)
})
rangeInput <- reactive({
df <- datasetInput()
values$range <- length(df[,1])
if(input$unit == "Percentile") {
values$first <- ceiling((values$range/100) * input$percentile[1])
values$last <- ceiling((values$range/100) * input$percentile[2])
} else {
values$first <- 1
values$last <- input$probes
}
})
plotInput <- reactive({
df <- datasetInput()
enable <- sepInput()
rangeInput()
p <- plot_profile(df[values$first:values$last,],
treatments=treatment,
sep=enable)
})
output$plot <- renderPlot({
print(plotInput())
})
output$downloadData <- downloadHandler(
filename = function() { paste(input$dataset, '_Data.csv', sep='') },
content = function(file) {
write.csv(datasetInput(), file)
}
)
output$downloadRangeData <- downloadHandler(
filename = function() { paste(input$dataset, '_', values$first, '_', values$last, '_Range.csv', sep='') },
content = function(file) {
write.csv(datasetInput()[values$first:values$last,], file)
}
)
output$downloadPlot <- downloadHandler(
filename = function() { paste(input$dataset, '_ProfilePlot.png', sep='') },
content = function(file) {
png(file)
print(plotInput())
dev.off()
}
)
})
ui.R
library(shiny)
library(shinyIncubator)
shinyUI(pageWithSidebar(
headerPanel('Profile Plot'),
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
choices = c("Raw Data", "Normalised Data - Pre QC")),
selectInput("sep", "Separate by Treatment?:",
choices = c("Yes", "No")),
selectInput("unit", "Unit:",
choices = c("Percentile", "Absolute")),
wellPanel(
conditionalPanel(
condition = "input.unit == 'Percentile'",
sliderInput("percentile",
label = "Percentile Range:",
min = 1, max = 100, value = c(1, 5))
),
conditionalPanel(
condition = "input.unit == 'Absolute'",
conditionalPanel(
condition = "input.dataset == 'Normalised Data - Pre QC'",
sliderInput("probes",
"Probes:",
min = 1,
max = length(eset.spike[,1]),
value = 30)
),
conditionalPanel(
condition = "input.dataset == 'Raw Data'",
sliderInput("probes",
"Probes:",
min = 1,
max = length(obatch[,1]),
value = 30)
)
)
)
),
mainPanel(
plotOutput('plot'),
wellPanel(
downloadButton('downloadData', 'Download Data Set'),
downloadButton('downloadRangeData', 'Download Current Range'),
downloadButton('downloadPlot', 'Download Plot')
)
)
))
I think you're looking for the updateSliderInput function that allows you to programmatically update a shiny input:
http://shiny.rstudio.com/reference/shiny/latest/updateSliderInput.html. There are similar functions for other inputs as well.
observe({
x.dataset.selection = input$selection
if (x.dataset.selection == "raw") {
x.num.rows = nrow(obatch)
} else {
x.num.rows = nrow(eset.spike)
}
# Edit: Turns out updateSliderInput can't do this,
# but using a numericInput with
# updateNumericInput should do the trick.
updateSliderInput(session, "probes",
label = paste("Slider label", x.dataset.selection),
value = c(1,x.num.rows))
})
Another alternative can be applying a renderUI approach like it is described in one of the shiny gallery examples:
http://shiny.rstudio.com/gallery/dynamic-ui.html