Compare CSV files from two dropdowns Rshiny - r

How can I create a shiny app with two dropdown menus with csv files from ./data folder then read those Csv and compare differences?
A user selects CSV from two dropdown menus then automatically generates differences
UI.R
library("shiny")
ui <- fluidPage(
fluidPage(
titlePanel("Automated Data Dictionary Comparison"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = 'Dic1',
label = 'Choose First Data Dictionary:',
choices = list.files(path = "./data",
full.names = FALSE,
recursive = FALSE)),
selectInput(inputId = 'Dic2',
label = 'Choose Second Data Dictionary:',
choices = list.files(path = "./data",
full.names = FALSE,
recursive = FALSE))
),
mainPanel(
tableOutput('contents')
)
)
)
)
SERVER.R
Library(shiny)
library(dplyr)
server <- function(input, output) {
dataset <- reactive({
infile <- input$Dic1
if (is.null(infile)){
return(NULL)
}
read.csv(infile[[1]])
})
output$contents <- renderDataTable({
#x <- dataset()
Diff <- render_diff(diff_data(data_ref=input$DIC1, data = input$DIC2),
Diff
})
}

From what I can see here, what you are doing is that you are correctly creating your reactive dataset object dataset (for 1 of your input files though not both), but you are not using this later on, when you want to generate the differences table (which again needs to be a reactive component as it will be generated from 2 reactive ones - dataset1 and dataset2).
Something like this should do the trick though (wrap it inside the server function):
# Parse first file
dataset1 <- reactive({
infile <- input$Dic1
if (is.null(infile)){
return(NULL)
}
x <- read.csv(infile[[1]])
x
})
# Parse second file
dataset2 <- reactive({
infile <- input$Dic2
if (is.null(infile)){
return(NULL)
}
x <- read.csv(infile[[1]])
x
})
# Create comparison table (reactive as both of its elements are reactive)
diff <- reactive({
x <- render_diff(diff_data(data_ref=dataset1(), data=dataset2()))
x
})
#Output
output$contents <- renderDataTable({
diff()
})
Check the above and let me know how it goes for you.

Related

Excel File Downloader using For Loops, dplyr and R shiny

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.

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)

Radio button selection to show table on next output

In my shiny app, I am uploading different types of files which I do want to process and display the result on different output sections. But the second output is depended on the first output result. In my second output, I am filtering the first table using column names. Now the problem I have is some files I am uploading don't have the columns I am using to subset for second output, they have been moved to the first row which means once I upload a file with different columns I should replace the columns existing with first row them filter results for the second output. Here is my app :
library(shiny)
library(DT)
ui<- shinyUI(fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose CSV File',
accept=c('text/csv',
'text/comma-separated-values,text/plain',
'.csv')),
# radio button to show either row or replaced column table
radioButtons("radio", label = h3("Replace columns"),
choices = list("Raw table" = 1, "change columns" = 2),
selected = 1)
),
mainPanel(
DT::dataTableOutput('contents'),
DT::dataTableOutput('filtered')
)
)
)
)
server <- function(input, output, session){
myData <- reactive({
inFile <- input$file1
if (is.null(inFile)) return(NULL)
data <- read.csv(inFile$datapath, header = TRUE)
data
})
output$contents <- DT::renderDataTable({
DT::datatable(myData())
})
#Replace columns reactive event
replaceColumns <- eventReactive(input$radio,{
#Change row to column and delete first row
colnames(myData()) <-myData()[1,]
df = myData()[-1, ]
df
})
data2<- reactive({
# Select columns of the dataframe
df1 <- select(myData(),mpg,cyl,wt)
df1
})
#Output based on either raw or replaced column table
output$filtered <- DT::renderDataTable({
DT::datatable(data2())
})
}
shinyApp(ui,server)
How can Use radio button or if there is another better way, so that when I upload a file and I see it has right columns, it automatically displays in first output, and when I click raw table In radio button it proceeds to filter and give filtered output and If uploaded file does not have right columns I click on change columns so that to replace columns with the first row of the table then filter and show filtered output?
The way I want my app to behave is, when I upload a file, it displays and if the file has right columns, then I click raw table on radio button which now proceeds to filter and display filtered output in second output but if I can see the file uploaded has wrong columns then I click on change columns in radio button which will replace columns with the first row and then proceed to filtered results in second output. What I simply mean is. I want the second output to depend on what I select on radio button.
In both cases, the first DT output shows the table as loaded (with good or bad names). One approach is to fix the column names silently:
library(shiny)
library(DT)
ui <- shinyUI(fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(fileInput(
'file1',
'Choose CSV File',
accept = c('text/csv',
'text/comma-separated-values,text/plain',
'.csv')
)),
mainPanel(
DT::dataTableOutput('contents'),
DT::dataTableOutput('filtered')
)
)
))
server <- function(input, output, session) {
myData <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
data <- read.csv(inFile$datapath, header = TRUE)
data
})
# Approach 1: fix the data silently
# Replace column names if needed
fixData <- reactive({
req(input$file1)
df <- myData()
expectedColumns <- c("mpg", "cyl", "wt")
if (!all(expectedColumns %in% colnames(myData()))) {
#Change row to column and delete first row
colnames(df) <- df[1, ]
df = df[-1,]
}
df
})
output$contents <- DT::renderDataTable({
DT::datatable(myData())
})
data2 <- reactive({
# Select columns of the dataframe
df1 <- select(fixData(), mpg, cyl, wt)
df1
})
#Output based on either raw or replaced column table
output$filtered <- DT::renderDataTable({
DT::datatable(data2())
})
}
runApp(list(ui = ui, server = server))
Another approach is to use radio buttons as you have planned:
library(shiny)
library(DT)
ui<- shinyUI(fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose CSV File',
accept=c('text/csv',
'text/comma-separated-values,text/plain',
'.csv')),
# radio button to show either row or replaced column table
radioButtons("radio", label = h3("Replace columns"),
choices = list("Raw table" = 1, "Change column names" = 2),
selected = 1)
),
mainPanel(
DT::dataTableOutput('contents'),
DT::dataTableOutput('filtered')
)
)
)
)
server <- function(input, output, session){
myData <- reactive({
inFile <- input$file1
if (is.null(inFile)) return(NULL)
data <- read.csv(inFile$datapath, header = TRUE)
data
})
# Approach 2: click on an radioButton to fix the data
fixData <- reactive({
req(input$file1)
df <- myData()
if (input$radio == 2) {
#Change row to column and delete first row
colnames(df) <- df[1,]
df = df[-1, ]
}
df
})
output$contents <- DT::renderDataTable({
DT::datatable(myData())
})
data2<- reactive({
# Select columns of the dataframe
df1 <- select(fixData(),mpg,cyl,wt)
df1
})
#Output based on either raw or replaced column table
output$filtered <- DT::renderDataTable({
DT::datatable(data2())
})
}
runApp(list(ui=ui,server=server))

R shiny: save chosen factors from input data

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"))
})

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