Accessing individual values within rhandsontable - r

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.

Related

Shiny App: How to collect all text inputs into a data frame without listing them individually (how to index reactive values?)

I have a tab of my app where I display a bunch of text inputs based on a three-column data frame that contains: variable_name, text_prompt, and example_data. The code below seems to work fine since it displays how I want it to. Eventually, I will be feeding it different data frames, depending on the circumstances, so I need to be able to do everything programmatically.
library(shiny)
library(tidyverse)
library(DT)
additional.data.fields <- tibble (var.name = c("project.id", "director.name"),
prompt.text = c("Enter Project ID", "Enter Director's name"),
var.value = c("e.g. 09-111", "e.g. Paul Smith"))
ui <- fluidPage(
tabsetPanel(
#Generate Input fields from dataframe
tabPanel("Input", #value = "input.2",
# Generate input fields with pmap
actionButton("submit", "Submit"),
pmap(additional.data.fields, ~textInput(..1, ..2, value = ..3)),
),
#Output data to tell if it updates with button click
tabPanel("Output", value = "output",
DT::dataTableOutput("data")
)
)
)
server <- function(input, output, session) {
# Create a reactive values object to store the input data
values <- reactiveValues()
# Set the reactive values object when the submit button is clicked
observeEvent(input$submit, {
var.names <- pull(additional.data.fields, var.name)
#THIS IS THE PART I DON'T KNOW HOW TO DO
#input.data <- ???
#I'll add dummy data so that the program loads
input.data <- tibble(var.names,
temp = 1:length(var.names))
values$data <- input.data
})
# Render the input data table
output$data <- DT::renderDataTable({
values$data
})
}
shinyApp(ui, server)
But what I want - and really have no idea how to do - is to get it back into a data frame after the user hits "submit" (I only need two columns in the subsequent data frame; I don't need the text_prompt data again.)
I know that the user input creates a list of read-only ReactiveValues called "input". But I can't figure out how to do anything with this list besides access using known names (i.e. I know that there is a variable named "project_id" which I can access using input$project_id). But what I want is not to have to write them all out, so that I can change the data used to create the input fields. So I need a way to collect them in a data frame without knowing all the individual names of the variables or even how many there are.
I figured this out on my own. You can't index reactive values with []. However, for some reason you can using [[]].
I would love to know why this is, if anyone has an answer that can help me understand why it works this way.
Here's the key bit of code that I was missing before:
input.data <- tibble (names = var.names,
values = map_chr(var.names, ~input[[.x]]))
The full code that works as I want it is pasted below. I'd still appreciate any feedback or recommendations for improvement.
library(shiny)
library(tidyverse)
library(DT)
additional.data.fields <- tibble (var.name = c("project.id", "director.name"),
prompt.text = c("Enter Project ID", "Enter Director's name"),
var.value = c("e.g. 09-111", "e.g. Paul Smith"))
ui <- fluidPage(
tabsetPanel(
#Generate Input fields from dataframe
tabPanel("Input", #value = "input.2",
# Generate input fields with pmap
actionButton("submit", "Submit"),
pmap(additional.data.fields, ~textInput(..1, ..2, value = ..3)),
),
#Output data to tell if it updates with button click
tabPanel("Output", value = "output",
DT::dataTableOutput("data")
)
)
)
server <- function(input, output, session) {
# Create a reactive values object to store the input data
values <- reactiveValues()
# Set the reactive values object when the submit button is clicked
observeEvent(input$submit, {
var.names <- pull(additional.data.fields, var.name)
input.data <- tibble (names = var.names,
values = map_chr(var.names, ~input[[.x]]))
values$data <- input.data
})
# Render the input data table
output$data <- DT::renderDataTable({
values$data
})
}
shinyApp(ui, server)

R Shiny Handsontable automatically updating function after changing a single cell

I am trying to get familiar with the rhandsontable package. So I tried something I thought should be pretty easy but I can't find a solution. Here is the idea:
I am creating a dataframe with random numbers and in a text box. The mean of column 1 of the dataframe should be displayed. Furthermore, that number should be updated as soon as I change the value of a cell in the dataframe.
My code:
ui <- fluidPage(
textOutput("num"),
rHandsontableOutput(outputId="frame")
)
server <- function(input, output, session) {
datavalue <- reactiveValues(data=df)
observeEvent(input$frame$changes$changes,{
mean_col1 <- mean(datavalue$data[[1]][1:10])
})
output$num <- renderText({
mean(datavalue$data[[1]][1:10])
})
output$frame <- renderRHandsontable({
rhandsontable(datavalue$data)
})
}
shinyApp(ui = ui, server = server)
I think you want to use hot_to_r to convert the handsontable to an R object when there is a change. You can update your reactiveValue datavalue$data when that happens, and your output$num will account for this change as well with the new mean.
Try using this in your observeEvent:
datavalue$data <- hot_to_r(input$frame)
As an alternative, you can do a general observe as follows:
observe({
req(input$frame)
datavalue$data <- hot_to_r(input$frame)
})

Shiny rhandsontable that is reactive to itself

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)

Capturing instance of reactive values?

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:

Update handsontable by editing table and/or eventReactive

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)
})
})

Resources