Call Variable from reactive data() in R Shiny App - r

I would like to call a certain variable within a reactive expression. Something like this:
server.R
library(raster)
shinyServer(function(input, output) {
data <- reactive({
inFile <- input$test #Some uploaded ASCII file
asc <- raster(inFile$datapath) #Reads in the ASCII as raster layer
#Some calculations with 'asc':
asc_new1 <- 1/asc
asc_new2 <- asc * 100
})
output$Plot <- renderPlot({
inFile <- input$test
if (is.null(inFile)
return (plot(data()$asc_new1)) #here I want to call asc_new1
plot(data()$asc_new2)) #here I want to call asc_new2
})
})
Unfortunately I could't find out how to call asc_new1 and asc_new2 within data(). This one doesn't work:
data()$asc_new1

Reactives are just like other functions in R. You can't do this:
f <- function() {
x <- 1
y <- 2
}
f()$x
So what you're within output$Plot() won't work either. You can do what you want by returning a list from data().
data <- reactive({
inFile <- input$test
asc <- raster(inFile$datapath)
list(asc_new1 = 1/asc, asc_new2 = asc * 100)
})
Now you can do:
data()$asc_new1

"With data()$asc_new1 you wont be able to access the in the reactive context created variables (at least with the current shiny version).
You need data()[1] data()[2] if you put it in a list like MadScone. Calling it with the $ sign would raise
Warning: Unhandled error in observer: $ operator is invalid for atomic vectors
However, the error your getting
Error in data()$fitnew : $ operator not defined for this S4 class
is not only because you access the variable wrong. You named the output of your reactive function data which is reserved name in R. You want to change that to myData or something.

Related

reactiveValues issue

I'm trying to merge two uploaded data frames, output it as a table, then being able to download it and reset the inputs, but only get the error: "Error 'by' must match numbers of columns".
I have trouble understanding reactiveValues I guess, since I can't simply call them as data frames in the app...
library(shiny)
library(shinyjs)
library(readxl)
library(DT)
ui <- fluidPage(
useShinyjs(),
fileInput('inFile1', 'Choose file'),
fileInput('inFile2', 'Choose file'),
actionButton('reset', 'Reset'),
tableOutput('overlap')
)
server <- function(input, output, session) {
rv <- reactiveValues()
observe({
req(input$inFile1)
rv$data1 <- readxl::read_xls(input$inFile1$datapath)
})
observe({
req(input$inFile2)
rv$data2 <- readxl::read_xls(input$inFile2$datapath)
})
observeEvent(input$reset, {
rv$data1 <- NULL
rv$data2 <- NULL
reset('inFile1')
reset('inFile2')
})
dataframe<-reactive({
if (!is.null(rv$data1) | !is.null(rv$data2))
return(NULL)
df <- merge(as.data.frame(rv$data1),as.data.frame(rv$data2),by.x = 1,by.y = 1)
colnames(df) <- c("GeneID",paste0(colnames(rv$data1)[2:ncol(rv$data1)],"_file_1"),
paste0(colnames(rv$data2)[2:ncol(rv$data2)],"_file_2"))
df
})
overlap1 <- reactive({
if(!is.null(dataframe()))
dataframe()
})
output$overlap <- renderDataTable({
datatable(overlap1())
})
}
shinyApp(ui, server)
At a first glance your reactive expressions look fine to me. And given that error message the error is caused by merge(). Taking a closer look there, what strikes me are those is.null checks at the top of the dataframe<-reactive(. The condition (!is.null(rv$data1) | !is.null(rv$data2)) means that you are trying to merge two objects that are NULL because only then the code wont't stop with return(NULL). If one or both rv-values are "Truthy" the code won't run and all the reactive is going to return is NULL.
I used isTruthy() below. I think it helps in two ways:
isTruthy() checks if the values contain anything "usable". That way, you do not have to care about how rv is initialised. It could be NA or integer(0) or anything else that is meaningless. isTruthy handles all these cases. Merging now only takes place when there are two values with "meaningful" data (note that this does not necessarily mean that the data can be coerced to data.frame).
It avoids a complicated double negative in the if-statement.
dataframe <- reactive({
if (isTruthy(rv$data1) && isTruthy(rv$data2)) {
df <- merge(rv$data1, rv$data2, by.x = 1,by.y = 1)
colnames(df) <- c("GeneID", paste0(colnames(rv$data1)[2:ncol(rv$data1)], "_file_1"),
paste0(colnames(rv$data2)[2:ncol(rv$data2)], "_file_2"))
} else df <- NULL
df
})
Final tweak: I removed as.data.frame in the merge statement because the first thing merge is trying to do is coerce the arguments to a data frame.

Error in .getReactiveEnvironment()$currentContext() while using reactive output in reactiveValues function

I'm trying to get a reactiveValue that is depending on a reactive. In the real code (this is a very simplified version), I load a dataset interactively. It changes when pushing the buttons (prevBtn/nextBtn). I need to know the number of rows in the dataset, using this to plot the datapoints with different colors.
The question: Why can't I use the reactive ro() in the reactiveValues function?
For understanding: Why is the error saying "You tried to do something that can only be done from inside a reactive expression or observer.", although ro() is used inside a reactive context.
The error is definitely due to vals(), I already checked the rest.
The code :
library(shiny)
datasets <- list(mtcars, iris, PlantGrowth)
ui <- fluidPage(
mainPanel(
titlePanel("Simplified example"),
tableOutput("cars"),
actionButton("prevBtn", icon = icon("arrow-left"), ""),
actionButton("nextBtn", icon = icon("arrow-right"), ""),
verbatimTextOutput("rows")
)
)
server <- function(input, output) {
output$cars <- renderTable({
head(dat())
})
dat <- reactive({
if (is.null(rv$nr)) {
d <- mtcars
}
else{
d <- datasets[[rv$nr]]
}
})
rv <- reactiveValues(nr = 1)
set_nr <- function(direction) {
rv$nr <- rv$nr + direction
}
observeEvent(input$nextBtn, {
set_nr(1)
})
observeEvent(input$prevBtn, {
set_nr(-1)
})
ro <- reactive({
nrow(dat())
})
output$rows <- renderPrint({
print(paste(as.character(ro()), "rows"))
})
vals <- reactiveValues(needThisForLater = 30 * ro())
}
shinyApp(ui = ui, server = server)
Error in .getReactiveEnvironment()$currentContext() :
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
I think you want
vals <- reactiveValues(needThisForLater = reactive(30 * ro()))
Not everything in a reactiveValues list is assumed to be reactive. It's also a good place to store constant values. So since it's trying to evaluate the parameter you are passing at run time and you are not calling that line in a reactive environment, you get that error. So by just wrapping it in a call to reactive(), you provide a reactive environment for ro() to be called in.

Is it possible to create a user-defined function that takes reactive objects as input? How do I do it?

So, I've been on google for hours with no answer.
I want to create a user-defined function inside the server side that takes inputs that I already know to wrap reactive({input$feature)} but the issue is how to incorporate reactive values as inputs too.
The reason why I want to do this is because I have a navbarPage with multiple tabs that shares elements such as same plots. So I want a user defined function that creates all the similar filtering and not have to create multiple of the same reactive expression with different input and reactive variable names which take up 2000+ lines of code.
server <- function(input, output) {
filtered_JointKSA <- reactiveVal(0)
create_filtered_data <- function(df, input_specialtya, filtered_JointKSA) {
if (input_specialtya == 'manual') {
data <- filter(data, SPECIALTY %in% input_specialtyb)
}
if (filtered_JointKSA != 0) {
data <- filter(data, SPECIALTY %in% filtered_JointKSA)
}
reactive({return(data)})
}
filtered_data <- create_filtered_data(df,
reactive({input$specialty1}),
filtered_JointKSA())
observeEvent(
eventExpr = input$clickJointKSA,
handlerExpr = {
A <- filtered_JointKSA(levels(fct_drop(filtered_data()$`Joint KSA Grouping`))[round(input$clickJointKSA$y)])
A
}
)
This gets me an error:
"Error in match(x, table, nomatch = 0L) :
'match' requires vector arguments"
The error is gone if I comment out where I try to create filtered_data but none of my plots are created because filtered_data() is not found.
What is the correct approach for this?
Ideally, I would like my observeEvents to be inside user defined functions as well if that has a different method.
This example may provide some help, but it's hard to tell without a working example. The change is to wrap the call to your function in reactive({}) rather than the inputs to that function, so that the inputs are all responsive to user input and the function will update.
library(shiny)
ui <- fluidPage(
numericInput("num", "Number", value = NULL),
verbatimTextOutput("out")
)
server <- function(input, output){
## User-defined function, taking a reactive input
rvals <- function(x){
req(input$num)
if(x > 5){x * 10} else {x*1}
}
# Call to the function, wrapped in a reactive
n <- reactive({ rvals(input$num) })
# Using output of the function, which is reactive and needs to be resolved with '()'
output$out <- renderText({ n() })
}
shinyApp(ui, server)

R / R Shiny - global dataframes array (assigned by reference)?

I have a dataframe df1, and subset it to df1sub and display it in an R shiny renderPlot() call. Similarly, I have df2, and I subset it to df2sub, and render it in R shiny via a separate renderPlot() call. Btw these subsets are created based on user choices in an R Shiny app.
Now, I have a datatable that I want to change to reflect whatever the current dataset is, so I wanted some kind of global like:
buffers[1] <- df1sub
buffers[2] <- df2sub
How would I go about defining this global var? I tried separately doing buffers = array() to initialize a global var but then the assignments as I wrote them above don't work?
Update: attempts to use the the '<<-' operator as suggested below yields the following:
buffers <- NULL #don't know how else to initialize. array() yields same error as below.
buffers[1] <<- df # Error in buffers[1] <<- df : object 'buffers' not found
You can adopt this approach:
library(shiny)
shinyServer(function(input, output) {
...
some.reactive.expression <- reactive1({
...
buffers[1] <<- df1sub
buffers[2] <<- df2sub
...
})
})
With some updates:
#In global.R
buffers <- list()
buffers[[1]] <- data.frame()
buffers[[2]] <- data.frame()
buffers[[1]] <- df1 #original dataset, as a default, before subsets are created
buffers[[2]] <- df2 #ditto.
Then in server.R:
r1 <- reactive({
... #create subset of df, then return it
buffers[[1]] <<- subset(...)
buffers[[1]] #return it as dynamic data for plots
})
r2 <- reactive({...}) #ditto
then in renderplot:
output$blah <- renderplot(r1()...)
output$foo <- renderplot(r2()...)
Leaving the global buffers[] var separately available to a 3rd UI widget (e.g. a data table)...

What's the difference between Reactive Value and Reactive Expression?

In Shiny tutorial, there is an example:
fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))
shinyServer(function(input, output) {
currentFib <- reactive({ fib(as.numeric(input$n)) })
output$nthValue <- renderText({ currentFib() })
output$nthValueInv <- renderText({ 1 / currentFib() })
})
I don't get how reactive caches the values. Does it internally do something like return(function() cachedValue)?
Now I am wondering if I can do this?
fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))
shinyServer(function(input, output) {
currentFib <- reactiveValues({ fib(as.numeric(input$n)) })
output$nthValue <- renderText({ currentFib })
output$nthValueInv <- renderText({ 1 / currentFib })
})
Using
currentFib <- reactiveValues({ fib(as.numeric(input$n)) }) will not work in this context.
You will get an error saying that you are accessing reactive values outside of the "reactive context."
However, if you wrap it inside a function call instead, it will work:
currentFib <- function(){ fib(as.numeric(input$n)) }
This works because now the function call is inside a reactive context.
The key difference is the distinction they make in the Shiny documentation, between reactive "sources" and "conductors." In that terminology, reactive({...}) is a conductor, but reactiveValues can only be a source.
Here's how I think of reactiveValues - as a way to extend input which gets specified in UI.R. Sometimes, the slots in input are not enough, and we want derived values based on those input slots. In other words, it is a way to extend the list of input slots for future reactive computations.
Reactive() does what you say -- it returns the value, after re-running the expression each time any reactive Value changes. If you look at the source code for reactive you can see it:
The last line is that cached value that is being returned: Observable$new(fun, label)$getValue where 'fun' is the expression that was sent in the call to reactive.

Resources