Write checkBoxGroup to file and read from it - r

I created a Shiny App which includes a checkBoxGroup. When called, this element returns a vector with the selected choices. I can write this to file using write.table()and it creates a CSV-file in which a line looks like this:
Jota 5 5 nature3 5 5 FALSE c("choice1", "choice2", "choice4") property c("choiceA", "choiceB", "ChoiceD", "choiceE", "choiceK") 5 5
But reading this file back using read.table() seems to be tricky as it returns in
Error in scan(file = file, what = what, sep = sep, quote = quote, dec = dec, :
Line 9 doesn't have 12 arguments
I assume that read.table() has difficulties to parse the vectors in the CSV file. Is there any workaround besides flattening the structure and turning every choice into a separate, TRUE/FALSE value with a unique column?
EDIT: Example App
As suggested, I put together a dummy app which replicated the problem. It is built on this article by Shiny. Unlike described above it uses write.csv() and creates a new file for every form submit but the problem is the same.
library(shiny)
fields <- c("name", "groupInput")
shinyApp(
ui = fluidPage(
textInput("name", "Name", ""),
checkboxGroupInput("groupInput", "Select from list", choices = c('abc', 'def', 'ghi')),
actionButton("submit", "Submit"),
DT::dataTableOutput("responses", width = 300)
),
server = function(input, output, session) {
formData <- reactive({
data <- sapply(fields, function(x) input[[x]])
data
})
observeEvent(input$submit, {
saveData(formData())
})
output$responses <- DT::renderDataTable({
input$submit
loadData()
})
outputDir <- "responses"
saveData <- function(data) {
data <- t(data)
# Create a unique file name
fileName <- sprintf("%s_%s.csv", as.integer(Sys.time()), digest::digest(data))
# Write the file to the local system
write.csv(
x = data,
file = file.path(outputDir, fileName),
row.names = FALSE, quote = TRUE
)
}
loadData <- function() {
# Read all the files into a list
files <- list.files(outputDir, full.names = TRUE)
data <- lapply(files, read.csv, stringsAsFactors = FALSE)
# Concatenate all data together into one data.frame
data <- do.call(rbind, data)
data
}
}
)
When you run it and check more than one box in the checkBoxGroup the CSV that will be stored looks like this:
"name","groupInput"
somename,c("abc", "def", "ghi")
The existence of the vector in the CSV seems to cause an error in read.csv, namely:
Warning: Error in read.table: more columns than column names
Stack trace (innermost first):
87: read.table
86: FUN
85: lapply
84: loadData [/Users/alexanderjulmer/Code/test-storage/app.R#45]
83: exprFunc [/Users/alexanderjulmer/Code/test-storage/app.R#25]
82: widgetFunc
81: func
80: origRenderFunc
79: renderFunc
78: origRenderFunc
77: output$responses
1: runApp
I think this is because the vector is not properly parsed within the data.frame which I doubt is possible. So I think it would be best to split the data from checkBoxGroup into several columns. But then, how to do that?

It sounds like what you're trying to avoid is having a separate column for each possible checkBoxGroup input - the alternative to this that I see is to treat any combination of them as an element of length 1 so that it can be read by read.csv as such. In your example app, if more than one is checked, the checks are a list with length 3, and read.csv understandably doesn't really know how to handle that.
To address this problem, my approach is to first unlist the elements of checkBoxGroup and then collapse the elements into a character vector, and stick that to the "Name" text input. All of this is done in your assignment to formData:
formData <- reactive({
data <- sapply(fields, function(x) input[[x]])
chks <- unlist(data[[2]])
data <- c(data[[1]], paste0(chks, collapse=", "))
data
})
This gives you a table of two columns, where the second is a comma-separated set of checkbox inputs. (Here, I've broken that process into two steps for clarity, but there's no reason you couldn't put them into one line of code if you want to.)
However, I'm still not clear how you intend to use your data, and therefore whether a character string will be useful to you and solve your ultimate problem...

Related

Environment for reading files within R Shiny app

I'm creating a simple GUI in Shiny for reading in a bunch of csv files and then filtering them by values present in the 5th column of each csv. I'm not sure how to access the correct shiny environment however. For example, within the server function, I first read the files in with the lines:
for (i in all_paths) {
n <- basename(i)
temp = list.files(path = i, pattern="*.csv",full.names = TRUE)
list2env(
lapply(setNames(temp, make.names(gsub(".*FRSTseg*", n, temp))),
read.csv), envir = .GlobalEnv)
}
And then filter with:
Pattern1<-grep("*.csv",names(.GlobalEnv),value=TRUE)
all_data<-do.call("list",mget(Pattern1))
newdfs <- lapply(all_data, function(x) subset(x, x[, 5] > 0))
list2env(newdfs,globalenv())
When I run the app, I get en error message saying it can't find the value of one of my csvs, which I have found to be the first element of the Pattern1 list. So I'm pretty sure the app fails right after the Pattern1 line.
I think the problem is that the csv files are not being read into the correct environment, such that the all_data <- do.call... line does not know where to look. So instead of using .GlobalEnv and globalenv, what should I be using? Any help is appreciated, thanks!
We can use reactiveValues and store the result of read_csv to be available across all observers in the app. I created a small app that reads the 5th column of different .csv files located in the project directory. In this case all the data will be stored inside an object called column_read$files that can be invoked inside any observer or reactive.
app:
library(tidyverse)
library(shiny)
set.seed(15)
#create the data
paste0('iris', 1:5, '.csv') %>%
map(~write_csv(x = slice_sample(iris,n = 10), .x))
ui <- fluidPage(
actionButton('read_files', "Read Files"),
textOutput('columns_print')
)
server <- function(input, output, session) {
columns_read <- reactiveValues(files = NULL)
observeEvent(input$read_files, {
files <- list.files(pattern = "*.csv",full.names = TRUE)
columns_read$files <- map(files, ~read_csv(.x, col_select = 5))
})
output$columns_print <- renderPrint({
req(columns_read$files)
columns_read$files
})
}
shinyApp(ui, server)

How to use test.R to unmerge the column using R shiny

After I get the column name from the user, I want to unmerge the column 'alpha.'
I have the dataframe, and I have the same dataframe on my system as a csv file. (I'm highlighted the dataframe here for clarity) -> All of the following is occurring in the r script named test.R
What I'm looking for is a way to use test.R When I click the "Unmerge" button in R shiny, I should get the results (with unmerging columns) and the final dataset should render in the main panel.
Since I'm new to R Shiny, I'm not sure how to go about doing it.
Could someone please help me?
Note: The browse button should take the same data frame as csv and provide the unmerged results in the main panel.
test.R
library(dplyr)
library(tidyr)
library(stringr)
library(tidyverse)
library(stringr)
library(svDialogs)
column_name <- dlg_input("Enter a number", Sys.info()["user"])$res
before_merge<- data.frame(ID=21:23, alpha=c('a b', 'c d', 'e z'))
before_merge
library(reshape2)
newColNames <- c("type1", "type2")
#column_name <- readline(prompt="Enter the desired column name: ")
newCols <- colsplit(before[[column_name]], " ", newColNames)
after_merge <- cbind(before, newCols)
after[[column_name]] <- NULL
after_merge
Shiny App
## Only run examples in interactive R sessions
library(shiny)
if (interactive()) {
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File", accept = ".csv"),
checkboxInput("header", "Header", TRUE),
actionButton("dataset2", "Extract", class = "btn-primary"),
),
mainPanel(
tableOutput("contents")
)
)
)
server <- function(input, output) {
output$contents <- renderTable({
file <- input$file1
ext <- tools::file_ext(file$datapath)
req(file)
validate(need(ext == "csv", "Please upload a csv file"))
read.csv(file$datapath, header = input$header)
})
observeEvent(input$dataset2, {
source("test.R", local = TRUE)
})
}
shinyApp(ui, server)
}
So what you want to do is twofold:
First, remove the dialog input from test.R and put in a corresponding input in Shiny itself. This is to identify the column, right? You can make a selectInput with the options being the column names that the dataframe has.
Next, put all the relevant code into a function. This function should do the following: take in an input dataframe (you are creating this before_merge in test.R, instead, use the dataframe that you are getting from the upload), do whatever you need to do (the colsplit, etc.), and then return the final result.
Once you have that, then it's just a matter of putting a line at the top of your Shiny file where you source your test.R, and then you can call the function directly.
Alternatively, you don't even need a separate test.R file - just put your function above the output$contents section and use it directly. It's useful to have a separate helper file if you have a lot of functions (or the functions are used elsewhere), but in this case you don't need it.

object$a:object of type 'closure' is not subsettable

I'm getting this error when I run below code, can anyone please tell how to overcome this error.
Below is the code in which mydata is the main data set and I have created a shiny dashboard using the below code.
I tried to make one of the column as URL , but its showing error as in title.
And I tried giving data()$IFX_USERNAME as in his is a very common error in shiny apps. This most typically appears when you create an object such as a list, data.frame or vector using the reactive() function – that is, your object reacts to some kind of input. If you do this, when you refer to your object afterwards, you must include parentheses.
For example, let’s say you make a reactive data.frame like so:
MyDF<-reactive({ code that makes a data.frame with a column called “X” })
If you then wish to refer to the data.frame and you call it MyDF or MyDF$X you will get the error. Instead it should be MyDF() or MyDF()$X You need to use this naming convention with any object you create using reactive(), even then its showing the same error
library("shiny")
library("datasets")
library("DT")
library("shinyBS")
library(tidyr)
lapply( dbListConnections( dbDriver( drv = "MySQL")), dbDisconnect)
#connecting to database
dbListTables(mydb)
dbListFields(mydb, 'DL_COMMUNITY')
rs = dbSendQuery(mydb, "select * from DL_COMMUNITY")
mydatabase=fetch(rs)
setDT(mydatabase)
colnames(mydatabase)
header <- dashboardHeader()
ui = shinyUI(fluidPage(
DT::dataTableOutput("mtcarsTable"),
bsModal("mtCarsModal", "My Modal", "",dataTableOutput('mytext'), size = "large")
))
on_click_js = "
Shiny.onInputChange('mydata', '%s');
$('#mtCarsModal').modal('show')
"
on_click_js1 = "
Shiny.onInputChange('mydata', '%s');
$('#mtcarsTable').modal('show')
"
convert_to_link = function(x) {
as.character(tags$a(href = "#", onclick = sprintf(on_click_js,x), x))
}
convert_to_link1 = function(x) {
as.character(tags$a(href = "#", onclick = sprintf(on_click_js1,x), x))
}
shinyApp(
ui = ui,
server = function(input, output, session) {
mtcarsLinked <- reactive({
mydatabase$IFX_USERNAME <- sapply(
mydatabase$IFX_USERNAME,convert_to_link)
return(mydatabase)
})
**linked <- reactive({
myd$TEAM_MEMBERS <- sapply(
myd$TEAM_MEMBERS,convert_to_link1)
return(myd)
})**
output$mtcarsTable <- DT::renderDataTable({
DT::datatable(mtcarsLinked(),
class = 'compact',
escape = FALSE, selection='none'
)
})
output$mytext = DT::renderDataTable({
#userQuery=paste("select PROJECT,COMMENT from DL_COMMUNITY where IFX_USERNAME = '",user,"'",sep="")
#rs = dbSendQuery(mysqlCon,userQuery)
userQuery=paste("SELECT *
from Heatmap.DL_PROJECT where CONCAT(',', TEAM_MEMBERS, ',') like '%,sa,%'
or PROJECT_OWNER like '%,sa,%'
or PROJECT_LEAD like '%,sa,%'")
rs = dbSendQuery(mydb,userQuery)
myd=fetch(rs,n=-1)
myd<-data.frame(myd)
myd$TEAM_MEMBERS<- as.list(strsplit(myd$TEAM_MEMBERS, ","))
#myd<-myd %>%
#mutate(TEAM_MEMBERS = strsplit(as.character(TEAM_MEMBERS), ",")) %>%
#unnest(TEAM_MEMBERS)
#setDT(myd)
#hello <- input$mydata
#myd<-mydatabase[mydatabase$IFX_USERNAME==input$mydata,]
#myd1<-t(myd)
DT::datatable(linked(),
class='compact',
escape = FALSE,selection = 'none')
})
}
)
First, always use my_reactive() when you call a reactive function e.g. my_reactive.
Second, the object of type closure not subsettable usually means that the object you want to subset (here with $) cannot be found. You are not having the object not found error because you gave it a name already known to R.
As in the example of jogo, the same error occurs when trying to subset mean. mean is an object in R so it exists and R will not return object not found but it is a function and you cannot subset from it hence the error object is not subsettable.
Compare the results of the following lines of code.
mean[1]
mean <- c(1, 3)
mean[1]
Also note that R can still use mean to perform the mean of a numeric vector as it knows when to look for a function or for something else. But it is strongly advised not to do that. You should always properly name your objects with meaningful names.

reactiveValues cannot be used to render output

I'm developing a shiny app using reactive value, of course. However, I'd like to explore the use of reactiveValues to test my understanding of the concept. My design is to create a dt container of reactive values, e.g. data, cols, rows; so that I can save shiny input$file uploaded data to dt$data; also I'd use checkboxGroupInput to display the columns of the data, which is saved as dt$cols, and let users to select columns and then render data table of dt$data[dt$cols]. Here's the code I used:
dt <- reactiveValues()
observeEvent(input$uploadbutton, {
file <- input$file
req(input$file)
f <- read.csv(file$datapath, header = TRUE)
dt$data <- f
# get the col names of the dataset and assign them to a list
cols <- mapply(list, names(dt$data))
# update columns ui under columnscontrol div
updateCheckboxGroupInput(session, "columns", "Select Columns:", choices = cols, selected = cols)
})
observeEvent(input$columns, { dt$cols <- input$columns })
output$datatbl <- DT::renderDataTable(
dt$data[dt$cols], rownames = FALSE,
# column filter on the top
filter = 'top', server = TRUE,
# autoWidth
options = list(autoWidth = TRUE)
)
The code didn't work, I was thrown with the error of "undefined columns" when dt$data[dt$cols] is called. However, the above works fine if I only use reactive value dt2 <- eventReactive(input$columns, { f <- dt$data[input$columns], f }) and then call dt2() in renderDataTable(). I wonder what's wrong with the use of the variables in reactiveValues.
When you upload the file, the instruction dt$data <- f will then trigger the renderDataTable which uses dt$data. This happens before dt$cols <- input$columns is called therefore dt$colsis NULL and dt$data[dt$cols] throws an error.
You can try with isolate :
isolate(dt$data)[dt$cols]

Conditional selection DataTables Shiny not working

When a row in a DataTable is clicked, I would like an image in a different panel to be loaded but, I keep getting an error and not.
**Warning in widgetFunc() :
renderDataTable ignores ... arguments when expr yields a datatable object; see ?renderDataTable
Error in basename(file) : a character vector argument expected**
output$image1 <- renderImage({
s = input$table1_rows_selected
if (length(s)) list(src=paste0(imagePath,"/peak",s,".png"))},deleteFile=FALSE)
The function below works however,
output$image1 <- renderImage({list(src=paste0(imagePath,"/peak1.png"))},deleteFile=FALSE)
Here is a full version of the code:
server.R
writeLines("Please select ANY image")
imagePath = file.choose()
# break up the character vector, delete the last word
imagePath = dirname(imagePath)
server = function(input, output) {
output$table1 = renderDataTable({
# the peak table
datatable(peaksTable,
# when rowname is false each row does not have a numeric # associated with it
rownames = FALSE,
# specify the name of the column headers
colnames = c("Seqnames", "Start", "End","Width","Strand","P","Q","Effectsize",
"FDR","Keep","Gene_name","Gene.nearest","Count","Count.pred",
"Coverage","Local.mut.density","Base.context.GC","Tn.Context.TpC",
"Tn.context.CpG","Dnase","Activechrom","Hetchrom","Rept"))
},
escape = FALSE)
# render an Image based on which rows are clicked on.
output$image1 <- renderImage({
s = input$table1_rows_selected
if (length(s)) list(src=paste0(imagePath,"/peak",s,".png"))},deleteFile=FALSE)
ui.R
shinyUI(navbarPage(
title = " Nanoproject",
# first panel , create table of the peaksTable dataframe
tabPanel('Peak Table' ,
dataTableOutput('table1')),
# second panel
tabPanel('Peak Images' ,
imageOutput("image1",width = "auto",height = "auto")
))
I'm not sure where I'm going wrong.
Like it's been pointed out, without a reproducible example it's hard to help.
My guess is that your code is not dealing with the case where no rows are selected. If that's true, something like this should fix the problem:
server.R
output$image1 <- renderImage({
s <- input$table1_rows_selected
# print(s)
if(is.null(s)) return(NULL)
list(src = paste0(imagePath,"/peak",s,".png"))
}, deleteFile=FALSE)
Printing out s could help you understand better whats going on.

Resources