Edit a dataframe from R shiny - r

I have been reviewing all sorts of documents all day but I do not see a clear solution to my problem. The issue I am having is that I want to bring in a file locally (.xlsx) into the shiny rmarkdown file. Once the app is hosted, users will be able to go in and edit the dataframe and upon exiting the app, the updated dataframe will reflect the changes made my the user.
---
title: "Component Health Dashboard"
output:
flexdashboard::flex_dashboard:
orientation: rows
social: menu
runtime: shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = F)
library(flexdashboard)
library(tidyverse)
library(readxl)
```
Dashboard
=======================================================================
## Table
```{r cars, echo= F}
cars<-read_xlsx("cars.xlsx")
DT::renderDataTable(cars, editable = T,rownames = F)
```
I have referenced this question r shiny: updating rhandsontable from another rhandsontable & Using Shiny to update dataframe values and access it in the local environment after Shiny session ends, but it only seems to work when a DF is in the global environment for a UI/Server. I feel like there is a much better way of doing this. Any help in pushing me into the correct direction or other idea is greatly appreciated.

Instead of using flex_dashboard, let me suggest you use shiny, something like the following.
library(shiny)
library(DT)
cars <- data.frame(rbind(c(1,2,3), c(4,5,6), c(7,8,9)))
ui <- fluidPage(
mainPanel(
HTML("<br><br><b>Double click on a cell in this table. Enter a number.</b><br><br>"),
DTOutput("table1"),
HTML("<br><br><b>See the change reflected in this table.</b><br><br>"),
DTOutput("table2")
)
)
server <- function(input, output) {
RV <- reactiveValues(data = NULL)
RV$data <- cars
observeEvent(input$table1_cell_edit, {
row <- input$table1_cell_edit$row
clmn <- input$table1_cell_edit$col + 1
val <- input$table1_cell_edit$value
RV$data[row, clmn] <- as.numeric(val)
})
output$table1 <- renderDT({
datatable(RV$data, options = list(dom = 't'), editable = TRUE, rownames = F)
})
output$table2 <- renderDT({
datatable(RV$data, options = list(dom = 't'), editable = FALSE, rownames = F)
})
}
shinyApp(ui = ui, server = server)

Related

Filtering data with selections and checkboxes in Flexdashboard

In a flexdashboard, I want to allow a selection by having selectInput() combined with a checkboxGroupInput(). But the data is not updated correctly and I cannot figure out what is wrong.
I used the toy example from this SO-example [LINK] and included a checkbox. It seems that the Checkbox is still ignored and messes up the other parts of the code.
It would be great if someone could adjust the code so that selection can be based on one of the three columns Team, Name or Year.
---
title: "example"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(DT)
library(shiny)
users <- data.frame(
Name = c("Allan A","Barbara B","Charles C","Darwin D","Evelyn E","Frank F","Greg G","Hans H"),
Team = c(1,2,3,3,2,1,2,2),
Year = c(1999,1999,1999,2000,2000,2000,2003,2003),
stringsAsFactors = FALSE)
```
Inputs {.sidebar}
=======================================================================
### Input Variables
```{r global_input}
### selection Input based on columns Team and Name
selectInput("teaminput","Team",c("All", unique(users$Team)), selected="All")
selectInput("userinput","User Name", c("All", unique(users$Name) ), selected="All")
### Add Input from CheckboxGroup based on Year starting with all
checkboxGroupInput("yearinput", label = "Year",choices = unique(users$Year), selected=unique(users$Year), inline = TRUE)
### Filter Team based on one of the three choices (Team, Name, Year)
teamFiltered <- reactive(users[input$teaminput=="All" |
users$Team==input$teaminput |
users$Year ==input$yearinput,])
### Observe if userinput changes based on SELECTION
observe(updateSelectInput(session,"userinput",
choices = c( unique(teamFiltered()$Name)),
selected="All"))
### Observe if yearinput changes based on CHECK BOX
observe(updateCheckboxGroupInput(session,"yearinput",
choices = teamFiltered()$Year,
selected="All"))
```
Results
=======================================================================
### Intake Coordinator KPIs
```{r daily_table}
userFiltered <- reactive(teamFiltered()[input$userinput=="All" |
teamFiltered()$Name==input$userinput |
teamFiltered()$Year==input$yearinput,])
renderDataTable(userFiltered())
```
Try this
library(DT)
library(shiny)
users <- data.frame(
Name = c("Allan A","Barbara B","Charles C","Darwin D","Evelyn E","Frank F","Greg G","Hans H"),
Team = c(1,2,3,3,2,1,2,2),
Year = c(1999,1999,1999,2000,2000,2000,2003,2003),
stringsAsFactors = FALSE)
ui <- fluidPage(
### selection Input based on columns Team and Name
selectInput("teaminput","Team",c("All", unique(users$Team)), selected= "All", multiple = TRUE),
selectInput("userinput","User Name", c("All", unique(users$Name) ), selected="All", multiple = TRUE),
### Add Input from CheckboxGroup based on Year starting with all
checkboxGroupInput("yearinput", label = "Year",choices = unique(users$Year), selected=unique(users$Year), inline = TRUE),
DTOutput("t1")
)
server <- function(input, output, session) {
### Filter Team based on one of the three choices (Team, Name, Year)
teamFiltered <- reactive(users["All" %in% input$teaminput |
users$Team %in% input$teaminput,])
### Observe if userinput changes based on SELECTION
observeEvent(c(input$yearinput,input$teaminput), {
updateSelectInput(session,"userinput", choices = c( unique(teamFiltered()$Name)), selected=unique(teamFiltered()$Name)
)
})
### Observe if yearinput changes based on CHECK BOX
observeEvent(c(input$userinput,input$teaminput), {
updateCheckboxGroupInput(session,"yearinput", choices = unique(teamFiltered()$Year),
selected=unique(teamFiltered()$Year))
})
### Intake Coordinator KPIs
userFiltered <- reactive(teamFiltered()[ "All" %in% input$userinput |
teamFiltered()$Name %in% input$userinput &
teamFiltered()$Year %in% input$yearinput,])
output$t1 <- renderDT(userFiltered())
}
shinyApp(ui, server)

organize reactivity with pre-loaded data

I have couple of tables stored each in a separate txt file, and also combined in a single Rdat file, something like this:
# generate tables
df.A <- data.frame(colA1=letters[1:20],colA2=LETTERS[1:20], stringsAsFactors = F)
df.B <- data.frame(colB1=rnorm(20),colB2=rnorm(20), stringsAsFactors = F)
# save tables as separate files
write.csv(df.A, 'tableA.txt')
write.csv(df.B, 'tableB.txt')
# I may have more tables
# save all tables in a single Rdat file
save(df.A,df.B, file = 'alldata.RDat')
Now in my shiny markdown document, I want to pre-load those tables from the RDat file, but also give user a possibility to re-load them a) separately from individual files, or b) altogether from the RDat file. Here is the first version I came to:
---
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
```{r}
load('alldata.RDat')
```
Tables below:
```{r}
inputPanel(
actionButton('load.A','Reload Table A'),
actionButton('load.B','Reload Table B'),
actionButton('load.Rdat','Load all from Rdat file')
)
tabsetPanel(type = "tabs",
tabPanel("table A", dataTableOutput("toA")),
tabPanel("table B", dataTableOutput("toB"))
)
# next 2 lines render pre-loaded tables before any buttons are pressed:
output$toA <- renderDataTable(df.A, options=list(pageLength=5))
output$toB <- renderDataTable(df.B, options=list(pageLength=5))
observeEvent(input$load.A, {
df.A <- read.csv('tableA.txt');
output$toA <- renderDataTable(df.A, options=list(pageLength=5))
})
observeEvent(input$load.B, {
df.B <- read.csv('tableB.txt');
output$toB <- renderDataTable(df.B, options=list(pageLength=5))
})
observeEvent(input$load.Rdat, {
load('alldata.RDat', verbose = T);
output$toA <- renderDataTable(df.A, options=list(pageLength=5));
output$toB <- renderDataTable(df.B, options=list(pageLength=5))
})
```
It works fine, but I have a feeling it is not the proper way to use reactivity. For example, every time after updating my table object (df.A or dfB), I have to call render explicitly output$toA <- renderDataTable(...) (3 times for each table!).
One solution I had in mind was to put my tables into reactiveValues(), which gave the following:
---
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
```{r}
load('alldata.RDat')
```
Tables below:
```{r}
inputPanel(
actionButton('load.A','Reload Table A'),
actionButton('load.B','Reload Table B'),
actionButton('load.Rdat','Load all from Rdat file')
)
tabsetPanel(type = "tabs",
tabPanel("table A", dataTableOutput("toA")),
tabPanel("table B", dataTableOutput("toB"))
)
rv <- reactiveValues(df.A = df.A, df.B = df.B)
output$toA <- renderDataTable(rv$df.A, options=list(pageLength=5))
output$toB <- renderDataTable(rv$df.B, options=list(pageLength=5))
observeEvent(input$load.A, {
rv$df.A <- read.csv('tableA.txt');
})
observeEvent(input$load.B, {
rv$df.B <- read.csv('tableB.txt');
})
observeEvent(input$load.Rdat, {
load('alldata.RDat', verbose = T);
rv$df.A <- df.A;
rv$df.B <- df.B;
})
```
This looks better because I have only one call of each renderDataTable() in my code. But now each of my tables are stored in memory twice - one in df.A (as loaded from 'alldata.RDat') and another one in rv$df.A... Does it make sense at all, or there's a more kosher way to solve these kinds of things?
You would like to load the data for df.A and df.B from either a csv or Rdat file. This almost certainly requires an intermediate variable, rv as you have correctly created.
The second example you have created is much better than the first, we must avoid multiple render calls to the same output, especially render calls inside an observer.
The code below executes load('alldata.RDat', verbose = T) from within the observer and the initial values of rv are set from within the observer instead of using the global load('alldata.RDat', verbose = T). This prevents the data from being loaded twice.
---
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
```{r}
#load('alldata.RDat')
```
Tables below:
```{r}
inputPanel(
actionButton('load.A','Reload Table A'),
actionButton('load.B','Reload Table B'),
actionButton('load.Rdat','Load all from Rdat file')
)
tabsetPanel(type = "tabs",
tabPanel("table A", dataTableOutput("toA")),
tabPanel("table B", dataTableOutput("toB"))
)
rv <- reactiveValues(df.A = NULL, df.B = NULL)
output$toA <- renderDataTable({
req(rv$df.A)
rv$df.A
}, options=list(pageLength=5))
output$toB <- renderDataTable({
req(rv$df.B)
rv$df.B
}, options=list(pageLength=5))
observeEvent(input$load.A, {
rv$df.A <- read.csv('tableA.txt');
})
observeEvent(input$load.B, {
rv$df.B <- read.csv('tableB.txt');
})
observeEvent(c(input$load.Rdat), {
load('alldata.RDat', verbose = T);
rv$df.A <- df.A;
rv$df.B <- df.B;
})
```

Pass reactive() element into user defined function that makes a shinyApp()

I am making a shiny dashboard that contains lots of tables that should appear and behave identically, but contain different data. In order to make my code more modular, I have written a function that will hold the shinyApp() call so that it can be reused. Here is a link to the official shiny documentation showing the use of shinyApp() within a user defined function.
The function works to an extent, because it renders a datatable, but it does not react to changes in the reactive data frame that is input.
Here is the function I have written, along with the libraries I've used and the sidebar that the datatable should be reacting to:
---
title: "Test"
runtime: shiny
output:
flexdashboard::flex_dashboard:
navbar:
orientation: rows
vertical_layout: fill
---
```{r packages, include = FALSE}
library(flexdashboard)
library(DT)
library(dplyr)
library(shiny)
library(datasets)
test_data <- datasets::CO2
data_chart <- function(shiny_data, col_names){
shinyApp(
ui = fluidPage(
DT::dataTableOutput("results")
),
server = function(input, output, session) {
output$results <- DT::renderDataTable(
datatable(
shiny_data,
filter = 'top',
colnames = col_names,
rownames = FALSE,
options = list(
dom = 'lptpi',
scrollY = '',
order = list(3, 'desc')
)
)
)
}
)
}
```
Header
===
Sidebar {.sidebar}
---
```{r}
checkboxGroupInput(
inputId = 'Plant1',
label = h4("Age Group"),
choices = list('Qn1', 'Qn2', 'Qn3', 'Qc1', 'Qc2', 'Qc3', 'Mn1', 'Mn2', 'Mn3', 'Mc1', 'Mc2', 'Mc3'),
selected = c('Qn1', 'Qn2', 'Qn3', 'Qc1', 'Qc2', 'Qc3', 'Mn1', 'Mn2', 'Mn3', 'Mc1', 'Mc2', 'Mc3')
)
```
Row
---
```{r}
data_1 = reactive({test_data %>%
filter(Plant %in% input$Plant1) %>%
data.frame()
})
data_chart(test_data, c('Plant', 'Type', 'Treatment', 'Conc', 'Uptake'))
I think the problem has something to do with the fact that within the function, I refer to the reactive data set "shiny_data()" as "shiny_data" (without parenthesis), which makes it behave statically. However, I have tried adding the parenthesis and I get the error:
ERROR: could not find function "shiny_data()"
Furthermore, I have tried wrapping various sections of the code with reactive({}) to try to explicitly define the data as reactive, but with no luck.
I'm confident this isn't an issue with having the shinyApp() within a function, because when I make a function that I have hard coded the reactive data_1() data frame, it reacts to changes in the sidebar. Here is an example of this result (the function is exactly the same as above, but instead of passing shiny_data into the function, data_1() is hardcoded in):
Row
---
```{r}
data_chart2 <- function(col_names){
shinyApp(
ui = fluidPage(
DT::dataTableOutput("results")
),
server = function(input, output, session) {
output$results <- DT::renderDataTable(
serverTable <- datatable(
data_1(),
filter = 'top',
colnames = col_names,
rownames = FALSE,
options = list(
dom = 'lptpi',
scrollY = '',
order = list(3, 'desc')
)
)
)
}
)
}
data_chart2(c('Plant', 'Type', 'Treatment', 'Conc', 'Uptake'))
```
As you can see in both provided examples, the data in the rendered datatable only reacts when the reactive dataset is explicitly called in the function. is there any way that at the time the data is input into/called by the user defined function, it can be treated as a reactive element?
Thank you for you help!
To be able to pass a reactive to a function, you need to pass it without brackets.
data_chart(data_1, c('Plant', 'Type', 'Treatment', 'Conc', 'Uptake'))
You can use it within the function with the name of the argument + brackets: shiny_data()
Full data_chart function:
data_chart <- function(shiny_data, col_names){
shinyApp(
ui = fluidPage(
DT::dataTableOutput("results")
),
server = function(input, output, session) {
output$results <- DT::renderDataTable(
datatable(
shiny_data(),
filter = 'top',
colnames = col_names,
rownames = FALSE,
options = list(
dom = 'lptpi',
scrollY = '',
order = list(3, 'desc')
)
)
)
}
)
}
Output

Select multiple items for search keyword by pressing Enter in R Shiny input

I am trying to select items in Shiny's select input by using entered search keyword and pressing Enter to select all matching items for keyword.
The observe function in the snippet works if I provide an item like ALL that is already present in the list but I want it to work for any typed keyword. e.g. App and hit Enter to select all matching items.
It will be interesting to see if there are other custom options that can be coded using jquery or something else to capture the typed input and capture filtered items. Or may be some regex instead of the "ALL" that I used in the if condition.
---
title: "search and select multiple items by pressing Enter"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
```
Column {.sidebar data-width=300}
-----------------------------------------------------------------------
```{r}
#####################
### Reactive Parameters
Parameters <- reactive({
c("ALL","Apple","App","Application","Approximate","Appointment","Ap_titude","Apricot","B","Ball","Bat","Battery")
})
output$params = renderUI({
selectInput(
'params',
'Parameters',
choices = Parameters(),
multiple = TRUE,
selectize = TRUE
)
})
observe({
if("ALL" %in% input$params){
param_selection <- setdiff(Parameters(), "ALL")
} else {
param_selection <- input$params
}
updateSelectInput(session, "params", selected = as.character(unlist(param_selection)))
})
uiOutput("params")
```
Column
-----------------------------------------------------------------------
### Summary
```{r}
```
I found help for selectize.js . It was hyperlinked on selectize page of Shiny.
I ended up using the create function to get it to work. Had to use callback instead of return. The selection based on search string was showing undefined, I could not get it to show correct selection. But since I had the observe function through which I was going to updateSelectInput, I did not worry about that.
Here's a sample code that I put together.
---
title: "search and select multiple items by pressing Enter"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
library(dplyr)
```
Column {.sidebar data-width=300}
-----------------------------------------------------------------------
```{r echo=FALSE}
#####################
### Reactive Parameters
Parameters <- reactive({
c("ALL","Apple","App","Application","Approximate","Appointment","Ap_titude","Apricot","B","Ball","Bat","Battery")
})
output$params = renderUI({
selectizeInput(
'params',
'Parameters',
selected = NULL,
choices = Parameters(),
multiple = TRUE,
options = list(
delimiter= ',',
persist= FALSE,
create = I("function(input, callback) {
callback({
'value': input,
'text': input
});
}")
)
)
})
observe({
dt <- as.character(unlist(Parameters()))
if(is.null(input$params)){
return()
} else{
if("ALL" %in% input$params){
param_selection <- setdiff(dt, "ALL")
} else {
param_selection <- dt[grep(paste(input$params, collapse = "|"), dt)]
}
}
updateSelectInput(session, "params", selected = as.character(unlist(param_selection)))
})
uiOutput("params")
```
Column
-----------------------------------------------------------------------
### Summary
```{r}
```
And this is the output:
Search string- "App", add it
The moment you click, "Add App", observe function triggers and updates the selection to all the values that match the keyword.
Hope this helps someone else that faces the same issue like I did.

How can I build multiple inputs into my shiny app with both updating and multiple selection?

I am building a flex dashboard / shiny app with a datatable and trying to build in two inputs as selections for this datatable with an "All" choice for each selection. First question is how can I limit the second selection "user" by the selection of the first choice "team"?
Then, using these inputs, I'd like to subset my data to any combination of the two selections ex. Team All, user "Darwin D" would return a single line datatable with his name, team and other metrics to be added etc.
All code for a flex markdown document below:
---
title: "example"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(shiny)
library(shinydashboard)
library(flexdashboard)
library(magrittr)
library(feather)
library(anytime)
library(data.table)
library(DT)
library(datasets)
Name <- c("Allan A","Barbara B","Charles C","Darwin D","Evelyn E","Frank F","Greg G","Hans H")
Team <- c(1,2,3,3,2,1,2,2)
users <- data.frame(Name,Team)
remove(Name,Team)
```
Inputs {.sidebar}
=======================================================================
### Input Variables
```{r global_input}
# input variable to call selection, name of field, selections/options variable
dateRangeInput('dateRange',
label = 'Date range input: yyyy-mm-dd',
start = Sys.Date() - 8,
end = Sys.Date() - 1,
min = "2013-01-01",
max = Sys.Date() -1
)
selectInput("teaminput","Team", c("All",unique(users$Team)))
observe({
if( input$teaminput == "All" ) {
subDT <- copy( users )
} else {
subDT <- users[ users$Team == input$teaminput, ]
}
updateSelectInput(
"userinput",
label = "User Name",
choices = c( "All", unique(subDT$Name ) )
)
})
```
### Intake Coordinator KPIs
```{r daily_table}
# reactive data object based on inputs above
daily_dt <- reactive({
if(input$teaminput == "All"){
subDT
} else{
subset(subDT$Team == input$teaminput)
}
})
# render DT datatable object with sorts/search
renderDataTable(daily_dt())
```
You might want to use 2 reactive, the first one to filter the data.frame by Team, the second to filter the result of the first one by Name :
---
title: "example"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(DT)
users <- data.frame(
Name = c("Allan A","Barbara B","Charles C","Darwin D","Evelyn E","Frank F","Greg G","Hans H"),
Team = c(1,2,3,3,2,1,2,2), stringsAsFactors = FALSE)
```
Inputs {.sidebar}
=======================================================================
### Input Variables
```{r global_input}
selectInput("teaminput","Team",c("All", unique(users$Team)), selected="All")
selectInput("userinput","User Name", c("All", unique(users$Name) ), selected="All")
teamFiltered <- reactive(users[input$teaminput=="All" | users$Team==input$teaminput,])
observe(updateSelectInput(session,"userinput", choices = c("All", unique(teamFiltered()$Name)), selected="All"))
```
Results
=======================================================================
### Intake Coordinator KPIs
```{r daily_table}
userFiltered <- reactive(teamFiltered()[input$userinput=="All" | teamFiltered()$Name==input$userinput,])
renderDataTable(userFiltered())
```
Note I can't test this unless you provide a reproducible example, but something along these lines should work. You need a reactive function in your server, which includes a subsetting step, and an updateSelectInput call. This will update the input in your ui any time the reactive is triggered.
observe({
if( input$team == "All" ) {
subDT <- copy( DT )
} else {
subDT <- DT[ Team == input$team, ]
}
updateSelectInput(
"user",
label = "User Name",
choices = c( "All", unique( subDT$Name ) )
)
})
So any time input$team is changed, we create a subset based on that selection, and use that subset to update the user input field.

Resources