Excel File Downloader using For Loops, dplyr and R shiny - r

I am trying to build a file processor, where I can upload the raw file, click the button, and get the sorted report. So in this instance, I m wanting to see domain performance for different lines, so it splits the data set and writes to a .xlsx file, where the data splits for each tab based on the line.
It runs fine on RStudio, but when I publish it, I get the following errors. Either of the 2 :
error in [[ subscript out of bounds no-stack-trace-available
warning-error-in-if-argument-is-of-length-zero-no-stack-trace-available
library(openxlsx)
library(readxl)
library(writexl)
library(dplyr)
library(magrittr)
library(lubridate)
library(shiny)
ui <- fluidPage(
titlePanel("Domain Performance"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Upload file',
accept=c('text/csv',
'text/comma-separated-values,text/plain',
'.csv','.xlsx'))
),
mainPanel(
#actionButton(inputId = "run",label = "Run the Code"),
downloadButton('sortspend',"Spend"),
tableOutput("outdata1")
)
))
server <- function(input, output) {
options(shiny.maxRequestSize=90*1024^2)
output$outdata1 <- renderTable({
req(input$file1)
if(is.null(input$file1)) {
return()
}
else {
input$file1
}
})
spenddom <-reactive({
req(input$file1)
dspec <- input$file1
domain_raw <- read.csv(dspec$datapath,header = TRUE)
linenames = unique(domain_raw['Line'])
lineids = unique(domain_raw$`Line Id`)
for (i in seq_along(lineids)){
ddf <-domain_raw%>% select(`Line Id`,Domain,`Advertiser Spending`,Impressions,Clicks,Conversion,Line)%>%
filter(`Line Id` == lineids[i])%>%
group_by(Line,Domain)%>%
summarise(Spend = sum(`Advertiser Spending`), Imp = sum(Impressions), Clicks = sum(Clicks), Conversions = sum(Conversion))%>%
mutate(CPA = Spend/Conversions)%>%arrange(desc(Spend))
}
})
tabcreator <- reactive({
req(input$file1)
dspec <- input$file1
domain_raw <- read.csv(dspec$datapath,header = TRUE)
linenames = unique(domain_raw['Line'])
lineids = unique(domain_raw$`Line Id`)
for (i in seq_along(lineids)){
tabname <- lineids[i]
}
})
output$sortspend<-downloadHandler(
filename = function(){paste0("domainperfdl",".xlsx")},
content = function(fname){
write.xlsx(spenddom(),fname,sheetName = tabcreator())
},
contentType = "application/xlsx"
)
}
shinyApp(ui=ui, server=server)
I m trying to understand what am I doing wrong here. It's the first time I m using for loops with R.

Related

How can I use Shiny as an upload form for an R Script?

I have an R script that loops through a folder of csv files, transforms them, and then writes several csv files once completed.
I want users to be able to use an input form to select various files before the R script runs. I have never used Shiny before, and I cannot figure out the best way to do this. Here is what I have come up with so far:
UI
library(shiny)
library(shinyjs)
fieldsMandatory <- c("calDataImport")
appCSS <- ".mandatory_star { color: red; }"
labelMandatory <- function(label) {
tagList(
label,
span("*", class = "mandatory_star")
)
}
ui <- fluidPage(
shinyjs::useShinyjs(),
shinyjs::inlineCSS(appCSS),
options(shiny.maxRequestSize = 30 * 1024^2),
titlePanel("Setup"),
sidebarLayout(
sidebarPanel(
fileInput("calDataImport", labelMandatory("Choose CSV File for Calibration Data"),
multiple = FALSE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")),
tags$hr(),
shinyDirButton("ticLoc", "Choose Sample Folder", "Upload"),
tags$hr(),
# Output: Go and Download----
downloadButton('submit', 'Select Save Location and Go', class= "action"),
# CSS style for the download button ----
tags$style(type='text/css', "#downloadFile { width:100%; margin-top: 35px;}")
),
mainPanel()
)
)
Server
server <- function(input, output) {
observe({
# check if all mandatory fields have a value
mandatoryFilled <-
vapply(fieldsMandatory,
function(x) {
!is.null(input[[x]]) && input[[x]] != ""
},
logical(1))
mandatoryFilled <- all(mandatoryFilled)
# enable/disable the submit button
shinyjs::toggleState(id = "submit", condition = mandatoryFilled)
})
# dir
shinyDirChoose(input, 'ticLoc', roots = c(home = '~'), filetypes = c('xlsx', 'csv'))
ticLoc <- reactive(input$ticLoc)
output$ticLoc <- renderPrint(ticLoc())
# path
path <- reactive({
home <- normalizePath("~")
file.path(home, paste(unlist(ticLoc()$path[-1]), collapse = .Platform$file.sep))
})
# files
calDataImport <- reactive({
inFile <- input$calDataImport
if (is.null(inFile)) return(NULL)
calDataImport <- read.csv(inFile$datapath, header = TRUE)
calDataImport
})
# Download handler in Server
output$submit <- downloadHandler(
filename = function() {
paste('RAPTOR_Output', Sys.Date(), '.zip', sep='')
},
content = function(con) {
owd <- setwd(tempdir())
on.exit(setwd(owd))
outputFiles <- NULL;
################ INSERTING STANDARD R SCRIPT HERE ##########
# [necessary libraries here]
# [functions defined here]
if("CAS#" %in% colnames(calDataImport)){names(calDataImport)[names(calDataImport) == 'CAS#']<-"CAS Number"}
if("Component RT" %in% colnames(calDataImport)){names(calDataImport)[names(calDataImport) == 'Component RT']<-"Ret. Time"}
outputFileName <- paste("calDataImport", "csv", sep = ".")
write.csv(calDataImport, outputFileName, row.names = FALSE)
outputFiles <- c(outputFileName,outputFiles)
setwd(ticLoc)
files <- list.files(pattern = "csv")
filesFull <- list.files(pattern = "csv", full.names = TRUE)
for (i in 1:length(files)) {
fileName <- str_split(files[i], "\\.", n=2)[[1]][1]
rawData <- read.csv(paste(fileName, str_split(files[i], "\\.", n=2)[[1]][2], sep="."), check.names=FALSE)
# [more transforming script here]
outputFileName <- paste(fileName, "_Annotated",".csv", sep = "")
write.csv(annotatedData, outputFileName, row.names = FALSE)
outputFiles <- c(outputFileName,outputFiles)
}
################### END ############
#create the zip file
zip(con,outputFiles)
})
}
# Create Shiny app ----
shinyApp(ui, server)
What I have here doesn't seem to work. I get the following error:
Warning: Error in UseMethod: no applicable method for 'filter' applied to an object of class "c('reactiveExpr', 'reactive', 'function')"
[No stack trace available]
So my question is, how can I make this work? My standard R script that I am inserting is nearly 2000 lines, so I would rather not have to go through and adjust the script if possible.
Thank you so much for any insight you can provide!
This is a shiny app letting the user to upload multiple csv files. Then, the app reads each csv file and then calculates some properties, which will be displayed on the app. Please note the option multiple = TRUE in fileInput:
library(tidyverse)
library(shiny)
ui <- fluidPage(
fileInput(
inputId = "files",
label = "Upload tables",
placeholder = "No files selected.",
multiple = TRUE,
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv"
)
),
tableOutput("table")
)
server <- function(input, output, session) {
output$table <- renderTable({
req(input$files %>% nrow() > 0)
input$files %>%
as_tibble() %>%
mutate(data = datapath %>% map(read_csv)) %>%
transmute(
datapath,
nrow = data %>% map_int(nrow),
colnames = data %>% map_chr(~ .x %>%
colnames() %>%
paste0(collapse = ", "))
)
})
}
shinyApp(ui, server)
Here I uploaded 2 csv files with the content of the R objects iris and mpg:
I do not know how your script is structured, but the uploaded files are in the directory dirname(input$files$datapath[[1]]).

How to use R shiny to filter a specific column from a csv file and extract the data in csv and pdf format

As I am new to R shiny, please go easy on me:
I have found this code useful: https://community.rstudio.com/t/download-dataset-filtered-in-shiny-input/75770. This code takes Iris data and filters based on the column 'Species'
In order to get the filtering results after uploading my own data via fileInput() I made some adjustments to the code above. I am trying to filter data using the column 'Type', but I am receiving the below-mentioned error.
Error:
object 'file1' not found
csv data:
ID Type Range
21 A1 100
22 C1 200
23 E1 300
code:
library(tidyverse)
library(shiny)
library(DT)
library(shinyWidgets)
ui <- fluidPage(
#setBackgroundColor(color = c("#66e0ff", "#00a3cc", "#003d4d")),
h1("Data"),
sidebarLayout(
sidebarPanel(fileInput("file1", label = "Choose species"),
downloadButton("download1","Download entire Table as csv")),
mainPanel(h4("Table 1: Iris"),
dataTableOutput("csv_dto")
)
))
server <- function(input, output, session) {
output$csv_dto <- 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)
})
thedata <- reactive({
file$datapath %>%
filter(Type == input$Type)
})
output$type_dto <- renderDataTable({
thedata() %>%
datatable(extensions = 'Buttons',
options = list(
#Each letter is a dif element of a datatable view, this makes buttons the last thing that's shown.
buttons = c("copy", "csv", "pdf")),
filter = list(
position = 'top'),
rownames = FALSE)
})
output$download1 <- downloadHandler(
filename = function() {
paste("type_", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(thedata(), file)
}
)
}
shinyApp(ui, server)
could someone help me fix this issue?
I can't help you with the PDF output but to get you started: You have to do some adjustments of the code in the examples from ?fileInput.
Instead of renderTable use reactive. Also do not assign to an output. Instead of dataTableOutput("csv_dto") use dataTableOutput("type_dto") in the UI.
library(tidyverse)
library(shiny)
library(DT)
library(shinyWidgets)
ui <- fluidPage(
# setBackgroundColor(color = c("#66e0ff", "#00a3cc", "#003d4d")),
h1("Data"),
sidebarLayout(
sidebarPanel(
fileInput("file1", label = "Choose species"),
downloadButton("download1", "Download entire Table as csv")
),
mainPanel(
h4("Table 1: Iris"),
dataTableOutput("type_dto")
)
)
)
server <- function(input, output, session) {
csv_dto <- reactive({
file <- input$file1
ext <- tools::file_ext(file$datapath)
req(file)
validate(need(ext == "csv", "Please upload a csv file"))
read.csv(file$datapath)
})
thedata <- reactive({
csv_dto() %>%
filter(Type == input$Type)
})
output$type_dto <- renderDataTable({
thedata() %>%
datatable(
extensions = "Buttons",
options = list(
# Each letter is a dif element of a datatable view, this makes buttons the last thing that's shown.
buttons = c("copy", "csv", "pdf")
),
filter = list(
position = "top"
),
rownames = FALSE
)
})
output$download1 <- downloadHandler(
filename = function() {
paste("type_", Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(thedata(), file)
}
)
}
shinyApp(ui, server)

Read csv files (more than 1) in r shiny and create new column for Filename

I want to upload multiple *.csv files, rbind them and create new column named Filename with original csv filenames.
With basename() function I am only apply to get temp file name and not the original filename.
input$datafile$name gets the original file name but I am not sure how to mutate new column from this name.
Below is my code for reference.
library(shiny)
library(data.table)
library(dplyr)
options(shiny.maxRequestSize = 10000*1024^2)
ui <- shinyUI(
fluidPage(
titlePanel("Example Read and Merge with new Column for Filename"),
sidebarLayout(
sidebarPanel( fileInput("datafile", h5("Choose CSV file:"),
accept = ".csv",multiple = TRUE)),
mainPanel(DT::dataTableOutput("Raw_data_show"),
verbatimTextOutput("results"),textOutput("filechosen"))
)))
server <- function(session,input, output) {
path <- reactiveValues(pth=NULL)
observeEvent(input$filechoose,{
path$pth <- file.choose()
})
output$filechosen <- renderText({
if(is.null(path$pth)){
return()
}else{
dirname(path$pth)
}
})
rawData <- reactiveValues(site = NULL)
observeEvent(input$datafile, {
req(input$datafile)
rawData$site <- input$datafile$datapath%>%
purrr::map_df(~fread(.x)%>%mutate(FileName_2D = basename(.x)))
})
output$results = renderPrint({
input$datafile$name
print(paste("First File (input$datafile$name)[[1]])",(input$datafile$name)[[1]]))
print(paste("2nd File (input$datafile$name)[[2]])",(input$datafile$name)[[2]]))
})
output$Raw_data_show <- DT::renderDataTable({
rawData$site
})
}
shinyApp(ui, server)
Below is the image of the app so far.
Appreciate some inputs.
You can use input$datafile$datapath to read the file and input$datafile$name to add a new column with the file name. Use map2_df to pass both the values together and combine into one dataset.
library(shiny)
library(tidyverse)
library(data.table)
ui <- shinyUI(
fluidPage(
titlePanel("Example Read and Merge with new Column for Filename"),
sidebarLayout(
sidebarPanel( fileInput("datafile", h5("Choose CSV file:"),
accept = ".csv",multiple = TRUE)),
mainPanel(DT::dataTableOutput("Raw_data_show"),
verbatimTextOutput("results"),textOutput("filechosen"))
)))
server <- function(session,input, output) {
path <- reactiveValues(pth=NULL)
observeEvent(input$filechoose,{
path$pth <- file.choose()
})
output$filechosen <- renderText({
if(is.null(path$pth)){
return()
}else{
dirname(path$pth)
}
})
rawData <- reactiveValues(site = NULL)
observeEvent(input$datafile, {
req(input$datafile)
rawData$site <- map2_df(input$datafile$name, input$datafile$datapath,
~fread(.y)%>% mutate(FileName_2D = .x))
})
output$Raw_data_show <- DT::renderDataTable({
rawData$site
})
}
shinyApp(ui, server)

(In R shiny) About upload multiple csv files and print all tables out

I want to upload two csv files and print both tables out.
Here is the code I wrote:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput(inputId = "files", label = "Choose CSV File", multiple = TRUE,accept = c(".csv")
)
),
mainPanel(
fluidRow(tableOutput("Policy1")),
fluidRow(tableOutput("Policy2")),
)
)
)
server <- function(input, output) {
data <- reactiveValues(file1 = NULL,
file2 = NULL)
output$Policy1 <- renderTable({
if(!is.null(input$files$datapath[1]))
data$file1 <- read.csv(input$files$datapath[1], header = TRUE)
data$file1
})
output$Policy2 <- renderTable({
if(is.null(input$files$datapath[2])) {return(1)}
else{return(NULL)}
})
}
shinyApp(ui, server)
and for the output$Policy2 part, I want to test when the is.null(input$files$datapath[2]) is true. I thought it should be true when I only upload one file or don't upload anything but
if I only upload one csv file, it didn't print out the table 1, which means is.null(input$files$datapath[2]) is false in this case.I don't know why this is the case.
And as a result, if I change the code to ask shiny print two tables for me and only upload one file, there will be an error, here is the code:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput(inputId = "files", label = "Choose CSV File", multiple = TRUE,accept = c(".csv")
)
),
mainPanel(
fluidRow(tableOutput("Policy1")),
fluidRow(tableOutput("Policy2")),
)
)
)
server <- function(input, output) {
data <- reactiveValues(file1 = NULL,
file2 = NULL)
output$Policy1 <- renderTable({
if(!is.null(input$files$datapath[1]))
data$file1 <- read.csv(input$files$datapath[1], header = TRUE)
data$file1
})
output$Policy2 <- renderTable({
if(!is.null(input$files$datapath[2]))
data$file2 <- read.csv(input$files$datapath[2], header = TRUE)
data$file2
})
}
shinyApp(ui, server)
where I only change a little part and here is the error :
which I assume is because I should return NULL when only one file inputed in, how can I fix this problem, thanks for any help
The value won't be NULL if it's missing. It's better to check that there are enough values checking the length of the vector or something. For example
output$Policy2 <- renderTable({
if(!is.null(input$files) && length(input$files$datapath)>=2)
data$file2 <- read.csv(input$files$datapath[2], header = TRUE)
data$file2
})

Read a Data Frame into a Separate Function from Shiny

I have a Shiny application which lets a user upload a CSV to undertake sentiment analysis.
The Aim:
I want to use Shiny to upload the CSV and then use a separate function (CapSent) to do the analysis and output the results.
Basically I am trying to pass the 'df' uploaded by the user into the function 'CapSent' (which resides in global.R) from Shiny. CapSent undertakes Sentiment analysis using a custom dictionary of words.
My Code So Far:
So far I have:
ui:
library(shiny)
source('global.R')
ui <- fluidPage(
sidebarPanel(
# Input: Select a file ----
fileInput("file1", "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv"))
))
Server:
server <- function(input, output) {
output$contents <- renderTable({
req(input$file1)
df <- read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote)
CapSent(0.1, df) # 0.1 represents a threashold, df is the data
})
}
shinyApp(ui, server)
Functions.R:
CapSent <- function(0.1, df){
newdf<-data.frame(df,stringsAsFactors = FALSE)
#....Do some sentiment analysis here on newdf
#....Then export the sentiment analysis results
write.csv(newdf,"myResults.csv")
}
The Issue
With the above code I receive the error 'Error in Encoding<-: a character vector argument expected'.
'CapSent' works when I manually add 'df' to the Global Environment (using readr) but I want users to upload their own data to analyse. Hence the question:
Is there a way to pass df to the Global Environment from Shiny?
Any advice would be much appreciated.
Try this:
ui.R
library(shiny)
# Define UI for app that draws a histogram ----
ui <- fluidPage(
# App title ----
titlePanel("Hello Shiny!"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: ----
fileInput("file1", "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")),
actionButton("button", "Apply function/download df"),
hr(),
uiOutput("downloadButton")
),
# Main panel for displaying outputs ----
mainPanel(
h2("ORIGINAL DATA FRAME"),
DT::dataTableOutput("contents"),
br(),
uiOutput("modify")
)
)
)
server.R
server <- function(input, output) {
temp_df <- reactiveValues(df_data = NULL)
temp_df2 <- reactiveValues(df_data = NULL)
output$contents <- DT::renderDataTable({
req(input$file1)
temp_df$df_data <- read.csv(input$file1$datapath, sep = ";")
temp_df$df_data
}, options = (list(pageLength = 5, scrollX = TRUE)))
output$contents2 <- DT::renderDataTable({
temp_df2$df_data
}, options = (list(pageLength = 5, scrollX = TRUE)))
observeEvent(input$button,{
if(!is.null(temp_df$df_data)){
temp_df2$df_data <- CapSent(temp = 0.7, temp_df$df_data)
output$modify <- renderUI({
tagList(
h2("MODIFY DATA FRAME"),
DT::dataTableOutput("contents2")
)
})
output$downloadButton <- renderUI({
downloadButton("downloadData", "Download")
})
}else{
showNotification("No data was upload")
}
})
output$downloadData <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(temp_df2$df_data, file)
})
}
as I do not know which CapSent end-use I made CapSent a function that adds a new column in the original data frame;
global.R
CapSent <- function(temp = 0.1, df){
newdf <- df
newdf$New_Col <- temp
return(newdf)
#....Do some sentiment analysis here on newdf
#....Then export the sentiment analysis results
#write.csv(newdf,"myResults.csv")
}
If you want to create a global function/variable just make a global.R which will let you use the function/variable everywhere on the ui.R or server.R.
This is the link to learn more: https://shiny.rstudio.com/articles/scoping.html
Edit: If you want to show the CSV, first of all you need to make a tabpanel and then make a table using the csv data like:
Use the package DT, install.packages("DT), is a package to make dynamic tables.
`output$yourtabpanelid = DT::renderDataTable({
req(input$file1)
df <- read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote)
return(df)
})`
Then don't make a functions.R, just put the function below the one and put the read.csv(.....) before the function to use it in all functions at server.R like:
`df <- read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote
)
server <- function(input, output, session) {`
And you can quit the df <- read.csv.... of the function DT but keep the return(df)

Resources