I am new to the shiny, I would like to edit different multiple data frames by radio button or selectinput by using rhandsontable package. However, my script can not show other data frame but only the first one, I don't know what is the problem.
ui.R:
library(rhandsontable)
fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("select2", label = h3("Choose to edit"),
choices = list("003.csv", "004.csv", "005.csv",
"006.csv", "007.csv"),
selected = "003.csv"),
actionButton("saveBtn", "Save changes")
),
mainPanel(
rHandsontableOutput("hot")
)))
server.R
values <- reactiveValues()
setHot <- function(x) values[["hot"]] <<- x
function(input, output, session) {
fname <- reactive({
x <- input$select2
return(x)
})
observe({
input$saveBtn # update csv file each time the button is pressed
if (!is.null(values[["hot"]])) {
write.csv(x = values[["hot"]], file = fname(), row.names = FALSE)
}
})
output$hot <- renderRHandsontable({
if (!is.null(input$hot)) { # if there is an rhot user input...
DF <- hot_to_r(input$hot) # convert rhandsontable data to R object
and store in data frame
setHot(DF) # set the rhandsontable values
} else {
DF <- read.csv(fname(), stringsAsFactors = FALSE) # else pull table from the csv (default)
setHot(DF) # set the rhandsontable values
}
rhandsontable(DF) %>% # actual rhandsontable object
hot_table(highlightCol = TRUE, highlightRow = TRUE, readOnly = TRUE) %>%
hot_col("Status", readOnly = FALSE)
})}
I can edit and save the dataframe that it shows the first one 003.csv, however when i use the drop down list to 004.csv, it didn't show the dataframe. please advise.
This will write (and possibly overwrite ⚠ any existing file with) dummy data:
for (i in c("003.csv", "004.csv", "005.csv", "006.csv", "007.csv")) {
write.csv(cbind(V1 = rep(i, 3), Status = "foo"), i, row.names = FALSE)
}
I overhauled server a bit:
library(shiny)
library(rhandsontable)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(
"select2", label = h3("Choose to edit"), selected = "003.csv",
choices = list("003.csv", "004.csv", "005.csv", "006.csv", "007.csv")
),
actionButton("saveBtn", "Save changes")
),
mainPanel(
rHandsontableOutput("hot")
)
)
)
server <- function(input, output, session) {
DF <- reactiveVal()
observe({
DF(read.csv(input$select2, stringsAsFactors = FALSE))
})
observe({
if (!is.null(input$hot)) DF(hot_to_r(input$hot))
})
observeEvent(input$saveBtn, {
if (!is.null(DF())) write.csv(DF(), input$select2, row.names = FALSE)
})
output$hot <- renderRHandsontable({
rhandsontable(DF()) %>%
hot_table(highlightCol = TRUE, highlightRow = TRUE, readOnly = TRUE) %>%
hot_col("Status", readOnly = FALSE)
})
}
shinyApp(ui, server)
Related
Unable to reset the textinput and selectinput. I tried to create the action button reset. Also used observeEvent. could you please help. I want to understand why the reset with observeEvent is not working, also when I manually clear the textinput, the app gives error. Any reason
libraries:
library(shiny)
library(shinyjs)
library(magrittr)
library(tidyverse)
UI part
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Package with datasets and functions"),
div(id='form',
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
textInput('pkg','Package Name', value = NULL),
actionButton("update", "Update View"),
actionButton("reset", "Reset inputs"),
helpText('Please enter the package name for which you want to see the list of datasets and functions (with parameters)'),
br(),
# br(),
selectInput('dat','Datasets', choices = NULL, selected = NULL)
),
# Show a plot of the generated distribution
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("List of Datasets in the Package", DTOutput("dataset1")),
tabPanel("Datasets View", DTOutput("dataset2")),
tabPanel("List of Functions with Parameters in the Package", verbatimTextOutput('func'))
)
)
)
)
)
server part
# Define server logic required to draw a histogram
server <- function(input, output, session) {
pkgs <- reactive({input$pkg})
observeEvent(input$reset, {
#pkgs() <- NULL
updateSelectInput(session, 'dat','Datasets', choices = NULL, selected = NULL)
updateTextInput('pkg','Package Name', value = NULL)
})
#
# if (!is.null(pkgs())){
df <- reactive({
# pksis <- require(input$pkg)
# cat(pksis)
# if (input$pkg %in% rownames(installed.packages()) == TRUE) {
data_name1 <- data(package=input$pkg)
data_name2 <- as_tibble(data_name1$results) %>% rename(name=Item, label=Title) %>% select(-LibPath, -Package)
data_name2
# } else {
# install.packages(input$pkg)
# library(input$pkg)
# data_name1 <- data(package=input$pkg)
# data_name2 <- as_tibble(data_name1$results) %>% rename(name=Item, label=Title) %>% select(-LibPath, -Package)
# data_name2
# }
})
# }
obse <- eventReactive(input$update, { df() })
funct <- eventReactive(input$update, { paste0('package:',input$pkg) })
# if (!is.null(pkg1())){
observe({
req(obse())
updateSelectInput(session, inputId = "dat", label = "Datasets", choices = c(df()$name), selected = df()$name[1])
})
# }
df2 <- reactive({
req(obse())
e <- new.env()
library(package = input$pkg, character.only = TRUE)
out <- data(list=input$dat, package = input$pkg, envir = e)
e[[out]]
# new <- input$dat
# data(new, package = input$pkg)
# cat(new)
})
output$dataset1 <- renderDataTable({
DT::datatable(obse())
})
output$dataset2 <- renderDataTable({
df2()
})
output$func <- renderPrint({
lsf.str(funct())
})
observeEvent(input$reset,{
output$dataset1 <- renderDataTable({
})
output$dataset2 <- renderDataTable({
})
output$func <- renderPrint({
})
})
}
# Run the application
# undebug(shinyApp)
shinyApp(ui = ui, server = server)
The following code makes the order of update and reset clearer in the server part.
library(shiny)
library(shinyjs)
library(magrittr)
library(tidyverse)
require(DT)
ui <- fluidPage(
# Application title
titlePanel("Package with datasets and functions"),
div(id='form',
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
textInput('pkg','Package Name', value = NULL),
actionButton("update", "Update View"),
actionButton("reset", "Reset inputs"),
helpText('Please enter the package name for which you want to see the list of datasets and functions (with parameters)'),
br(),
# br(),
selectInput('dat','Datasets', choices = NULL, selected = NULL)
),
# Show a plot of the generated distribution
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("List of Datasets in the Package", DTOutput("dataset1")),
tabPanel("Datasets View", DTOutput("dataset2")),
tabPanel("List of Functions with Parameters in the Package", verbatimTextOutput('func'))
)
)
)
)
)
server <- function(input, output, session) {
pkgs <- reactive({
req(input$pkg)
})
##### update
observeEvent(input$update, {
updateTextInput(inputId = 'pkg', value = pkgs())
# check if this_package is installed
if(system.file(package = pkgs()) == ""){
updateSelectInput(session, 'dat', choices = NULL , selected = NULL)
}
else{
# data sets in the package
data_pkg <- data(package = pkgs())
# names of data sets in the package
data_names <- data_pkg$results[, "Item"]
updateSelectInput(session, 'dat', choices = data_names, selected = data_names[1])
### dataset1 - data names
output$dataset1 <- renderDataTable({
DT::datatable(data.frame(data_names))
})
### dataset3 - function list
funs <- paste0('package:', pkgs())
output$func <- renderPrint({
lsf.str(funs)
})
}
})
### dataset2 - selected dataset
data_name <- reactive({
req(input$dat)
})
output$dataset2 <- renderDataTable({
e <- new.env()
library(package = pkgs(), character.only = TRUE)
out <- data(list=data_name(), package = pkgs(), envir = e)
d2 <- e[[out]]
# some datasets are 3-d, e.g., "ozone" in package "plyr"
if(length(dim(d2)) == 3){
d2 <- d2[, , 1]
}
DT::datatable(d2)
})
##### reset
observeEvent(input$reset, {
updateTextInput(inputId = 'pkg',value = NULL)
updateSelectInput(session, 'dat', choices = NULL , selected = NULL)
output$dataset1 <- renderDataTable({
DT::datatable(data.frame("No package selected" = NULL))
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
I have the dataframe below:
DF2 = data.frame(agency_postcode = factor(rep(c(12345,45678,24124,32525,32325),2)),
car_group=factor(rep(c("Microcar","City car","Supermini","Compact","SUV"),2)),
transmission=factor(rep(c("automatic","manual"),5)))
which I use and display as rhandsontable in order to create a second table. First you are supposed to select one or more options from filter by input and then a level from the selected filter(s). Then you press search. What I basically want to do is subset the second table based on the first row of every selected column of the first table. The issue is in line 30 of server.r in which I should give the input$sel
#ui.r
library(shiny)
library(rhandsontable)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(width=2,
selectInput("sel","Filter by:",
choices = c("agency_postcode","date_start","days","car_group","transmission","driver_age"),
multiple=T,selected = "agency_postcode"),
actionButton("sr","Search")
),
mainPanel(
fluidRow(
column(4,offset = 0, style='padding:0px;',rHandsontableOutput("hot")),
column(8,offset = 0, style='padding:0px;',rHandsontableOutput("hot2"))
)
)
)
)
#server.r
#server.r
library(shiny)
library(rhandsontable)
library(jsonlite)
server <- function(input, output) {
#Create rhandsontable as a reactive expression
DFR2<-reactive({
rhandsontable(DF2[1,1:2], rowHeaders = NULL,height = 200)%>%
hot_col(colnames(DF2)[1:2])
})
#Display the rhandsontable
output$hot <- renderRHandsontable({
DFR2()
})
#Convert the rhandsontable to a daraframe
DFR3<-reactive({
req(input$hot)
hot_to_r(input$hot)
})
#Subset the initial dataframe by value of the 1st row-1st column cell of DF3
DFR4 <- reactive({
req(DFR3())
D<-DF2[ which(DF2[,1] %in% DFR3()[1, 1]), ] #input$sel is supposed to be used here instead of 1
for(i in 1:ncol(D)){
D[,i] <- factor(D[,i])
}
D
})
#Display the new rhandsontable
output$hot2 <- renderRHandsontable({
input$sr
isolate(rhandsontable(DFR4()[1,], rowHeaders = NULL,height = 200)%>%
hot_col(colnames(DFR4())) )
})
}
OK. Here is an app that uses a small table to filter a larger one using inner_join. I am not sure this will match the design you had in mind. It is still unclear to me where the filter levels are coming from, or what the hands on tables are for. But you should be able to adapt this approach to your design. Note also that I am not using hands on tables. A direct replacement of the calls to renderTable with renderRHandsontable should work too.
library(shiny)
library(dplyr)
library(purrr)
sub_cars <- mtcars[, c("cyl", "gear", "am")]
ui <- fluidPage(
column(width=3,
selectInput(
inputId = "sel_col",
label = "Select variables",
multiple = TRUE,
choices = c("cyl", "gear", "am"),
selectize = TRUE),
uiOutput("cyl"),
uiOutput("gear"),
uiOutput("am")
),
column(width = 3,
tableOutput("filter_table")),
column(width = 6,
tableOutput("large_table"))
)
server <- function(input, output) {
output$cyl <- renderUI({
if ("cyl" %in% input$sel_col) {
selectInput(
inputId = "sel_cyl",
label = "Select cylinders",
choices = unique(sub_cars$cyl),
multiple = TRUE,
selectize = TRUE
)
}
})
output$gear <- renderUI({
if ("gear" %in% input$sel_col) {
selectInput(
inputId = "sel_gear",
label = "Select gears",
choices = unique(sub_cars$gear),
multiple = TRUE,
selectize = TRUE
)
}
})
output$am <- renderUI({
if ("am" %in% input$sel_col) {
selectInput(
inputId = "sel_am",
label = "Select am",
choices = unique(sub_cars$am),
multiple = TRUE,
selectize = TRUE
)
}
})
# make a small filter table
filter_df <- reactive({
validate(
need(!is_null(input$sel_col),
message = "Please select a column"))
cols <- input$sel_col
cols_vals <- map(cols, function(x) input[[paste0("sel_", x, collapse="")]])
df <- map2_dfr(cols, cols_vals, function(x, y)
filter(sub_cars,!!as.name(x) %in% y)) %>%
select(one_of(cols)) %>%
distinct()
return(df)
})
output$filter_table <- renderTable({
validate(
need(nrow(filter_df()) > 0,
message = "Please select filter values"))
filter_df()
})
# inner join the larger table
large_df <- reactive({
validate(
need(nrow(filter_df()) > 0,
message = "Please select filter values"))
cols <- input$sel_col
inner_join(x=filter_df(), y=mtcars, by = cols)
})
output$large_table <- renderTable({large_df()})
}
shinyApp(ui, server)
Here is a gif of what it does.
I have an R shiny app with a DT datatable that is rendered using the datatable function in order to set various options. I would like to use dataTableProxy and replaceData to update the data in the table, but all the examples I can find assume the DT is rendered directly from the data object, not using the datatable function. The reprex below shows what I would like to do, but replaceData doesn't work in this pattern. How do I do this? Thanks.
# based on
# https://community.rstudio.com/t/reorder-data-table-with-seleceted-rows-first/4254
library(shiny)
library(DT)
ui = fluidPage(
actionButton("button1", "Randomize"),
fluidRow(
column(6,
h4("Works"),
DT::dataTableOutput('table1', width="90%")),
column(6,
h4("Doesn't Work"),
DT::dataTableOutput('table2', width="90%"))
)
)
server = function(input, output, session) {
my <- reactiveValues(data = iris)
output$table1 <- DT::renderDataTable(isolate(my$data))
output$table2 <- DT::renderDataTable({
DT::datatable(isolate(my$data),
options = list(lengthChange=FALSE, ordering=FALSE, searching=FALSE,
columnDefs=list(list(className='dt-center', targets="_all")),
stateSave=TRUE, info=FALSE),
class = "nowrap cell-border hover stripe",
rownames = FALSE,
editable = FALSE
) %>%
DT::formatStyle('Sepal.Width', `text-align`="center")
})
observeEvent(input$button1, {
# calculate new row order
row_order <- sample(1:nrow(my$data))
my$data <- my$data[row_order, ]
proxy1 <- DT::dataTableProxy('table1')
DT::replaceData(proxy1, my$data)
proxy2 <- DT::dataTableProxy('table2')
DT::replaceData(proxy2, my$data)
})
}
shinyApp(ui, server)
Update: Very strangely, removing rownames = FALSE made it all possible. I'm not exactly sure why, but probably rownames might be essential for replacing Data.
# based on
# https://community.rstudio.com/t/reorder-data-table-with-seleceted-rows-first/4254
library(shiny)
library(DT)
ui = fluidPage(
actionButton("button1", "Randomize"),
fluidRow(
column(6,
h4("Works"),
DT::dataTableOutput('table1', width="90%")),
column(6,
h4("Doesn't Work"),
DT::dataTableOutput('table2', width="90%"))
)
)
server = function(input, output, session) {
my <- reactiveValues(data = iris)
output$table1 <- DT::renderDataTable(isolate(my$data))
output$table2 <- DT::renderDataTable({
DT::datatable(isolate(my$data),
options = list(lengthChange=FALSE, ordering=FALSE, searching=FALSE,
columnDefs=list(list(className='dt-center', targets="_all")),
stateSave=TRUE, info=FALSE),
class = "nowrap cell-border hover stripe",
# rownames = FALSE,
editable = FALSE
) %>%
DT::formatStyle('Sepal.Width', `text-align`="center")
})
observeEvent(input$button1, {
# calculate new row order
row_order <- sample(1:nrow(my$data))
my$data <- my$data[row_order, ]
proxy1 <- DT::dataTableProxy('table1')
DT::replaceData(proxy1, my$data)
proxy2 <- DT::dataTableProxy('table2')
DT::replaceData(proxy2, my$data)
})
}
shinyApp(ui, server)
I'm pretty new to shiny (being playing around for about a week). And I'm trying to create an app that takes and input tab-separated text file and perform several exploratory functions. In this case I'm presenting a very simplified version of that app just to highlight what I want to do in a specific case:
Problem:
If you try the app with the sample data (or any data in the same format) you can notice that the app effectively performs the default summary table (if selectInput="summarize", then output$sumfile), but when you try to select "explore", the previous table gets removed from the mainPanel, and outputs the full file (selectInput="explore",then output$gridfile) in the place where it would be as if selectInput="summarize" was still selected.
If you re-select "summarize", excelOutput("sumfile") gets duplicated on the mainPanel.
My goal is simple:
excelOutput("sumfile") when selectInput="summarize" ONLY and
excelOutput("gridfile") when selectInput="explore" ONLY
without placement issues or duplications on the mainPanel
So far I've tried:
inFile=input$df
if(is.null(inFile))
return(NULL)
if(input$show=="summarize")
return(NULL)
or
inFile=input$df
if(is.null(inFile))
return(NULL)
if(input$show=="explore")
return(NULL)
To control what shows up on the mainPanel, but with placement and duplication issues.
sample data:
#Build test data
testdat<-data.frame(W=c(rep("A",3),
rep("B",3),
rep("C",3)),
X=c(letters[1:9]),
Y=c(11:19),
Z=c(letters[1:7],"",NA),
stringsAsFactors = FALSE)
#Export test data
write.table(testdat,
"your/path/file.txt",
row.names = FALSE,
sep = "\t",
quote = FALSE,
na="")
shiny app (app.R):
library(shiny)
library(excelR)
#function to summarize tables
Pivot<-function(df){
cclass<-as.character(sapply(df,
class))
df.1<-apply(df,
2,
function(x) unlist(list(nrows = as.numeric(NROW(x)),
nrows.unique = length(unique(x))-(sum(is.na(x))+length(which(x==""))),
nrows.empty = (sum(is.na(x))+length(which(x==""))))))
df.2<-data.frame(df.1,
stringsAsFactors = FALSE)
df.3<-data.frame(t(df.2),
stringsAsFactors = FALSE)
df.3$col.class<-cclass
df.3$col.name<-row.names(df.3)
row.names(df.3)<-NULL
df.3<-df.3[c(5,4,1,2,3)]
return(df.3)
}
ui <- fluidPage(
ui <- fluidPage(titlePanel(title=h1("Summary generator",
align="center")),
sidebarLayout(
sidebarPanel(
h3("Loading panel",
align="center"),
fileInput("df",
"Choose file (format: file.txt)",
accept = c("plain/text",
".txt")),
selectInput("show",
"Choose what to do with file",
choices=c("summarize","explore")),
p("**'summarize' will output a summary of the selected table"),
p("**'explore' will output the full selected editable table"),
tags$hr()
),
mainPanel(
excelOutput("gridfile"),
excelOutput("sumfile")
))))
server <- function(input, output) {
dat<-reactive({
fp<-input$df$datapath
read.delim(fp,
quote="",
na.strings="\"\"",
stringsAsFactors=FALSE)
})
#get summary
output$sumfile<-renderExcel({
inFile=input$df
if(is.null(inFile)) #if fileInput is empty return nothing
return(NULL)
if(input$show=="explore") #if selectInput = "explore" return nothing
return(NULL)
dat.1<-data.frame(dat())
dat.2<-Pivot(dat.1)
excelTable(dat.2,
defaultColWidth = 100,
search = TRUE)
})
#get full file
output$gridfile<-renderExcel({
inFile=input$df
if(is.null(inFile)) #if fileInput is empty return nothing
return(NULL)
if(input$show=="summarize") #if selectInput = "summarize" return nothing
return(NULL)
dat.1<-data.frame(dat())
excelTable(dat.1,
defaultColWidth = 100,
search = TRUE)
})
}
shinyApp(ui = ui, server = server)
One way to do what you want is to use observeEvent for your inputs input$show and input$df and renderExcel based on your selection of `input$show. Here is an updated version for your code:
library(shiny)
library(excelR)
#function to summarize tables
Pivot<-function(df){
cclass<-as.character(sapply(df,
class))
df.1<-apply(df,
2,
function(x) unlist(list(nrows = as.numeric(NROW(x)),
nrows.unique = length(unique(x))-(sum(is.na(x))+length(which(x==""))),
nrows.empty = (sum(is.na(x))+length(which(x==""))))))
df.2<-data.frame(df.1,
stringsAsFactors = FALSE)
df.3<-data.frame(t(df.2),
stringsAsFactors = FALSE)
df.3$col.class<-cclass
df.3$col.name<-row.names(df.3)
row.names(df.3)<-NULL
df.3<-df.3[c(5,4,1,2,3)]
return(df.3)
}
ui <- fluidPage(
ui <- fluidPage(titlePanel(title=h1("Summary generator",
align="center")),
sidebarLayout(
sidebarPanel(
h3("Loading panel",
align="center"),
fileInput("df",
"Choose file (format: file.txt)",
accept = c("plain/text",
".txt")),
selectInput("show",
"Choose what to do with file",
choices=c("summarize","explore")),
p("**'summarize' will output a summary of the selected table"),
p("**'explore' will output the full selected editable table"),
tags$hr()
),
mainPanel(
excelOutput("gridfile"),
excelOutput("sumfile")
))))
server <- function(input, output) {
dat<-reactive({
fp<-input$df$datapath
read.delim(fp,
quote="",
na.strings="\"\"",
stringsAsFactors=FALSE)
})
observeEvent({
input$show
input$df
}, {
inFile=input$df
if(is.null(inFile)) #if fileInput is empty return nothing
return(NULL)
if(input$show=="explore") {
output$gridfile<-renderExcel({
dat.1<-data.frame(dat())
excelTable(dat.1,
defaultColWidth = 100,
search = TRUE)
})
}
if(input$show=="summarize") {
output$sumfile<-renderExcel({
dat.1<-data.frame(dat())
dat.2<-Pivot(dat.1)
excelTable(dat.2,
defaultColWidth = 100,
search = TRUE)
})
}
})
}
shinyApp(ui = ui, server = server)
Hope it helps!
I have a question regarding R shiny and the observ function. Is it possible to save the selected factors and the state of the work? For Example I created a programm which can choose colnames from the input data. After using bookmark and reopening the programm with the link in the browser the input data are loaded but the select factors of the colnames are reset. But I want to save the chosen colnames. Has anyone an idea? Thank you for your help!
ui <- function(request) {
fluidPage(
sidebarLayout(
sidebarPanel(
radioButtons(
"fileType_Input",
label = h5(""),
choices = list(".csv" = 1, ".xlsx" = 2),
selected = 1,
inline = TRUE
),
fileInput('file1', '' ),
selectInput("letters", label=NULL, factors, multiple = TRUE),
bookmarkButton()
),
mainPanel(
tableOutput("contents")
)
)
)
}
server <- function(input, output,session) {
myData <- reactive({
inFile <- input$file1
# Get the upload file
if (is.null(inFile)) {
return(NULL) }
if (input$fileType_Input == "1") {
read.csv2(inFile$datapath,
header = TRUE,
stringsAsFactors = FALSE)
} else {
read_excel(inFile$datapath)
}
})
observe({
if(is.null(input$letters)){
data <- myData()
if(is.null(data)){
}else{
factors <- colnames(data)
t$choices <- input$letters # append(u$choices,input$letters2)
updateSelectInput(session, "letters",
choices = factors #[!factors2 %in% u$choices)]
)}
}
})
#Display all input Data
output$contents <- renderTable(digits = NULL,{
df <-myData()
df
})
}
enableBookmarking("server")
shinyApp(ui, server)
You can save all needed inputs in a file, and then reapply them with functions like updateRadioButtons() and others.
Saving it to the file could look like this:
observeEvent(input$someRadioButton, {
states <- list()
states$someRadioButton <- input$someRadioButton
#you can save all the needed inputs like this
...
save(states, file = paste0(getwd(), "/myfile"))
})