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)
Related
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.
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
})
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 am trying to create a shiny UI to input CSV, perform paired t-test (equal variance) and to generate a heatmap from a sample dataset.
I have been able to generate a UI CSV upload tab, however, I am now struggling with my t-test and p-value tab, I continue to get this error message:
Warning: Error in : evaluation nested too deeply: infinite recursion / options(expressions=)?
90:
shinyServer <- function(input, output, session){
data5<- reactive({
req(input$file1)
data5<-read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote)
})
output$contents <- renderTable({
req(input$file1)
if(input$disp == "head") {
return(head(data5()))
}
else {
return(data5())
}
})
####ttests
data1 <- reactive({
data1 <- data.matrix(data5())
})
ctrl <- reactive({
ctrl <- data1()[, c(2:11)]
})
smple <- reactive({
smple <- data1()[, c(12:21)]
})
vector1 <- c(1:10)
pvalue <- c()
pval <- reactive ({
for (i in vector1) {
pvalue[i] <-
t.test(ctrl()[i, ],
smple()[i, ],
paired = FALSE,
var.equal = FALSE)$p.value
}
pvalue
})
signif <- reactive({
sig <- c()
for (n in vector1) {
if (pval()[n] < 0.05) {
sig <- append(sig, n)
}
}
sig
})
genecol <- reactive({
genecol <- data5()[, 1]
})
P.Vals <- reactive({
as.character(P.Vals())
})
data6 <- reactive({
data6 < -data.frame(genecol(), P.Vals())
})
output$pvalue <- renderTable(data6())
}
UI:
library(shiny)
library(shinythemes)
ui <- fluidPage(
####name app
titlePanel("Uploading Files"),
tabsetPanel(
tabPanel("Upload CSV"),
# Sidebar
sidebarLayout(
sidebarPanel(
###input option CSV file
fileInput(
"file1",
"Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
tags$hr(),
###check if CSV has a header
checkboxInput("header", "Header", TRUE),
###format text file into table with separator (comma,semicolon,tab)
radioButtons(
"sep",
"Separator",
choices = c(
Comma = ",",
Semicolon = ";",
Tab = "\t"
),
selected = ","
),
tags$hr(),
####select head of data or all
radioButtons(
"disp",
"Display",
choices = c(Head = "head",
All = "all"),
selected = "head"
)
),
####output panel
mainPanel(# Output: DATA
tableOutput("contents"))
)
),
tabPanel("T-Test",
h4("pvalue"),
tableOutput("pvalue"))
)
I understand this may be complicated but I am a complete beginner, really struggling to get my head round it
Without a reproducible example (see the comments), I am guessing it could be your reactives. I.e.:
data1 <- reactive({
data1 <- data.matrix(data5())
})
A reactive is kinda like a function, as it returns the last value (or whatever is passed with a return statement). In your code, you seem to be reassigning the reactive data1 with a value, within it self. This might not be the case due to scoping, but with the recursive error, this is my first guess.
I suggest you start with editing your reactives to:
data1 <- reactive({
data.matrix(data5())
})
EDIT:
Found it:
P.Vals <- reactive({
as.character(P.Vals())
})
This is most definitely a recursive expression. This is your culprit.
I recently updated R and shiny.
The problem I am having is translating Shiny code into shiny modules. I thought the RStudio guide was very good, but I seem to be missing something fundamental. I would love any direct guidance on the project I am working on. I would also love recommended principles for how to convert from Shiny code to modularized code, particularly hints missing from the RStudio article
My goal in the shiny app below (which is part of a larger project) is to create an app that allows someone to load a .csv file, transform that file into a dissimilarity matrix, and download the matrix.
I have code that works in Shiny, but I haven't been able to convert it into a modularized version.
Note: Because I couldn't figure out how to add a separate .csv file for people to use when evaluating this code, and I wanted to make sure I included sample code, I tossed in sample data and commented out the code that would have allowed people to select an external .csv file. I hope that doesn't mess things up too much.
library(dplyr)
library(cluster)
library(shiny)
bin <- sample(letters[1:10], 50, replace = TRUE) %>% as.factor()
df <- matrix(bin, ncol = 5) %>% as.data.frame()
# User Input Function
cardsortInput <- function(id) {
ns <- NS(id)
tagList(
fileInput(
inputId = "datafile",
label = "Card Sort Results (.csv)",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
checkboxInput(inputId = "header",
label = "Header",
value = TRUE),
checkboxInput(inputId = "strings",
label = "Strings as Factors",
value = TRUE),
selectInput(inputId = "dmetric",
label = "Distance Metric",
choices = c("Euclidean" = "euclidean",
"Manhattan" = "manhattan",
"Gower" = "gower"),
selected = "gower"),
checkboxInput(inputId = "standard",
label = "Data Standardization",
value = FALSE),
downloadButton('downloadData', 'Download Dissimilarity Matrix'),
br(),
br(),
tabsetPanel(
tabPanel(
"Card Sort Data",
tableOutput(
outputId = "cs_table")
),
tabPanel(
"Dissimilarity Matrix",
tableOutput(
outputId = "cs_dissMatrix")
)
)
)
}
# Server Function
cardsort <- function(input, output, session) {
userFile <- reactive({
validate(need(input$datafile, message = FALSE))
input$datafile
})
#dataframe <- reactive({
# read.csv(
# file = userFile()$data.path,
# header = input$header,
#stringsAsFactors = TRUE)
#})
dataframe <- reactive({df
})
diss_matrix <- reactive({
data <- dataframe()
if (is.null(data))
return(NULL)
daisy(
x = data,
metric = input$dmetric,
stand = input$standard) %>%
as.matrix()
})
output$dissMatrix <- renderTable({
diss_matrix()
})
output$downloadData <- downloadHandler(
filename = function() {paste("data-",
Sys.Date(),
".csv", sep="")
},
content = function(file) {
write.csv(diss_matrix(),
file,
row.names = FALSE)
}
)
}
#########
ui <- fluidPage(
cardsortInput("test")
)
server <- function(input, output, session) {
datafile <- callModule(cardsort, "test")
}
shinyApp(ui = ui, server = server)