shiny debounce doesn't render initial plot - r

If I add a debounce to a get_data() reactive expression, the first time the data is retrieved the plot does not render. However, changing the data (by selecting a new mpg) causes the plot to then render. Why is this? Is there a workaround?
Here is a simple minimal example demonstrating the problem. Try removing %>% debounce(500) to see that it works as expected without it:
if (interactive()) {
options(device.ask.default = FALSE)
library(shiny)
library(magrittr)
ui <- fluidPage(
selectInput("select", label = "select mpg", choices = c(mtcars$mpg, ""), selected = ""),
plotOutput("plot")
)
server <- function(input, output, session) {
get_data <- reactive({
req(input$select)
mtcars[mtcars$mpg == input$select,]
}) %>% debounce(500)
get_plot <- reactive({
data <- get_data()
print(data)
plot(get_data())
})
output$plot <- renderPlot({
get_plot()
})
}
shinyApp(ui, server)
}

Couple of things going on here. I don't think we are allowed to have duplicates in a select input. mtcars$mpg has duplicate values inside it. Setting the initial value to "" is also causing strange behaviors. If you really want the initial plot to be empty along with debounce we could set it to " ". Here is what that would look like.
if (interactive()) {
options(device.ask.default = FALSE)
library(shiny)
library(magrittr)
ui <- fluidPage(
selectInput("select", label = "select mpg", choices = c(" ",unique(mtcars$mpg)),selected = " "),
plotOutput("plot")
)
server <- function(input, output, session) {
get_data <- reactive({
req(input$select)
if(!is.na(as.numeric(input$select))) mtcars[mtcars$mpg == input$select,] else NULL
}) %>% debounce(500)
get_plot <- reactive({
req(get_data())
data <- get_data()
print(data)
plot(get_data())
})
output$plot <- renderPlot({
get_plot()
})
}
shinyApp(ui, server)
}
Else if you're okay with having an initial plot the following works. Note its important to use unique()
if (interactive()) {
options(device.ask.default = FALSE)
library(shiny)
library(magrittr)
ui <- fluidPage(
selectInput("select", label = "select mpg", unique(mtcars$mpg)),
plotOutput("plot")
)
server <- function(input, output, session) {
get_data <- reactive({
req(input$select)
mtcars[mtcars$mpg == input$select,]
}) %>% debounce(500)
get_plot <- reactive({
req(get_data())
data <- get_data()
print(data)
plot(get_data())
})
output$plot <- renderPlot({
get_plot()
})
}
shinyApp(ui, server)
}
I even tried replacing the select input with selectizeInput("select", label = "select mpg", choices = unique(mtcars$mpg),multiple = TRUE,options = list(maxItems = 1)) This still caused issues.

Related

shinymeta: how to use expandChain in modules?

I'm making an app with modules in which the user can create as many UI as he wants. Each UI contain one table and I would like to give the possibility to the user to see the code for each of this table separately, not in a unique chunk. Therefore, I included the part of the code with expandChain in my module (module_server).
However, expandChain won't detect the reactive stuff I'm calling because the name of this stuff changes since it is created in a module. Take a look at the app below:
library(dplyr)
library(shiny)
library(shinymeta)
library(WDI)
module_ui <- function(id){
ns <- NS(id)
tagList(
fluidRow(
actionButton(ns("show_table"), "Show table"),
actionButton(ns("show_code"), "Show code"),
tableOutput(ns("table"))
)
)
}
module_server <- function(input, output, session){
data <- metaReactive2({
req(input$show_table)
isolate(metaExpr({
mtcars
}))
})
output$table <- renderTable({
data()
})
observeEvent(input$show_code, {
showModal(modalDialog(
renderPrint({
expandChain(data())
})
))
})
}
ui <- fluidPage(
actionButton("launch", "Launch")
)
server <- function(input, output, session) {
count <- reactiveValues(value = 0)
observeEvent(input$launch, {
count$value <- count$value + 1
insertUI(selector = "#launch",
where = "afterEnd",
ui = module_ui(count$value))
callModule(module_server, count$value)
})
}
shinyApp(ui, server)
When I try to show the code for the table generated, I have the error:
Warning: Error in : <text>:2:2: unexpected input
1: `1_data` <- mtcars
2: 1_
^
133: <Anonymous>
Since the module renames data() by adding a number, data() is not recognized by expandChain. I tried with:
expandChain(paste0(id, "_data()"))
without success (since expandChain does not support character).
Does anybody know how to do it?
Also asked on RStudio Community
Here's the solution given on RStudio Community (see the link for some additional details):
library(dplyr)
library(shiny)
library(shinymeta)
library(WDI)
module_ui <- function(id){
ns <- NS(id)
tagList(
fluidRow(
actionButton(ns("show_table"), "Show table"),
actionButton(ns("show_code"), "Show code"),
tableOutput(ns("table"))
)
)
}
module_server <- function(input, output, session){
data <- metaReactive2({
req(input$show_table)
isolate(metaExpr({
mtcars
}))
})
output$table <- renderTable({
data()
})
observeEvent(input$show_code, {
showModal(modalDialog(
renderPrint({
expandChain(data())
})
))
})
}
ui <- fluidPage(
actionButton("launch", "Launch")
)
server <- function(input, output, session) {
count <- reactiveValues(value = 0)
observeEvent(input$launch, {
count$value <- count$value + 1
insertUI(selector = "#launch",
where = "afterEnd",
ui = module_ui(paste0("x", count$value)))
callModule(module_server, paste0("x", count$value))
})
}
shinyApp(ui, server)

Update column in R datatable based on inputs in Shiny

I am trying to append the user input to the output table in Shiny app. And when the user changes any values for Total Cost it should update in the table before I click on run. How can I fix that?
library(dplyr)
library(shiny)
shinyApp(
ui = basicPage(
mainPanel(
numericInput("model_input", label = h5("Total Cost"), value = 10000),
numericInput("iterations", label = h5("Runs"), value = 900),
actionButton("run", "Run"),
actionButton("reset", "reset"),
tableOutput("view")
)
),
server = function(input, output) {
v <- reactiveValues(data = mtcars %>% mutate(budget = input$model_input)) # this makes sure that on load, your default data will show up
observeEvent(input$run, {
v$data <- mtcars %>% mutate(new = mpg * input$model_input +input$iterations)
})
observeEvent(input$reset, {
v$data <- mtcars # your default data
})
output$view <- renderTable({
v$data
})
}
)
You cant use input$model_input outside a reactive context. That was probably causing some issues. We simple move it outside into an observeEvent.
library(dplyr)
library(shiny)
shinyApp(
ui = basicPage(
mainPanel(
numericInput("model_input", label = h5("Total Cost"), value = 10000),
numericInput("iterations", label = h5("Runs"), value = 900),
actionButton("run", "Run"),
actionButton("reset", "reset"),
tableOutput("view")
)
),
server = function(input, output) {
v <- reactiveValues(data = mtcars) # this makes sure that on load, your default data will show up
observeEvent(input$model_input,{
v$data <- v$data %>% mutate(budget = input$model_input)
})
observeEvent(input$run, {
v$data <- mtcars %>% mutate(new = mpg * input$model_input +input$iterations)
})
observeEvent(input$reset, {
v$data <- mtcars # your default data
})
output$view <- renderTable({
v$data
})
}
)

Passing argument to a function in shiny R

I'm trying to pass the node value of a simple network as an argument to a function in Shiny R. However, I'm getting this error:
Error in rsqlite_send_query: no such column: input$id
Can anyone help with this issue? Thanks.
library(shiny)
library(networkD3)
ui <- shinyUI(fluidPage(
fluidRow(
column(4, simpleNetworkOutput("simple")),
column(4, DT::dataTableOutput("table"))
)
))
server <- shinyServer(function(input, output, session) {
session$onSessionEnded(stopApp)
output$simple <- renderSimpleNetwork({
sn<-simpleNetwork(df)
sn$x$options$clickAction = 'Shiny.onInputChange("id",d.name)'
sn
})
output$table <- DT::renderDataTable(DT::datatable(get(funct(input$id))))
})
shinyApp(ui = ui, server = server)
take out the deparse and substitute from your sprintf command, and add single quotes around the value you want to match in the SQL statement you're generating
get rid of the get function because you're not trying to "get" an object
for example....
library(shiny)
library(networkD3)
library(DT)
library(sqldf)
df <- read.csv(header = T, text = '
source,name,age,hair
dad,Jon X,18,brown
dad,Jon Y,22,blonde
')
funct <-
function (n) {
isp <- sprintf("Select df.age From df Where df.name='%s';", n)
isd <- sqldf::sqldf(isp)
return(isd)
}
ui <- shinyUI(fluidPage(
fluidRow(
column(4, simpleNetworkOutput("simple")),
column(4, DT::dataTableOutput("table"))
)
))
server <- shinyServer(function(input, output, session) {
session$onSessionEnded(stopApp)
output$simple <- renderSimpleNetwork({
sn<-simpleNetwork(df)
sn$x$options$clickAction = 'Shiny.onInputChange("id",d.name)'
sn
})
output$table <- DT::renderDataTable(DT::datatable(funct(input$id)))
})
shinyApp(ui = ui, server = server)
however, if all you want is to display a value associated with a given selection, I highly suggest drastically reducing the complexity to something like this
library(shiny)
library(networkD3)
df <- read.csv(header = T, text = '
source,name,age,hair
dad,Jon X,18,brown
dad,Jon Y,22,blonde
')
ui <- shinyUI(fluidPage(
fluidRow(
column(4, simpleNetworkOutput("simple")),
column(4, textOutput("text"))
)
))
server <- shinyServer(function(input, output, session) {
session$onSessionEnded(stopApp)
output$simple <- renderSimpleNetwork({
sn <- simpleNetwork(df)
sn$x$options$clickAction <- 'Shiny.onInputChange("id", d.name)'
sn
})
output$text <- renderPrint({ df$age[df$name == input$id] })
})
shinyApp(ui = ui, server = server)

Get selected row value instead of number in shiny using DT

Small question here: I know I can access selected rows by input$dfname_rows_selected it gives back the numbers of rows selected, but how do I read the rows names, not numbers? In my case they are generated not in the order I use them later, therefore I need to get the values inside to make it work.
edit: added example
ui <- shinyUI(fluidPage(
DT::dataTableOutput("table"),
actionButton("btn", "press me")
))
server <- function(input, output) {
observeEvent(input$btn, {
print(input$table_rows_selected)
})
output$table <- DT::renderDataTable({
mtcars %>%
datatable(selection = "multiple")
})
}
shinyApp(ui = ui, server = server)
Something like this:
library(shiny)
library(DT)
ui <- basicPage(
mainPanel(DT::dataTableOutput('mytable')),
textOutput("selected")
)
server <- function(input, output,session) {
mydata <- reactive({mtcars})
output$mytable = DT::renderDataTable(
datatable(mydata())
)
selectedRow <- eventReactive(input$mytable_rows_selected,{
row.names(mtcars)[c(input$mytable_rows_selected)]
})
output$selected <- renderText({
selectedRow()
})
}
runApp(list(ui = ui, server = server))
I don't think you can. What you could do is write a reactive, where all modifications to your dataframe take place, before creating the datatable. An example:
library(shiny)
library(DT)
ui <- shinyUI(fluidPage(
DT::dataTableOutput("table"),
textOutput("selectedcar")
)
)
server <- function(input, output,session) {
# the reactive where we filter/sort/modify the data
reactive_df <- reactive({
mtcars[order(mtcars$cyl),]
})
# This datatable uses the reactive directly, so no more modifications
output$table <- DT::renderDataTable({
DT::datatable(reactive_df())
})
# now we can get the row/rowname as follows:
output$selectedcar <- renderText({
paste0(rownames(reactive_df())[input$table_rows_selected], collapse = ", ")
})
}
shinyApp(ui, server)
Hope this helps!

"empty data message" in renderTable

I user renderTable to show some data. However, sometimes the data table is empty, in which case I'd like to print "No data to show" or something similar. the default by renderTable is to show nothing for empty data. can this be changed? how?
You can use a condition into a renderUi to render either a message or a "tableOutput" (you can't render directly the table)
datas <- data.frame()
shiny::runApp(list(
ui = pageWithSidebar(
headerPanel("Example"),
sidebarPanel(
selectInput("dataset", "Dataset", choices = c("iris", "datas"))
),
mainPanel(
uiOutput("ui")
)
),
server = function(input, output, session) {
datasetInput <- reactive({
switch(input$dataset,
"iris" = iris,
"datas" = datas)
})
output$ui <- renderUI({
if(nrow(datasetInput()) == 0)
return("No data to show")
tableOutput("table")
})
output$table <- renderTable({
head(datasetInput())
})
}
))
I think you are looking for something like validate function.
Using example code provided by Julien:
datas <- data.frame()
shiny::runApp(list(
ui = pageWithSidebar(
headerPanel("Example"),
sidebarPanel(
selectInput("dataset", "Dataset", choices = c("iris", "datas"))
),
mainPanel(
tableOutput('table')
)
),
server = function(input, output, session) {
datasetInput <- reactive({
switch(input$dataset,
"iris" = iris,
"datas" = datas)
})
output$table <- renderTable({
y <- head(datasetInput())
validate(
need(nrow(y) > 0, "No Data to show")
)
y
})
}
))
If you still want to show a "table" within the UI, do this:
output$table_output <- renderTable {
data <- data.frame(a = c(1,2),
b = c(8,9)) #example data.frame
if (nrow(data) > 0) {
data
} else {
datatable(data.frame(Nachricht = "Die ausgewählte Schnittstelle enthält hierfür keine Daten."))
}
}

Resources