I struggle at the moment and would require some help.
I have two rhandondables therefore two outputs. Within the render function I make some changes of a dataframe and the user is able to change the dataframe in rhandsonable.
first table:
output$out <- renderRHandsontable({
if (is.null(input$out)) {
hot <- rhandsontable(df())
} else {
hot <- hot_to_r(input$out)
hot <- rhandsontable(hot)
}
})
second table:
output$out2 <- renderRHandsontable({
if (is.null(input$out)) {
hot <- rhandsontable(df())
} else {
hot <- hot_to_r(input$out2)
hot <- rhandsontable(hot)
}
})
To make it a little more clear, lets assume the first table (output$out) shows a table in absolute numbers and the second (output$out2) in percentage. What I would like to point at is that if one updates one table the other table needs to be updated as well. i.e. percentage numbers need to be calculated in absolute numbers and "go back" to a big flat datatable.
Now how do I make this two interactive so that if I update table one the changes will be submitted to table two and the other way around so that always the "most recent" changes are reflected.
Appreciate any help
If both your tables share the same underlying data, and you update the underlying data whenever change happens, then it should work. Below is an simple example where both tables will show the same dataset. Update in one will be reflected in the other.
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel("Syncing two RHandontables"),
sidebarLayout(
sidebarPanel(
),
mainPanel(
rHandsontableOutput("out1"),
tags$hr(),
rHandsontableOutput("out2")
)
)
))
server <- shinyServer(function(input, output) {
data <- reactiveValues(data=head(iris))
output$out1 <- renderRHandsontable({
rhandsontable(data$data)
})
output$out2 <- renderRHandsontable({
rhandsontable(data$data)
})
observeEvent(input$out1, {
data$data <- hot_to_r(input$out1)
})
observeEvent(input$out2, {
data$data <- hot_to_r(input$out2)
})
})
shinyApp(ui = ui, server = server)
Related
I am trying to access the data frame created in one render function into another render function.
There are two server outputs, lvi and Category, in lvi I have created Data1 data frame and Category I have created Data2 dataframe. I want to select Data2 where Data1 ID is matching.
I am following the below steps to achieve my objective but I get error "Object Data1 not found".
My UI is
ui <- fluidPage(
# App title ----
titlePanel("Phase1"),
fluidPage(
column(4,
# Input: Select a file ----
fileInput("file1", "Import file1")
)
),
fluidPage(
column(4,
# Input: Select a file ----
fileInput("file2", "Import File2")
)
),
# Main panel for displaying outputs ----
mainPanel(
# Output: Data file ----
dataTableOutput("lvi"),
dataTableOutput("category")
)
)
My server code is
server <- function(input, output) {
output$lvi <- renderDataTable({
req(input$file1)
Data1 <- as.data.frame(read_excel(input$file1$datapath, sheet = "Sheet1"))
})
output$category <- renderDataTable({
req(input$file2)
Data2 <- as.data.frame(read_excel(input$file2$datapath, sheet = "Sheet1"))
Data2 <- Data2[,c(2,8)]
Data2 <- Data2[Data1$ID == "ID001",]
})
}
shinyApp(ui, server)
Once a reactive block is done executing, all elements within it go away, like a function. The only thing that survives is what is "returned" from that block, which is typically either the last expression in the block (or, when in a real function, something in return(...)). If you think of reactive (and observe) blocks as "functions", you may realize that the only thing that something outside of the function knows of what goes on inside the function is if the function explicitly returns it somehow.
With that in mind, the way you get to a frame inside one render/reactive block is to not calculate it inside that reactive block: instead, create that frame in its own data-reactive block and use it in both the render and the other render.
Try this (untested):
server <- function(input, output) {
Data1_rx <- eventReactive(input$file1, {
req(input$file1, file.exists(input$file1$datapath))
as.dataframe(read_excel(input$file1$datapath, sheet = "Sheet1"))
})
output$lvi <- renderDataTable({ req(Data1_rx()) })
output$category <- renderDataTable({
req(input$file2, file.exists(input$file2$datapath),
Data1_rx(), "ID" %in% names(Data1_rx()))
Data2 <- as.data.frame(read_excel(input$file2$datapath, sheet = "Sheet1"))
Data2 <- Data2[,c(2,8)]
Data2 <- Data2[Data1_rx()$ID == "ID001",]
})
}
shinyApp(ui, server)
But since we're already going down the road of "better design" and "best practices", let's break data2 out and the data2-filtered frame as well ... you may not be using it separately now, but it's often better to separate "loading/generate frames" from "rendering into something beautiful". That way, if you need to know something about the data you loaded, you don't have to (a) reload it elsewhere, inefficient; or (b) try to rip into the internals of the shiny DataTable object and get it manually. (Both are really bad ideas.)
So a slightly better solution might start with:
server <- function(input, output) {
Data1_rx <- eventReactive(input$file1, {
req(input$file1, file.exists(input$file1$datapath))
as.dataframe(read_excel(input$file1$datapath, sheet = "Sheet1"))
})
Data2_rx <- eventReactive(input$file2, {
req(input$file2, file.exists(input$file2$datapath))
dat <- as.dataframe(read_excel(input$file2$datapath, sheet = "Sheet1"))
dat[,c(2,8)]
})
Data12_rx <- reactive({
req(Data1_rx(), Data2_rx())
Data2_rx()[ Data1_rx()$ID == "ID001", ]
})
output$lvi <- renderDataTable({ req(Data1_rx()); })
output$category <- renderDataTable({ req(Data12_rx()); })
}
shinyApp(ui, server)
While this code is a little longer, it also groups "data loading/munging" together, and "render data into something beautiful" together. And if you need to look at early data or filtered data, it's all right there.
(Side note: one performance hit you might see from this is that you now have more copies of data floating around. As long you are not dealing with "large" data, this isn't a huge deal.)
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)
})
I would like to use a Shiny app to load a file (tab-separated), dynamically create a checkboxGroupInput, after the loading of the file (using observeEvent) using the column headers, then subset the data frame that comes from the file based on the selected checkboxes. The data is then plotted using code I can't share right now.
All is working fine, apart from the last bit: subsetting the dataframe based on the selected checkboxes in checkboxGroupInput. The checkboxes all start selected, and the plot is created fine. If you un-select one of the checkboxes, the plot re-plots appropriately for a split second (so the subsetting is working fine) then the unselected checkbox re-selects itself and the plot goes back to the old plot.
This is the tiny problem I'm trying to solve, guessing it's one line of code. I'm assuming it's because of some reactivity that I don't understand and the checkbox constantly resetting itself.
Here is an example:
###
## Some functions I can't share
### Shiny app
library(shiny)
# Define UI
ui <- fluidPage(
# Application title
titlePanel("MagicPlotter"),
# Sidebar
sidebarLayout(
sidebarPanel(
fileInput(inputId = "myInputID",
label = "Your .csv file",
placeholder = "File not uploaded"),
uiOutput("mylist"),
uiOutput("submitbutton")
),
# Show a plot
mainPanel(
verticalLayout(
plotOutput("myPlot"))
)
)
)
# Define server
server <- function(input, output) {
output$myPlot <- renderPlot({
inputfile <- input$myInputID
if(is.null(inputfile))
{return()}
mydataframe <- read.table(file=inputfile$datapath, sep="\t", head=T, row.names = 1)
mydataframecolumnnames <- colnames(mydataframe[1:(length(mydataframe)-1)])
# the last column is dropped because it's not relevant as a column name
observeEvent(input$myInputID, {
output$mylist <- renderUI({
checkboxGroupInput(inputId="mylist",
label="List of things to select",
choices=mydataframecolumnnames,
selected=mydataframecolumnnames)
})
})
observeEvent(input$myInputID, {
output$submitbutton <- renderUI({
submitButton("Subset")
})
})
mysubset <- mydataframe[input$mylist]
myPlot(mysubset)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Thanks all
I think there are a few things that might help...
One, you can move your observeEvent methods outside of your renderPlot.
Also, you can create a reactive function to read in the data table.
I hope this helps.
server <- function(input, output) {
myDataFrame <- reactive({
inputfile <- input$myInputID
if(is.null(inputfile))
{return()}
read.table(file=inputfile$datapath, sep="\t", head=T, row.names = 1)
})
output$myPlot <- renderPlot({
req(input$mylist)
mysubset <- myDataFrame()[input$mylist]
plot(mysubset)
})
observeEvent(input$myInputID, {
mydata <- myDataFrame()
mydataframecolumnnames <- colnames(mydata[1:(length(mydata)-1)])
output$mylist <- renderUI({
checkboxGroupInput(inputId="mylist",
label="List of things to select",
choices=mydataframecolumnnames,
selected=mydataframecolumnnames)
})
})
observeEvent(input$myInputID, {
output$submitbutton <- renderUI({
submitButton("Subset")
})
})
}
I have encountered this problem while developing an app, and reproduced it here in a simplified script using Fruits df.
Basically, i have selectInput box to select a Year, which is a column in Fruits. I create unique list of Years, and feed it into selectInput box.
Then, ideally, i wanted my plot to display only the records for the year I selected. However, as you'll see in my code - the second you uncomment a block of 3 lines to accomplish that, - the plot stops displaying even though there doesn't seem to be any errors. Anybody knows why is this? Thanks in advance!!!
Related question - while debugging this i saw that the input$explore_year is at first "Null". I'm trying to handle this in the code but not sure why the selected="2010" doesn't take care of it automatically.
library(shiny)
library(googleVis)
library(DT)
listOfFruits <- sort(unique(Fruits$Year), decreasing = FALSE)
ui <- fluidPage(title = "Fruits Bug Recreated",
fluidRow(
column(3,
wellPanel(
uiOutput("choose_year"),
br()
)),
column(9,
tags$hr(),
htmlOutput("view")
)),
fluidRow(DT::dataTableOutput("tableExplore"))
)
server <- function(input, output) {
output$view <- renderGvis({
#Uncomment these 3 lines to see how the plot stops displaying.
# local_exloreYear <- input$explore_year
# if (is.null(local_exloreYear)) {local_exloreYear <- "2010"}
# FruitsSubset <- subset(Fruits, Year == local_exloreYear)
#------------I wanted to use the commented line below instead of the
#that follows
#gvisBubbleChart(FruitsSubset, idvar="Fruit",
#-------------
gvisBubbleChart(Fruits, idvar="Fruit",
xvar="Sales", yvar="Expenses",
colorvar="Year", sizevar="Profit",
options=list(
hAxis='{minValue:70, maxValue:125, title:"Sales"}',sortBubblesBySize=TRUE,
vAxis='{title: "Expenses",minValue:60, maxValue:95}'
))
})
# Drop-down selection box for dynamic choice of minutes in the plans to compare
output$choose_year <- renderUI({
selectInput("explore_year", "Select Year", as.list(listOfFruits),selected ="2010")
})
output$tableExplore <- DT::renderDataTable(DT::datatable({
FruitsSubset <- subset(Fruits, Fruits$Year == input$explore_year)
myTable <-FruitsSubset[,c(1,2,3,4,5,6)]
data <- myTable
data
},options = list(searching = FALSE,paging = FALSE)
))
}
shinyApp(ui = ui, server = server)
Like i wrote in the comments you can solve it by make the rendering conditional on the input being non-NULL.
output$view <- renderGvis({
if(!is.null(input$explore_year)){
...
}
})
Nevertheless, I donĀ“t think it is really intended that you have to do that, as in other render functions it is not required e.g. in the DT::renderDataTable(), where you also use the same input (being NULL initially).
Therefore, I would suggest reporting it as a bug.
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)
})
})