In R Shiny, Is there a way of capturing a particular instance of reactive value so then that instance is totally unreactive?
So I'd have a table made up of reactive values and when the user hits the submit button those values are copied over to an un reactive table which I can then go on to manipulate etc.
So in the following code, the user enters their values into a table from rhandsontable package (which is awesome btw), and all I am trying to do is convert it to a basic data frame namely tabplot which should be unreactive so I can go ahead and do any type of operations on it.
library(shiny)
library(rhandsontable)
seq1 <- seq(1:6)
mat1 <- matrix(seq1, 2)
tabplot<-data.frame(car=numeric(2),num=numeric(2),truck=numeric(2))
did_recalc <- FALSE
ui <- fluidPage(
rHandsontableOutput('table'),
tableOutput('result'),
tableOutput('kl'),
textOutput('ca'),
actionButton("goButton","Confirm"),
actionButton("checkButton","Apply"),
br(),
actionButton("recalc", "Return to original values")
)
server <- function(input,output,session)({
tabplot<-data.frame(car=numeric(2),num=numeric(2),truck=numeric(2))
seq1 <- seq(1:6)
mat1 <- matrix(seq1, 2)
mat1<-data.frame(mat1)
#creates reactive values for the data frame
#obviously they have to be reactive values to function with the rhandsontable which is being continuously updated
#as the documentation says "values taken from the reactiveValues object are reactive but the object itself is not
values <- reactiveValues(data=mat1)
#if recalc --- which connects to an action button in the ui is hit, values goes back to original data frame
observe({
input$recalc
values$data<-mat1
})
#Where the magic happens
output$table <- renderRHandsontable({
rhandsontable(values$data,selectCallback = TRUE)
})
#this changes the handsontable format to an r object
observe({
if(!is.null(input$table))
values$data <-hot_to_r(input$table)
})
#Here we create a reactive function that creates a data frame of the rhandsontable output but it is a reactive function
fn<-reactive({
co<-data.frame((values$data))
return(co)
})
#Bit of testing, this demonstrates that the fn() is only updated after the button is pressed
output$result<-renderTable({
input$goButton
isolate({
fn()
})
})
isolate({
# tabplot<-reactive({ #Format co[desired row:length(colums)][desired column]
tabplot[1,1:3][1]<-fn()[1,1:3][1]
tabplot[1,1:3][2]<-fn()[1,1:3][2]
tabplot[1,1:3][3]<-fn()[1,1:3][3]
tabplot[2,1:3][1]<-fn()[2,1:3][1]
tabplot[2,1:3][2]<-fn()[2,1:3][2]
tabplot[2,1:3][3]<-fn()[2,1:3][3]
})
output$kl<-renderTable({
tabplot
})
observe({
input$goButton
output$ca<-renderText({
tabplot$car
cat('\nAccessing Subset with $:', tabplot$car)
cat('\nAccessing specific cell:',tabplot[1,3])
cat('\noperations on specific cell:',tabplot[1,3]*2)
})
})
})
shinyApp(ui = ui, server = server)
This might be what you want. It leverages the much scorned <<- operator, but it is what I do when I need to subvert the "lazy reactive" architecture of shiny.
Note I set a parallel dataframe tabplot1 and display it beneath where you display tabplot.
library(shiny)
library(rhandsontable)
seq1 <- seq(1:6)
mat1 <- matrix(seq1, 2)
tabplot<-data.frame(car=numeric(2),num=numeric(2),truck=numeric(2))
did_recalc <- FALSE
ui <- fluidPage(
rHandsontableOutput('table'),
tableOutput('result'),
tableOutput('kl'),
tableOutput('kl1'),
textOutput('ca'),
actionButton("goButton","Confirm"),
actionButton("checkButton","Apply"),
br(),
actionButton("recalc", "Return to original values")
)
server <- function(input,output,session)({
tabplot<-data.frame(car=numeric(2),num=numeric(2),truck=numeric(2))
tabplot1 <- tabplot
seq1 <- seq(1:6)
mat1 <- matrix(seq1, 2)
mat1<-data.frame(mat1)
#creates reactive values for the data frame
#obviously they have to be reactive values to function with the rhandsontable which is being continuously updated
#as the documentation says "values taken from the reactiveValues object are reactive but the object itself is not
values <- reactiveValues(data=mat1)
#if recalc --- which connects to an action button in the ui is hit, values goes back to original data frame
observe({
input$recalc
values$data<-mat1
})
#Where the magic happens
output$table <- renderRHandsontable({
rhandsontable(values$data,selectCallback = TRUE)
})
#this changes the handsontable format to an r object
observe({
if(!is.null(input$table))
values$data <-hot_to_r(input$table)
})
#Here we create a reactive function that creates a data frame of the rhandsontable output but it is a reactive function
fn<-reactive({
co<-data.frame((values$data))
return(co)
})
#Bit of testing, this demonstrates that the fn() is only updated after the button is pressed
output$result<-renderTable({
input$goButton
tabplot1 <<- data.frame(values$data)
colnames(tabplot1) <<- colnames(tabplot)
isolate({
fn()
})
})
isolate({
# tabplot<-reactive({ #Format co[desired row:length(colums)][desired column]
tabplot[1,1:3][1]<-fn()[1,1:3][1]
tabplot[1,1:3][2]<-fn()[1,1:3][2]
tabplot[1,1:3][3]<-fn()[1,1:3][3]
tabplot[2,1:3][1]<-fn()[2,1:3][1]
tabplot[2,1:3][2]<-fn()[2,1:3][2]
tabplot[2,1:3][3]<-fn()[2,1:3][3]
})
output$kl<-renderTable({
tabplot
})
output$kl1<-renderTable({
input$goButton
tabplot1
})
observe({
input$goButton
output$ca<-renderText({
tabplot$car
cat('\nAccessing Subset with $:', tabplot$car)
cat('\nAccessing specific cell:',tabplot[1,3])
cat('\noperations on specific cell:',tabplot[1,3]*2)
})
})
})
shinyApp(ui = ui, server = server)
Yielding:
Related
I am working on building a shiny App. I have used some filters and rendered a data frame and the data frame changes dynamically as per the user input. But I cannot store a particular column value from a data frame into a vector. I need to store the reactive output every time into a vector so that I can use the values later again. Here the values are stored in text_vec and i need to pass that into the API but I cannot access the values from text_vec and i have to pass the updated values every time into the API
library(dplyr)
library(shiny)
shinyApp(ui = fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "cyl",
label = "Number cylinders:",
choices = c("all",sort(unique(mtcars$cyl))),
selected = "all"),
actionButton("capture",
"capture value")
), # closes sidebarPanel
mainPanel(
tableOutput("text"),
tableOutput("text2"),
tableOutput("text3"),
tableOutput("table")
) # closes mainPanel
) # closes sidebarLayout
), # closes fluidPage
server = function(input, output) {
# some example reactive data
cars_react <- reactive({
mtcars %>%
filter(cyl == input$cyl | input$cyl == "all")
})
# simply global assignment of a reactive vector
observeEvent(cars_react(), {
# here is a globally assigned vector taken from the reactive data
# reused in a render statement it will not react to change, since it is not reactive
test_vec3 <<- unique(cars_react()$hp)
})
# here a file is written to the working directory of your shiny app
# everytime cars_react() changes write (and overwrite) vector to a file
observeEvent(cars_react(), {
test_vec = unique(cars_react()$hp)
saveRDS(test_vec, file = "test_vec.Rdata")
})
# same as above but the file is gradually growing and not overwritten
# everytime cars_react() changes add vector to a (over several sessions growing) list
observeEvent(cars_react(), {
test_vec2 = unique(cars_react()$hp)
if (file.exists("test_list.Rdata")) {
temp = readRDS("test_list.Rdata")
test_list = c(temp, list(test_vec2))
} else {
test_list = list(test_vec2)
}
saveRDS(test_list, file = "test_list.Rdata")
})
# here we access the reactive data with isolate and make it non-reactive, but can update the values through a button click
text_vec <<- eventReactive(input$capture, {
isolate(unique(cars_react()$hp))
})
# output of our reactive data as table
output$table <- renderTable({
cars_react()
})
# text output of globally assigned non-reactive vector test_vec3 (not changing!)
output$text <- renderText({
test_vec3
})
# you can capture values of reactives with isolate, but then, they don't change anymore
# text output of isolated formely reactive vector unique(cars_react()$hp (not changing!)
output$text2 <- renderText({
isolate(unique(cars_react()$hp))
})
# text output of new reactive vector (changes when input$capture button is clicked)
output$text3 <- renderText({
text_vec()
})
for (i in text_vec)
{
url = "https://oscar.com/prweb/PRRestService/"
parameters<-'{
{
"Reference":"Account"
,"ReferenceValue":""
}'
b<-fromJSON(parameters)
b["ReferenceValue"]=i
r <- POST(url, body = parameters,encode = "json")
r_c<-toJSON(content(r))
print(r_c)
}
}
)
A simple way to get a data frame to persist across all environments used within your Shiny app, is to use the '<<-' assignment instead of the '<-" assignment. This is not a great programming technique, but it may be what you're hoping to find.
# To get a data frame to persist, use
a <<- b
# instead of
a <- b
** Updated answer **
Based on your updated answer, I would wrap you API call into an observeEvent which gets triggered once the action button is pressed. Since you do not provide a working example with some real code, I am not sure whether the example below is of help. I further assume that your for loop is correct and working (on my end, I cannot know without a real API and some real values).
library(dplyr)
library(shiny)
library(httr)
library(jsonlite)
shinyApp(ui = fluidPage(
selectInput(inputId = "cyl",
label = "Number cylinders:",
choices = c("all",sort(unique(mtcars$cyl))),
selected = "all"),
actionButton("capture",
"capture value")
), # closes fluidPage
server = function(input, output) {
# some example reactive data
cars_react <- reactive({
mtcars %>%
filter(cyl == input$cyl | input$cyl == "all")
})
# here we access the reactive data with isolate and make it non-reactive, but can update the values through a button click
observeEvent(input$capture, {
for (i in unique(cars_react()$hp))
{
url = "https://oscar.com/prweb/PRRestService/"
parameters<-'{
"Reference":"Account"
,"ReferenceValue":""
}'
b<-fromJSON(parameters)
b["ReferenceValue"]=i
r <- POST(url, body = parameters,encode = "json")
r_c<-toJSON(content(r))
print(r_c)
}
})
}
)
Old answer
It is not clear from your question how, where and how often you want to use the vector of your reactive data frame. But it is an important question, since the concept of reactivity and how to access it is very hard to grasp when you come from a pure non reactive R environment.
Below is a simple example app which shows how to access vectors in reactive data frames, and how they could be used.
I hope it helps to get a better understanding of reactivity in shiny.
library(dplyr)
library(shiny)
shinyApp(ui = fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "cyl",
label = "Number cylinders:",
choices = c("all",sort(unique(mtcars$cyl))),
selected = "all"),
actionButton("capture",
"capture value")
), # closes sidebarPanel
mainPanel(
tableOutput("text"),
tableOutput("text2"),
tableOutput("text3"),
tableOutput("table")
) # closes mainPanel
) # closes sidebarLayout
), # closes fluidPage
server = function(input, output) {
# some example reactive data
cars_react <- reactive({
mtcars %>%
filter(cyl == input$cyl | input$cyl == "all")
})
# simply global assignment of a reactive vector
observeEvent(cars_react(), {
# here is a globally assigned vector taken from the reactive data
# reused in a render statement it will not react to change, since it is not reactive
test_vec3 <<- unique(cars_react()$hp)
})
# here a file is written to the working directory of your shiny app
# everytime cars_react() changes write (and overwrite) vector to a file
observeEvent(cars_react(), {
test_vec = unique(cars_react()$hp)
saveRDS(test_vec, file = "test_vec.Rdata")
})
# same as above but the file is gradually growing and not overwritten
# everytime cars_react() changes add vector to a (over several sessions growing) list
observeEvent(cars_react(), {
test_vec2 = unique(cars_react()$hp)
if (file.exists("test_list.Rdata")) {
temp = readRDS("test_list.Rdata")
test_list = c(temp, list(test_vec2))
} else {
test_list = list(test_vec2)
}
saveRDS(test_list, file = "test_list.Rdata")
})
# here we access the reactive data with isolate and make it non-reactive, but can update the values through a button click
text_vec <- eventReactive(input$capture, {
isolate(unique(cars_react()$hp))
})
# output of our reactive data as table
output$table <- renderTable({
cars_react()
})
# text output of globally assigned non-reactive vector test_vec3 (not changing!)
output$text <- renderText({
test_vec3
})
# you can capture values of reactives with isolate, but then, they don't change anymore
# text output of isolated formely reactive vector unique(cars_react()$hp (not changing!)
output$text2 <- renderText({
isolate(unique(cars_react()$hp))
})
# text output of new reactive vector (changes when input$capture button is clicked)
output$text3 <- renderText({
text_vec()
})
}
)
I am trying to create a shiny app that has a rhandsontable in it. I want rhandsontable to be able to update its values in one of its columns if the corresponding values in another column is selected/ checked. So far, I have been able to use reactive / observe events to change the output values between two objects but i am unable to wrap my head around it , i.e, how do i make once column of rhandsontable reactive to another column in the same table ?
Here is a simple example of what i am trying to build:
library(shiny)
library(rhandsontable)
ui <- fluidPage(
rHandsontableOutput('table')
)
server <- function(input,output,session)({
data <- data.frame(c1=c(5,10,15), c2=c(3,6,9) , diff=c(0,0,0), select= as.logical( c(FALSE,FALSE,FALSE)))
output$table <- renderRHandsontable({
rhandsontable(data)
})
})
shinyApp(ui = ui, server = server)
So if i check the column 'Select', column 'diff' should produce the difference between column c1 & c2
From what I understand, your goal is to do some calculation depending on the values of some other column. So if for example a box of the third column is checked, you might want to compute the difference between elements of column 1 and 2.
If you had just a data frame, that would be easy, wouldn't it? Well, this is possible using reactive values. The main idea is that you can store the rhandsontable in a data frame in the backend, modify the data frame and then render the modified data frame once again back in the handsontable.
I hope this helps:
For a more detailed example on reactive values you can see
this: http://stla.github.io/stlapblog/posts/shiny_editTable.html
and this : https://www.youtube.com/watch?v=BzE1JmC0F6Q&list=PL6wLL_RojB5wXR3NR3K38sIvexZ_45alY&index=3
library(rhandsontable)
library(shiny)
ui <- fluidPage(
mainPanel(
rHandsontableOutput("hot")
)
)
server = function(input, output, session){
df<- data.frame(c1=c(5,10,15), c2=c(3,6,9) , diff=c(0,0,0), select= as.logical( c(FALSE,FALSE,FALSE)))
values <- reactiveValues(data = df)
observe({
if(!is.null(input$hot)){
values$data <- as.data.frame(hot_to_r(input$hot))
isolate(values$data[,'diff'] <- ifelse(values$data[,'select'], values$data[,'c1']-values$data[,'c2'] ,0))
print(values$data)
output$hot <- renderRHandsontable({
rhandsontable(values$data)
})
}
})
output$hot <- renderRHandsontable({
rhandsontable(values$data)
})
}
shinyApp(ui, server)
I have a problem accessing individual values of a data frame that has been created from rhandsontable package. In the following code co[1] retrieves column 1 which is sensible, but then I try to access col[1,1] being row 1 column 1 produces missing value where TRUE/FALSE needed which is a bizarre error in this circumstance. I have tried accessing it with a list method namely co[[[1]][1] with no luck.
library(shiny)
library(rhandsontable)
did_recalc <- FALSE
ui <- fluidPage(
rHandsontableOutput('table'),
dataTableOutput('result'),
br(),
# tableOutput(),
actionButton("recalc", "Return to original values")
)
server <- function(input,output,session)({
a<-c(1,2,4,5,6,7)
b<-c(1,2,4,5,6,7)
c<-rbind(a,b)
df1<-data.frame(c)
#creates reactive values for the data frame
values <- reactiveValues(data=df1)
#if recalc --- which connects to an action button in the ui is hit, values goes back to original data frame
observe({
input$recalc
values$data <- df1
})
#this changes the handsontable format to an r object
observe({
if(!is.null(input$table))
values$data <- hot_to_r(input$table)
})
#important that you are using the values$data that have come from a reactive function
output$table <- renderRHandsontable({
rhandsontable(values$data)
})
fn<-reactive({
co<-data.frame((values$data))
#output$tst<-renderTable(co[3,2]*2) #So if I convert to a new data frame I can do operations on the columns
#Still need to be able to access individual cells
return(co)
})
output$result<-renderDataTable({
fn()
})
})
shinyApp(ui = ui, server = server)
I get the feeling that the object I am trying to access is not clean or structured in the way I am assuming it is.
I'm building a shiny app that queries an SQL database so the user can ggplot the data. I would like the user to be able to rename factors manually but am struggling to get going. Here is an example of what I want to do:
ui.R
library(markdown)
shinyUI(fluidPage(
titlePanel("Reactive factor label"),
sidebarLayout(
sidebarPanel(
numericInput("wafer", label = h3("Input wafer ID:"), value = NULL),
actionButton("do", "Search wafer"),
textInput("text", label = h3("Factor name to change"), value = ""),
textInput("text", label = h3("New factor name"), value = ""),
actionButton("do2", "Change name")
),
mainPanel(
verbatimTextOutput("waf"),
verbatimTextOutput("que"),
verbatimTextOutput("pos"),
dataTableOutput(outputId="tstat")
)
)
)
)
server.R
# Create example data
Name <- factor(c("Happy", "New", "Year"))
Id <- 1:3
dd <- data.frame(Id, Name)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbWriteTable(con, "dd", dd)
query <- function(...) dbGetQuery(con, ...)
wq = data.frame()
sq = data.frame()
shinyServer(function(input, output){
# create data frame to store reactive data set from query
values <- reactiveValues()
values$df <- data.frame()
# Wait for user to search
d <- eventReactive(input$do, { input$wafer })
# Reactive SQL query
a <- reactive({ paste0("Select * from dd where Id=",d()) })
wq <- reactive({ query( a() ) })
# Check outputs
output$waf <- renderPrint(input$wafer)
output$que <- renderPrint({ a() })
output$pos <- renderPrint( wq()[1,1] )
# observe d() so that data is not added until user presses action button
observe({
if (!is.null(d())) {
sq <- reactive({ query( a() ) })
# add query to reactive data frame
values$df <- rbind(isolate(values$df), sq())
}
})
output$tstat <- renderDataTable({
data <- values$df
})
})
In static R I would normally use data table to rename factors i.e.:
DT <- data.table(df)
DT[Name=="Happy", Name:="Sad"]
But I'm not sure how to go about this with a reactiveValues i.e. values$df.
I have read this (R shiny: How to get an reactive data frame updated each time pressing an actionButton without creating a new reactive data frame?). This lead me to try this but it doesn't do anything (even no error):
observeEvent(input$do2, {
DT <- data.table(values$df)
DT[Name == input$text1, Name := input$text2]
values$df <- data.frame(values$df)
})
Perhaps there is a way around this..maybe there is a way to use an action button to "lock in" the data as a new data frame, which can then be used to rename?
Sorry for such a long winded question. My real app is much longer and more complex. I have tried to strip it down.
Your approach works but there are a few issues in your app.
In ui.R, both textInput have the same id, they need to be different so you can refer to them in the server.R. In the observeEvent you posted, you refer to input$text1 and input$text2 so you should change the id of the textInputs to text1 and text2.
In the observeEvent you posted, the last line should be values$df <- as.data.frame(DT), otherwise it does not change anything.
I am using the rhandsontable package in a Shiny app which should have the following functionality:
the data used in the calculation can be randomly generated, invoked by an actionButton (and when the app starts)
the data can be manually edited by the user via the handsontable object
after manual editing it should be possible to re-generate random data, invoking a new calculation
The following app does exactly that what I want, but I could not figure it out how to get rid of the global variable did_recalc. It is a minimal example, where the data consists of two numeric values which are summed up.
library(shiny)
library(rhandsontable)
did_recalc <- FALSE
ui <- fluidPage(
rHandsontableOutput('table'),
textOutput('result'),
actionButton("recalc", "generate new random vals and calculate")
)
server <- function(input,output,session)({
dataset_generator <- eventReactive(input$recalc, {
df <- as.data.frame(runif(2))
output$table <- renderRHandsontable({rhandsontable(df)})
did_recalc <<- TRUE
df
}, ignoreNULL = FALSE)
output$result <- renderText({
df <- dataset_generator()
if (!is.null(input$table) && !did_recalc)
df <- hot_to_r(input$table)
did_recalc <<- FALSE
sum(df)
})
})
shinyApp(ui = ui, server = server)
If I remove the !did_recalc condition within output$result <- ... then editing the table still invokes a (correct) calculation. But if "recalc" is pressed (after some manual editing was done), then the "recalc" button just generates new random values, but without recalculating the sum.
It seems to me, that input$table can just be changed by manual edits of the table object and does not care about new values given via renderRHandsontable. Hence I need this hack with the global variable, which allows me to track if the user just re-generated the data (causing that input$table is "outdated")
Has anybody an idea how to get the functionality of this example without the global variable?
You could store the data in a reactiveValues and have two observers updating it; one if the button is clicked, one if the table is edited by hand.
In your output$table and output$result, you then just need to use the data that is in the reactiveValues. Here's an example (same ui.R as you posted):
server <- function(input,output,session)({
values <- reactiveValues(data=as.data.frame(runif(2)))
observe({
input$recalc
values$data <- as.data.frame(runif(2))
})
observe({
if(!is.null(input$table))
values$data <- hot_to_r(input$table)
})
output$table <- renderRHandsontable({
rhandsontable(values$data)
})
output$result <- renderText({
sum(values$data)
})
})