Shiny R - source script that updates data frame - error in .jcall - r

I am trying to build a shiny application that visualizes data using ggplot2. I want the app to be adaptive to new data that is copied as .xlsx files to a dropbox folder.
I wrote a script to read all excel files in the dropbox folder and to tranform them to a data frame (.Rda). The script works perfectly fine but when I try to source it from my shiny app, the application breaks down. Below you can find my app, the script to import data and the error that occurs.
This is an example of my App:
library(shiny)
library(ggplot2)
load("df.Rda")
ui <- bootstrapPage(
actionButton(inputId = "button", label="Upload new Data"),
plotOutput("plot")
)
server <- function(input, output) {
observeEvent(input$button,{
source("PreparationServer.R")
})
output$plot <- renderPlot(
ggplot(df, aes(City, Total.Inc.VAT))+
geom_bar(stat="identity")
)
}
shinyApp(ui, server)
This is the script I use to transform the excel files to .Rda:
###Load required libraries
library(xlsx)
##########################################################################
## Load Excel Sheets as Data Frames
files <- (Sys.glob("c:///Users/T400/Dropbox/HTM (1)/*.xlsx"))
listOfFiles <- lapply(files, function(x) read.xlsx(x,
sheetIndex = 1,
colIndex = 1:19,
header=TRUE,
encoding = 'UTF-8'))
df <- as.data.frame(listOfFiles[1])
for(i in 2:length(listOfFiles)){
df_temp <- as.data.frame(listOfFiles[i])
df <- rbind(df, df_temp)
}
save(df, file="c://Users/T400/Dropbox/HTM (1)/df.Rda")
##########################################################################
I am getting the following error when I try to source my script from the shiny app:
Warning: Error in .jcall: java.lang.IllegalArgumentException: Your
InputStream was neither an OLE2 stream, nor an OOXML stream

Related

uploading multiple file input and accessing them through the shiny app to perfrom a loop

I'm pretty stuck here; I have created a simple shiny app with the possibility of uploading multiple files. However, I don't know how can I move on from here and access the files directly within the shiny app, for example, get all the uploaded data files into one data.frame to perform a loop later on.
for example we have
data_1 <- "data file 1"
data_2 <- "data file 2"
data_3 <- "data file 3"
data_4 <- "data file 4"
dataSet <- data.frame(DATA= c(1,2,3,4),
DATAFILE=c(data_1 ,data_2 ,data_3 ,data_4))
Is there any way to do that? I hope I have been able to explain myself thoroughly. I really appreciate any help you can provide.
library(shiny)
options(shiny.maxRequestSize = 30 * 1024^2)
ui <- fluidPage(
fileInput("upload", NULL, buttonLabel = "Upload...", multiple = TRUE),
tableOutput("files")
)
server <- function(input, output, session) {
output$files <- renderTable(input$upload)
}
shinyApp(ui, server)
input$upload is a data.frame containing four columns, to read the files we'll need datapath column that contains the temp path with the uploaded data, in this case they are csv's. From there we use a function like readr::read_csv() to transform the raw uploaded data into a df.
We can construct a reactive that consists in a list with all the uploaded files in it.
# read all the uploaded files
all_files <- reactive({
req(input$upload)
purrr::map(input$upload$datapath, read_csv) %>%
purrr::set_names(input$upload$name)
})
Full app:
library(shiny)
library(tidyverse)
library(DT)
# create some data to upload
write_csv(mtcars, "mtcars.csv")
write_csv(mpg, "mpg.csv")
write_csv(iris, "iris.csv")
options(shiny.maxRequestSize = 30 * 1024^2)
ui <- fluidPage(
fileInput("upload", NULL, buttonLabel = "Upload...", multiple = TRUE),
DT::DTOutput("files"),
tableOutput("selected_file_table")
)
server <- function(input, output, session) {
output$files <- DT::renderDT({
DT::datatable(input$upload, selection = c("single"))
})
# read all the uploaded files
all_files <- reactive({
req(input$upload)
purrr::map(input$upload$datapath, read_csv) %>%
purrr::set_names(input$upload$name)
})
#select a row in DT files and display the corresponding table
output$selected_file_table <- renderTable({
req(input$upload)
req(input$files_rows_selected)
all_files()[[
input$upload$name[[input$files_rows_selected]]
]]
})
}
shinyApp(ui, server)
There are two stages to this:
When you select a file what happens is that is gets copied into a temp directory. One of the values returned by the input is the location of the temp file, another is the original file name.
Once you have the file path you can use a function to read the data from that temp file.
The example at the bottom of this should be helpful (although your example needs a little bit more than this one because you have selected multiple files):
https://shiny.rstudio.com/reference/shiny/1.6.0/fileInput.html

Shiny - importing and exporting without user interaction by only specifying one file and its path

All the shiny tutorials I see import multiple data manually via fileInput() then export manually.
Currently, I just have a single R script files that I manually change the few variables each time I run it.
For example, at directory C:/Users/Users/Project/000-0000, I want to update 000-0000_result1 and 000-0000_result2 using information from 000-0000_NewData.
#### Variables I change
file_name <- "C:/Users/Users/Project/000-0000/000-0000_NewData.csv"
parameterNum <- 3
#### Rest of the codes that I never change
setwd(dirname(file_name)
projectID <- str_extract(file_name, "[^_]+") #would be 000-0000 in this case
dat0 <- read_csv(file_name)
prev_result1 <- read_csv(str_c(projectID, "_result1"))
prev_result2 <- read_csv(str_c(projectID, "_result2"))
... #data step using parameterNum
write_csv(new_result1, str_c(projectID, "_result1"))
write_csv(new_result2, str_c(projectID, "_result2"))
I want to create a Shiny app where I can just specify the file_name with fileInput("dat0","Upload a new data") and numericInput() then run the rest of the script.
I do not want to manually select multiple files then export them, because I have a lot of _result files mixed with other files sharing the same filetypes.
I was looking at input$dat0$datapath but it seems that shiny creates a tmp folder with only files loaded through fileInput()
Is my plan possible using Shiny? I am using flexdashboard, but I also welcome and will try to adjust standard Shiny answer on my own.
Perhaps something like this:
library(shiny)
library(tidyverse)
ui <- fluidPage(
textInput('file_name', 'Path to filename', value = "C:/Users/Users/Project/000-0000/000-0000_NewData.csv"),
numericInput('parameterNum', 'Insert Parameter Number',value = 3, min = 0),
actionButton(inputId = 'save', label = 'Write csvs')
)
server <- function(input, output, session) {
observe({
setwd(dirname(input$file_name))
})
projectID <- reactive({
str_extract(inpt$file_name, "[^_]+")
})
prev_result1 <- reactive({
read_csv(str_c(projectID(), "_result1"))
#some calculation
})
prev_result2 <- reactive({
read_csv(str_c(projectID(), "_result2"))
#some calculation
})
observeEvent(input$save, {
write_csv(prev_result1(), str_c(projectID(), "_result1"))
write_csv(prev_result2(), str_c(projectID(), "_result2"))
})
}
shinyApp(ui, server)

Deploying R Markdown File with Shiny Component

Building off my previous question, I'm having trouble creating a DEPLOYABLE R Markdown file that has a Shiny component JUST to allow user to upload an Excel workbook.
Essentially, I want to run Python code on top of the data that the user provides.
This doesn't seem to deploy correctly to RConnect (it just times out):
---
output: html_document
---
library(dplyr)
library(miniUI)
library(shiny)
library(XLConnect)
launch_shiny <- function() {
ui <- miniPage(
gadgetTitleBar("Input Data"),
miniContentPanel(
fileInput(inputId = "my.file", label = NULL,
multiple = FALSE)
)
)
server <- function(input, output, session) {
wb <- reactive({
new.file <- input$my.file
loadWorkbook(
filename = new.file$datapath,
create = FALSE,
password = NULL
)
})
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
df_lst <- reactive({
# read all sheets into a list
lapply(getSheets(wb()),
function(sheet){
readWorksheet(object = wb(),
sheet = sheet)
})
})
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
observeEvent(input$done, {
# get the list of dfs from the app
stopApp(c(df_lst()))
})
}
runGadget(ui, server)
}
test <- launch_shiny()
If I add runtime: shiny at the beginning, it would fail as runGadget uses runApp, which is also used by shiny; however, I need to make sure that I can return a variable (in this case, list of data frames) to the global environment, which is why I was using stopApp.
What are my options at this point?

Passing reactive data to global environment

I want to use Shiny within RMarkdown for users to upload data (xlsx file).
Then I want to pass all the worksheets as R data frames (w/o reactivity) to run rest of the RMarkdown file.
I mainly want to convert them into data frames so I can use reticulate to run Python code as well.
I've tried this, and it doesn't seem to quite work:
library(dplyr)
library(miniUI)
library(shiny)
library(XLConnect)
launch_shiny <- function() {
ui <- miniPage(
gadgetTitleBar("Input Data"),
miniContentPanel(
fileInput(inputId = "my.file", label = NULL, multiple = FALSE)
)
)
server <- function(input, output, session) {
wb <- reactive({
new.file <- input$my.file
loadWorkbook(
filename = new.file$datapath,
create = FALSE,
password = NULL
)
})
observeEvent(input$done, {
stopApp(c(wb()))
})
}
runGadget(ui, server)
}
test <- launch_shiny()
df1 <- readWorksheet(object = test, sheet = "sheet1")
df2 <- readWorksheet(object = test, sheet = "sheet2")
It throws this error:
Error in (function (classes, fdef, mtable) :
unable to find an inherited method for function ‘readWorksheet’ for signature ‘"list", "character"’
I can return one sheet at a time using stopApp(readWorksheet(object = wb(), sheet = "sheet1")), but I can't seem to return an entire workbook or multiple data frames at the same time.
I don't really want to read in xlsx, save each sheet as csv in working directory, then read those files in again.
Would anyone have a good suggestion on how to get around this?
The documentation of fileInput() states in the details:
datapath
The path to a temp file that contains the data that was
uploaded. This file may be deleted if the user performs another upload
operation.
Meaning that the datapath given in the input variable is a temporary file that is no longer accessible after you close the App, which is what the function readWorksheet will try to do.
So you'll have to read the sheets in the server and return the dataframes somehow.
I did that by defining a second reactive value which is basically a list of dataframes returned by applying lapply on all the sheets in wb, in this case test will be this list of data frames.
There might be other ways (more efficient, or suits your purpose better) to do this, but here it is:
library(dplyr)
library(miniUI)
library(shiny)
library(XLConnect)
launch_shiny <- function() {
ui <- miniPage(
gadgetTitleBar("Input Data"),
miniContentPanel(
fileInput(inputId = "my.file", label = NULL,
multiple = FALSE)
)
)
server <- function(input, output, session) {
wb <- reactive({
new.file <- input$my.file
loadWorkbook(
filename = new.file$datapath,
create = FALSE,
password = NULL
)
})
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
df_lst <- reactive({
# read all sheets into a list
lapply(getSheets(wb()),
function(sheet){
readWorksheet(object = wb(),
sheet = sheet)
})
})
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
observeEvent(input$done, {
# get the list of dfs from the app
stopApp(c(df_lst()))
})
}
runGadget(ui, server)
}
test <- launch_shiny()

Reactive Loading in R Shiny [duplicate]

I want to import a .RData file with fileInput but It doesn't work, I have this error message :
Error in my.data$TYPE_DE_TERMINAL : $ operator is invalid for
atomic vectors
dt <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
load(inFile$datapath)
})
GetData <- reactive({
my.data <- dt()
When I try my application with a .RData imported manually it works well (I remplaced dt() directly with a dataframe in my directory) ...
The following example solves the problem. It allows you to upload all .RData files.
Thanks to #Spacedman for pointing me to a better approach of loading the data:
Load the file into a new environment and get it from there.
For the matter of the example being "standalone" I inserted the top section that stores two vectors to your disk in order to load and plot them later.
library(shiny)
# Define two datasets and store them to disk
x <- rnorm(100)
save(x, file = "x.RData")
rm(x)
y <- rnorm(100, mean = 2)
save(y, file = "y.RData")
rm(y)
# Define UI
ui <- shinyUI(fluidPage(
titlePanel(".RData File Upload Test"),
mainPanel(
fileInput("file", label = ""),
actionButton(inputId="plot","Plot"),
plotOutput("hist"))
)
)
# Define server logic
server <- shinyServer(function(input, output) {
observeEvent(input$plot,{
if ( is.null(input$file)) return(NULL)
inFile <- isolate({input$file })
file <- inFile$datapath
# load the file into new environment and get it from there
e = new.env()
name <- load(file, envir = e)
data <- e[[name]]
# Plot the data
output$hist <- renderPlot({
hist(data)
})
})
})
# Run the application
shinyApp(ui = ui, server = server)

Resources