How to efficiently subset a dataframe in R Shiny? - r

In the below example code I reactively subset the mtcars dataframe inside the renderPlot() function. However, in my larger App with many render functions in the server section I am having to repeat the same rv$x[1:input$samples], etc., over and over in many places. How would I apply this subsetting instead "at the top", into the rv <- reactiveValues(...) function itself or equivalent "master function"? I tried subsetting inside the reactiveValues() and got the message "Warning: Error in : Can't access reactive value 'samples' outside of reactive consumer. Do you need to wrap inside reactive() or observer()?" I assumed incorrectly that the reactiveValues() function is a "reactive consumer".
If someone can answer this basic understanding question, please explain the logic for correctly subsetting "at the top" because I am getting very embarrassed by my repeated questions about Shiny reactivity.
library(shiny)
ui <- fluidPage(
sliderInput('samples','Nbr of samples:',min=2,max=32,value=16),
plotOutput("p")
)
server <- function(input, output, session) {
rv <- reactiveValues(
x = mtcars$mpg,
y = mtcars$wt
)
output$p <- renderPlot({plot(rv$x[1:input$samples],rv$y[1:input$samples])})
}
shinyApp(ui, server)

There are multiple ways you can handle this.
Here is one way to create new subset reactive values inside observe.
library(shiny)
ui <- fluidPage(
sliderInput('samples','Nbr of samples:',min=2,max=32,value=16),
plotOutput("p")
)
server <- function(input, output, session) {
rv <- reactiveValues(
x = mtcars$mpg,
y = mtcars$wt
)
observe({
rv$x_sub <- rv$x[1:input$samples]
rv$y_sub <- rv$y[1:input$samples]
})
output$p <- renderPlot({plot(rv$x_sub,rv$y_sub)})
}
shinyApp(ui, server)

I'd use reactiveValues only if you need them to be modified in different places.
reactive is shiny's basic solution for this:
library(shiny)
library(datasets)
ui <- fluidPage(
sliderInput(
'samples',
'Nbr of samples:',
min = 2,
max = 32,
value = 16
),
plotOutput("p")
)
server <- function(input, output, session) {
reactive_mtcars <- reactive({mtcars[1:input$samples,]})
output$p <- renderPlot({
plot(reactive_mtcars()$mpg, reactive_mtcars()$wt)
})
}
shinyApp(ui, server)

Related

R Shiny Debounce with reactiveValues and observeEvent / R input debounce

I am currently trying in vain to use the debounce function in Shiny to delay my input a bit. The goal is to have the renderText not fire every few milliseconds, but only after 2 second intervals.
I tried to implement the following solution. Thereby I absolutely need the reactiveValues and observeEvent functions. Other solutions here never take this combination into account and I am currently stuck. My example code is shortened. In reality the variable name1$data is still used by different functions and the RenderText accesses different variables.
if (interactive()) {
ui <- fluidPage(
textInput("IText1", "Input i want to slow down"),
textOutput("OName")
)
server <- function(input, output, session) {
Name1 <- reactiveValues()
observeEvent(input$IText1, {Name1$data <- input$IText1})
#Solutions on stackoverflow
#Just causes errors for me
#Name1$t <- debounce(Name1$data, 2000)
output$OName <- renderText({
Name1$data
})
}
shinyApp(ui, server)
}
Thank you very much for any hint!
Normally we debounce reactive conductors (reactive({......})):
ui <- fluidPage(
textInput("IText1", "Input i want to slow down"),
textOutput("OName")
)
server <- function(input, output, session) {
Name1 <- reactive({
input$IText1
})
Name1_d <- debounce(Name1, 2000)
output$OName <- renderText({
Name1_d()
})
}
shinyApp(ui, server)
EDIT
Or you need
server <- function(input, output, session) {
Name1 <- reactiveValues()
observe({
invalidateLater(2000, session)
Name1$data <- isolate(input$IText1)
})
output$OName <- renderText({
Name1$data
})
}

Updating non-reactive values in R Shiny

I have the following example:
library(shiny)
ui <- fluidPage(
textOutput("out"),
actionButton("plusX", "Increase X"),
actionButton("redraw", "redraw")
)
server <- function(input, output, session) {
x <- 0
observeEvent(input$plusX, {x <<- x+1})
output$out <- renderText({
input$redraw
x
})
}
shinyApp(ui, server)
Is this considered an anti-pattern in Shiny to modify a non-reactive variable in this way? Obviating the super assignment which can be problematic by itself.
I know this could be done, for example with a reactiveVal to store X, and isolate to obtain a similar result. This second way seems clearer and that would be my usual choice, but I was wondering if there any caveats in the first one, or it is possible way of doing that.
library(shiny)
ui <- fluidPage(
textOutput("out"),
actionButton("plusX", "Increase X"),
actionButton("redraw", "redraw")
)
server <- function(input, output, session) {
x <- reactiveVal(0)
observeEvent(input$plusX, {x(x()+1)})
output$out <- renderText({
input$redraw
isolate(x())
})
}
shinyApp(ui, server)
In this example there is no important difference between both codes as you are not using the benefit of ReactiveVal.
The benefit of ReactiveVal is that it has a reactive nature and thus can interact with other reactive elements.
Try for example to add a table to your code that depends on x:
output$tab <- renderTable({data.frame(y = x)})
(x() in the case of ReactiveVal)
The difference you will see that in the case of ReactiveVal the table automatically updates with plusX whereas in the case of the regular variable it does not update.

How to use original and updated version of reactive data.table in Shiny?

I'm trying to include a dataset in a Shiny app that first gets read in and used as is, e.g. by displaying it as a table. I would like to allow the user to then be able to manipulate this dataset and update the same table output with the updated dataset.
I can get both parts to work separately - I can display the original data, and I can display reactive updated data. But I can't figure out how to get both to work using the same dataset? The below code is a simple example using iris, with an attempt to display the original dataset and then rbinding it so there are twice as many rows to display in the updated dataset when you hit 'Run'. Note that I've converted the data to data.table because my actual code will be using data.table a lot.
library(shiny)
library(data.table)
iris <- as.data.table(iris)
ui <- fluidPage(
fluidRow(column(4, actionButton("run", "Run"))),
fluidRow(column(12, tabPanel(title = "tab1",
DT::dataTableOutput("table1"))))
)
server <- function(input, output, session) {
irisdata <- reactive({
irisdata <- iris
})
irisdata <- eventReactive(input$run, {
rbind(irisdata(), iris, fill = TRUE)
})
output$table1 <- DT::renderDataTable({
irisdata()
})
}
shinyApp(ui, server)
The rbind results in: Error in : evaluation nested too deeply: infinite recursion / options(expressions=)?
Which is to be expected I suppose as it's self-referencing, but I can't figure out how to write the code otherwise?
Working code of the above example, based on the linked threads in the comments:
library(shiny)
library(data.table)
iris <- as.data.table(iris)
ui <- fluidPage(
fluidRow(column(4, actionButton("run", "Run"))),
fluidRow(column(12, tabPanel(title = "tab1",
DT::dataTableOutput("table1"))))
)
server <- function(input, output, session) {
irisdata <- reactiveValues(data = iris)
observeEvent(input$run, {
irisdata$data <- rbind(irisdata$data, iris, fill = TRUE)
})
output$table1 <- DT::renderDataTable({
irisdata$data
})
}
shinyApp(ui, server)

Passing a reactive dataset and function from parent app to module in Shiny

I am struggling with a workflow to pass a reactive dataset and function to a module in shiny. I have made a simple version of what I mean below; the app simply prints the mean mpg for a each value of cyl.
library(shiny)
# Module
textToolUI <- function(id){
ns <- NS(id)
textOutput(ns("text"))
}
textTool <- function(input, output, session, value){
output$text <- renderText({paste(value)})
}
# App
ui <- basicPage(
selectInput("carbSelect", "Carburetor Selector", choices = c(1,2,3,4)),
textToolUI("text1")
)
server <- function(input, output, session){
data <- reactive(filter(mtcars, carb == input$carbSelect))
myfunc <- function(x){return(mean(x))}
callModule(textTool, "text1", value = myfunc(data$mpg)) # This throws up the "object of type closure not subsettable" error
# Using data()$mpg means it is not reactive
}
shinyApp(ui = ui, server = server)
The problem arises due to the fact that both the dataset and function (myfunc) need to sit outside the module. In my actual app there are multiple different datasets and functions used.
I assume the problem here is that the function is evaluated before the reactive dataset and hence I need a different work flow but I can't think of a suitable alternative.
The module needs to be passed a reactive object only, no params.
The example below moves the function into the module, and turns it into a table instead of text as mean(mtcars) was outputting NA
library(shiny)
myfunc <- function(x){colMeans(x)}
myfunc2 <- function(x){colSums(x)}
# Module
textToolUI <- function(id){
ns <- NS(id)
tableOutput(ns("text"))
}
textTool <- function(input, output, session, value, f){
output$text <- renderTable({
req(value())
paste(f(value()))
})
}
# App
ui <- basicPage(
selectInput("carbSelect", "Carburetor Selector", choices = c(1,2,3,4)),
p("myfunc1 - colMeans"),
textToolUI("text1"),
p("myfunc2 - colSums"),
textToolUI("text2")
)
server <- function(input, output, session){
data <- reactive(dplyr::filter(mtcars, carb == input$carbSelect))
callModule(textTool, "text1", value = data, f = myfunc)
callModule(textTool, "text2", value = data, f = myfunc2)
# Using data()$mpg means it is not reactive
}
shinyApp(ui = ui, server = server)

Shiny Module that calls a reactive data set in parent Shiny server

I'm looking to port some older Shiny apps to use Shiny Modules, but running into trouble trying to port over my reactive expressions.
According to the documentation:
The goal is not to prevent modules from interacting with their
containing apps, but rather, to make these interactions explicit. If a
module needs to use a reactive expression, take the reactive
expression as a function parameter.
I have existing reactive expressions that import data from APIs etc. that I would like to pass in, but can't seem to find the syntax. If I modify the given Shiny module example below I can get to the same problem.
Could anyone modify the below so that you can pass in the car_data() reactive data into the module? I've tried just about every combination of isolate and car_data/car_data() I can think of and am stumped :)
I would prefer to not need to call the data within the module itself, as in my case I'm trying to generalise an ETL function applicable to lots of datasets.
library(shiny)
library(ggplot2)
linkedScatterUI <- function(id) {
ns <- NS(id)
fluidRow(
column(6, plotOutput(ns("plot1"), brush = ns("brush"))),
column(6, plotOutput(ns("plot2"), brush = ns("brush")))
)
}
linkedScatter <- function(input, output, session, data, left, right) {
# Yields the data frame with an additional column "selected_"
# that indicates whether that observation is brushed
dataWithSelection <- reactive({
brushedPoints(data(), input$brush, allRows = TRUE)
})
output$plot1 <- renderPlot({
scatterPlot(dataWithSelection(), left())
})
output$plot2 <- renderPlot({
scatterPlot(dataWithSelection(), right())
})
return(dataWithSelection)
}
scatterPlot <- function(data, cols) {
ggplot(data, aes_string(x = cols[1], y = cols[2])) +
geom_point(aes(color = selected_)) +
scale_color_manual(values = c("black", "#66D65C"), guide = FALSE)
}
ui <- fixedPage(
h2("Module example"),
linkedScatterUI("scatters"),
textOutput("summary")
)
server <- function(input, output, session) {
### My modification
### making the reactive outside of module call
car_data <- reactive({
mpg
})
## This doesn't work
## What is the syntax for being able to call car_data()?
df <- callModule(linkedScatter, "scatters", car_data(),
left = reactive(c("cty", "hwy")),
right = reactive(c("drv", "hwy"))
)
output$summary <- renderText({
sprintf("%d observation(s) selected", nrow(dplyr::filter(df(), selected_)))
})
}
shinyApp(ui, server)
Drop the parens after car_data:
df <- callModule(linkedScatter, "scatters", car_data,
left = reactive(c("cty", "hwy")),
right = reactive(c("drv", "hwy"))
)
The module seems to want "unresolved" reactives. The parentheses "resolves" them.
If you want to pass input which is not part of the module just wrap it around reactive() as stated in a tutorial.
If a module needs to access an input that isn’t part of the module,
the containing app should pass the input value wrapped in a reactive
expression (i.e. reactive(...)):
callModule(myModule, "myModule1", reactive(input$checkbox1))
Update:
As correctly stated in another answer and Joe Cheng correct way to pass reactive expression is without brackets ()
callModule(linkedScatter, "scatters", car_data)
One option is also to modularize your API input function so you don't need to define reactive expression outside modules. Example of modularized input can be found from this answer.
Below your code with right answer.
library(shiny)
library(ggplot2)
linkedScatterUI <- function(id) {
ns <- NS(id)
fluidRow(
column(6, plotOutput(ns("plot1"), brush = ns("brush"))),
column(6, plotOutput(ns("plot2"), brush = ns("brush")))
)
}
linkedScatter <- function(input, output, session, data, left, right) {
# Yields the data frame with an additional column "selected_"
# that indicates whether that observation is brushed
dataWithSelection <- reactive({
brushedPoints(data(), input$brush, allRows = TRUE)
})
output$plot1 <- renderPlot({
scatterPlot(dataWithSelection(), left())
})
output$plot2 <- renderPlot({
scatterPlot(dataWithSelection(), right())
})
return(dataWithSelection)
}
scatterPlot <- function(data, cols) {
ggplot(data, aes_string(x = cols[1], y = cols[2])) +
geom_point(aes(color = selected_)) +
scale_color_manual(values = c("black", "#66D65C"), guide = FALSE)
}
ui <- fixedPage(
h2("Module example"),
linkedScatterUI("scatters"),
textOutput("summary")
)
server <- function(input, output, session) {
data(mpg)
### My modification
### making the reactive outside of module call
car_data <- reactive({
mpg
})
## Fix This doesn't work by reactive (var) no brackets()
## What is the syntax for being able to call car_data()?
df <- callModule(linkedScatter, "scatters", reactive(car_data),
left = reactive(c("cty", "hwy")),
right = reactive(c("drv", "hwy"))
)
output$summary <- renderText({
sprintf("%d observation(s) selected", nrow(dplyr::filter(df(), selected_)))
})
}
shinyApp(ui, server)

Resources