I have created a Shiny app which takes .csv file as input and generates tabs based on the Grade column.
The data looks like this
Name Age Score Grade
Jane 13 89 A
Hanna 14 67 B
Jack 13 80 A
Leena 14 78 B
Amy 12 65 B
Nina 14 90 A
Steven 12 45 C
Omy 13 59 C
The code will generate tables in each tab containing only rows of the dataset that match the Grade.
These tables are editable. I am trying to download the edited table from the active tabPanel, but I am stuck on what should be the content in the downloadHandler. I have attached my code for reference. Sorry for the messy code, I am fairly new to shiny.
library(shiny)
library(xlsx)
library(rhandsontable)
ui <- fluidPage(
titlePanel("Scores"),
sidebarLayout(
sidebarPanel(
fileInput("file", "Upload the file"),
br(),
downloadButton('downloadData', 'Save as excel')
),
mainPanel(uiOutput("op"))
)
)
server <- function(input, output, session) {
data <- reactive({
file1 <- input$file
if (is.null(file1)) {
return()
}
read.csv(file = file1$datapath)
})
fun1 <- function(x) {
mydf <- data()
DF <- mydf[(mydf$Grade == x), c(1:3)]
table <- renderRHandsontable({
newtable<- rhandsontable(DF, rowHeaders = NULL)
})
tabPanel(x, table)
}
output$op <- renderUI({
if (is.null(data()))
helpText("File not uploaded!")
else{
mydf <- data()
Tabs <- lapply((unique(mydf$Grade)), fun1)
do.call(tabsetPanel, c(id = "tabs", Tabs))
}
})
output$downloadData <- downloadHandler(
filename = function() {
'Edited table.xls'
},
# what should go in place of table
content = function(file) {
write.xlsx(table, file)
}
)
}
shinyApp(ui, server)
Related
I have some R code that is meant to run a .rmd file so that I can create a unique report for multiple individuals at the same time.
Simplistically, my issue is that code chunk 1 creates a PDF output that is correct, however, will only create one PDF, and not one for each athlete in my dataset.
Code chunk 2 will create multiple PDF outputs (one for each athlete in the dataset), however, the variables created within the for loop (i.e., Athlete_full_name_i, Athlete_Sport_i) do not work. In the PDF output, these values are presented as "c("values","in","the","list")".
I think the code is close to working well but I would Love some advice to make this run smoother.
Thank you!
code chunk 1:
'''
#libraries
library(tidyverse)
library(rmarkdown)
library(dplyr)
#extra information
Folder_path1 <- "path to folder"
Excel_sheet1 <- "Data"
StartDate1 <- "2021-09-01"
EndDate1 <- Sys.Date()
#create athlete dataframe to run one, or multiple athlete reports at once.
Athlete_full_name <- c("John Smith", "Joe Tree") #as the name should appear in the report
Athlete_file_name <- c("SmithJ", "TreeJ") #Last name, first initial
Athlete_Team <- c("Men's Football", "Men's Darts") #Sport as it should appear in the report
Athlete_df <- data.frame(Athlete_full_name, Athlete_file_name, Athlete_Team)
Names <- unique(Athlete_df$Athlete_file_name)
#For loop - each athlete in Athlete_df gets unique report
for (i in 1:length(Names)) {
#creating new variables for each iteration
#determine athlete full name
Athlete_full_name_i <- Athlete_df %>%
filter(Athlete_file_name == Names[i]) %>%
subset(select = 1)
#determine athlete sport
Athlete_Sport_i <- Athlete_df %>%
filter(Athlete_file_name == Names[i]) %>%
subset(select = 3)
#create Athlete report with unique params and unique filename
rmarkdown::render("R2S_Dinos_JumpV3.Rmd",
params = list(Athlete_csvname = Names[i],
Athlete_fullname = Athlete_full_name_i,
Team = Athlete_Sport_i,
Folder_path = Folder_path1,
Excel_sheet = Excel_sheet1,
StartDate = StartDate1,
EndDate = EndDate1),
output_format = "pdf_document",
output_file=paste0("Athlete Reports/", Names[i], "_Report_", Sys.Date(), ".pdf"))
}
'''
code chunk 2:
'''
'''
#libraries
library(tidyverse)
library(rmarkdown)
library(dplyr)
#extra information
Folder_path1 <- "path to folder"
Excel_sheet1 <- "Data"
StartDate1 <- "2021-09-01"
EndDate1 <- Sys.Date()
#create athlete dataframe to run one, or multiple athlete reports at once.
Athlete_full_name <- c("John Smith", "Joe Tree") #as the name should appear in the report
Athlete_file_name <- c("SmithJ", "TreeJ") #Last name, first initial
Athlete_Team <- c("Men's Football", "Men's Darts") #Sport as it should appear in the report
Athlete_df <- data.frame(Athlete_full_name, Athlete_file_name, Athlete_Team)
#For loop - each athlete in Athlete_df gets unique report
for (Athlete_file_name in Athlete_df$Athlete_file_name) {
#creating new variables for each iteration
#determine athlete full name
Athlete_full_name_i <- Athlete_df %>%
filter(Athlete_file_name == Athlete_file_name) %>%
subset(select = 1)
#determine athlete sport
Athlete_Sport_i <- Athlete_df %>%
filter(Athlete_file_name == Athlete_file_name) %>%
subset(select = 3)
#create Athlete report with unique params and unique filename
rmarkdown::render("R2S_Dinos_JumpV3.Rmd",
params = list(Athlete_csvname = Athlete_file_name,
Athlete_fullname = Athlete_full_name_i,
Team = Athlete_Sport_i,
Folder_path = Folder_path1,
Excel_sheet = Excel_sheet1,
StartDate = StartDate1,
EndDate = EndDate1),
output_format = "pdf_document",
output_file=paste0("Athlete Reports/", Athlete_file_name, "_Report_", Sys.Date(), ".pdf"))
}
'''
When I run Code chunk 1 with a test Rmd output file it works as expected (two pdf files):
test_out.Rmd file:
---
title: "test_out"
params:
name: Bob
personid: 1
---
`r params$name` is person number `r params$personid`
Shortened slightly for clarity:
library(tidyverse)
library(rmarkdown)
Athlete_full_name <- c("John Smith", "Joe Tree")
Athlete_file_name <- c("SmithJ", "TreeJ")
Athlete_Team <- c("Men's Football", "Men's Darts")
Athlete_df <- data.frame(Athlete_full_name, Athlete_file_name, Athlete_Team)
Names <- unique(Athlete_df$Athlete_file_name)
for (i in 1:length(Names)) {
Athlete_full_name_i <- Athlete_df %>%
filter(Athlete_file_name == Names[i]) %>%
subset(select = 1)
Athlete_Sport_i <- Athlete_df %>%
filter(Athlete_file_name == Names[i]) %>%
subset(select = 3)
rmarkdown::render(
"test_out.Rmd",
params = list(Athlete_fullname = Athlete_full_name_i,
Team = Athlete_Sport_i),
output_format = "pdf_document",
output_file = paste0("test_out/", Names[i], "_Report_", Sys.Date(), ".pdf")
)
}
You could make this tidier and easier to debug with an anonymous function inside group_walk - this is the way I'd recommend doing it:
Athlete_df |>
rowwise() |>
group_walk(function(data, key) {
rmarkdown::render(
"test_out.Rmd",
params = list(
Athlete_fullname = data$Athlete_full_name,
Team = data$Athlete_Team
),
output_format = "pdf_document",
output_file = paste0("test_out/", data$Athlete_file_name, "_Report", ".pdf")
)
})
(same result)
In your code chunk 2 the problem comes when you filter with Athlete_file_name == Athlete_file_name. This tests whether these variables are equal within the dataframe, i.e. keep every row where the Athlete_file_name column equals the Athlete_file_name column, which means every row every time! Change the name of the iterating variable to e.g. in_name:
for (in_name in Athlete_df$Athlete_file_name) {
...
filter(Athlete_file_name == in_name) %>% # twice
...
output_file = paste0("test_out/", in_name,
"_Report_", Sys.Date(), ".pdf"
...
}
Thanks for the feedback everyone, there was an error in the for loop. The solution is as follows:
'''
#libraries
library(tidyverse)
library(rmarkdown)
library(dplyr)
#extra information
Folder_path1 <- "path to folder"
Excel_sheet1 <- "Data"
StartDate1 <- "2021-09-01"
EndDate1 <- Sys.Date()
#create athlete dataframe to run one, or multiple athlete reports at once.
Athlete_full_name <- c("John Smith", "Joe Tree") #as the name should appear in the report
Athlete_file_name <- c("SmithJ", "TreeJ") #Last name, first initial
Athlete_Team <- c("Men's Football", "Men's Darts") #Sport as it should appear in the report
Athlete_df <- data.frame(Athlete_full_name, Athlete_file_name, Athlete_Team)
Names <- unique(Athlete_df$Athlete_file_name)
#For loop - each athlete in Athlete_df gets unique report
for (i in Names) {
#creating new variables for each iteration
#determine athlete full name
Athlete_full_name_i <- Athlete_df %>%
filter(Athlete_file_name == i) %>%
subset(select = 1)
#determine athlete sport
Athlete_Sport_i <- Athlete_df %>%
filter(Athlete_file_name == i) %>%
subset(select = 3)
#create Athlete report with unique params and unique filename
rmarkdown::render("R2S_Dinos_JumpV3.Rmd",
params = list(Athlete_csvname = i,
Athlete_fullname = Athlete_full_name_i,
Team = Athlete_Sport_i,
Folder_path = Folder_path1,
Excel_sheet = Excel_sheet1,
StartDate = StartDate1,
EndDate = EndDate1),
output_format = "pdf_document",
output_file=paste0("Athlete Reports/", i, "_Report_", Sys.Date(), ".pdf"))
}
enter image description hereI copied the exercise code but it is for cereal, how do I change it to show air quality? I will attach a picture for reference from the book rstudio for dummies. I try inputting the data set but it throws off my app.
library(shinydashboard)
library(MASS)
ui <- dashboardPage(
dashboardHeader(title = "Brushing"),
dashboardSidebar(collapsed = TRUE),
dashboardBody(
fluidRow(
plotOutput("CerealPlot",
click = "single_click",
hover = "hovering",
brush = "brushing")
),
box((verbatimTextOutput("coords")), width = 8)
)
)
head(airquality)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$CerealPlot <- renderPlot({
plot(x=UScereal$protein, y=UScereal$calories,
xlab="Protein(gm)",
ylab="Calories",
pch=as.character(UScereal$mfr))
})
output$coords <- renderPrint({
nearPoints(UScereal, input$single_click,
xvar = "protein", yvar = "calories", threshold=20)
})
}
# Run the application
shinyApp(ui=ui, server = server)
I have a shiny app below that shows a file content from a server-side sqlite file
I want to overwrite the sqlite every week on my shiny server from a csv upload and filter it through the App
When I update the sqlite file from within the shiny ran in RStudio I get new content OK and am able to work with it and upload multiple new content as expected
When I run the same app on the shiny server in my browser, I get disconnected from the server as soon as I click on the 'Upload' button after uploading the new csv with 'Browse' (which succeeds).
Can someone please explain this behaviour
I suspect something off in :
observeEvent(input$Upload, {
if(is.null(input$Browse))
{
return(NULL)
}
else
{
file <- input$Browse
createDB(file$datapath, basename(file$name), dbfile)
shinyalert(paste(basename(file$name), "database uploaded, please refresh the session", sep=" "), type = "success", timer=2000)
}
})
REM: it is not the shinyalert though
my full App code :
# accounts.shinyapp
# R/shiny tool to filter the weekly accounts_filtered.csv
library("shiny")
library("shinyBS")
library("shinyalert")
library("RSQLite")
library("DT")
# you may un-comment the next line to allow 10MB input files
options(shiny.maxRequestSize=10*1024^2)
# the following test checks if we are running on shinnyapps.io to limit file size dynamically
# ref: https://stackoverflow.com/questions/31423144/how-to-know-if-the-app-is-running-at-local-or-on-server-r-shiny/31425801#31425801
#if ( Sys.getenv('SHINY_PORT') == "" ) { options(shiny.maxRequestSize=1000*1024^2) }
# App defaults
app.name <- "accounts"
script.version <- "1.0b"
version <- "NA"
names <- c("Last","First","Email","Phone","Level","DeptNum","Code","Short","Logon","Location")
# database functions
createDB <- function(filepath, filename, dbfile){
data <- read_csv(filepath,
locale = locale(encoding = "ISO-8859-2",
asciify = TRUE))
# give proper english names to columns
colnames(data) <- names
data$Email <- tolower(data$Email)
version <- data.frame(version=filename)
# create sqlite and save
mydb <- dbConnect(RSQLite::SQLite(), dbfile)
dbWriteTable(mydb, "data", data, overwrite=TRUE)
dbWriteTable(mydb, "version", version, overwrite=TRUE)
dbDisconnect(mydb)
}
loadDB <- function(dbfile){
mydb <- dbConnect(RSQLite::SQLite(), dbfile)
data <- dbReadTable(mydb, "data")
version <- dbReadTable(mydb, "version")
dbDisconnect(mydb)
# return resulting data.frame
return(list(data = as.data.frame(data), version = as.data.frame(version)))
}
# initial DB creation
# infile <- "Data/ori_accounts_filtered.csv"
# createDB(infile, basename(infile), dbfile)
#############################
# Define UI for application #
#############################
ui <- fluidPage(
useShinyalert(),
HTML('<style type="text/css">
.row-fluid { width: 25%; }
.well { background-color: #99CCFF; }
.shiny-html-output { font-size: 14px; line-height: 15px; }
</style>'),
# Application header
headerPanel("Filter the weekly accounts list"),
# Application title
titlePanel(
windowTitle = "accounts",
tags$a(href="https://http://someIP:8787/accounts", target="_blank",
img(src='logo.png', align = "right",
width="150", height="58.5", alt="myApp"))
),
sidebarLayout(
# show file import weekly update csv data
sidebarPanel(
tags$h5(paste(app.name, " version: ", script.version, sep="")),
tipify(fileInput("Browse",
"Choose new Weekly update:",
accept = ".csv"),
"a accounts_filtered.csv file"),
tipify(actionButton("Upload", "Upload new table"),
"This will replace the current database content!"),
hr(),
checkboxGroupInput("show_vars",
"Check columns to be shown:",
names,
selected = names[c(1:4,6)]),
hr(),
tipify(actionButton("Refresh", "Refresh Session"),
"This will reload the database content!")
),
mainPanel(
htmlOutput("version_tag"),
hr(),
dataTableOutput('dataTable')
)
)
)
#######################
# Define server logic #
#######################
server <- function(input, output, session) {
# initialize content at startup
dbfile <- "Data/data.sqlite"
# load both data and version
mydat <- loadDB(dbfile)
version <- mydat$version[1,1]
accounts <- mydat$data
names <- colnames(accounts)
output$version_tag <- renderText({
paste("<b>Data file: ", version, "</b>")
})
observeEvent(input$Refresh, {
session$reload()
})
observeEvent(input$Upload, {
if(is.null(input$Browse))
{
return(NULL)
}
else
{
file <- input$Browse
createDB(file$datapath, basename(file$name), dbfile)
shinyalert(paste(basename(file$name), "database uploaded, please refresh the session", sep=" "), type = "success", timer=2000)
}
})
output$dataTable <- renderDT(
accounts[,input$show_vars], # data
class = "display nowrap compact", # style
filter = "top", # location of column filters
options = list(pageLength = 20, autoWidth = TRUE),
rownames= FALSE
)
}
# Run the application
shinyApp(ui = ui, server = server)
This is missing.
library("readr")
I have a data table that contains some very wide columns and I want to add a scrolling-bar to make it more presentable. So far I have found examples using a scrolling-bar for the entire table - but ideally I would like to have a scrolling-bar for EACH column in the table if that is possible. Below there is an illustrating example. In this code I want a scrolling-bar for both "This_is_a_very_long_name_1", "This_is_a_very_long_name_2" etc.
library("shinydashboard")
library("shiny")
body <- dashboardBody(
fluidPage(
column(width = 4,
box(
title = "Box title", width = NULL, status = "primary",
div(style = 'overflow-x: scroll', tableOutput('table'))
)
)
)
)
ui <- dashboardPage(
dashboardHeader(title = "Column layout"),
dashboardSidebar(),
body
)
server <- function(input, output) {
test.table <- data.frame(lapply(1:8, function(x) {1:10}))
names(test.table) <- paste0('This_is_a_very_long_name_', 1:8)
output$table <- renderTable({
test.table
})
}
# Preview the UI in the console
shinyApp(ui = ui, server = server)
I thought about splitting the table into 8 tables, making a scrolling table for each of them and then putting them next to each other, but space was added betweeen them and it did not look that nice. I think it would be preferable to keeping it as one table (but suggestions are very welcome!).
Does anyone whether this is possible - and how to solve it?
Thanks in advance!
I would not recommend scrolling column header, i think it would not be very clear to read it or so. Here is the code which You can use to get the header in 2 lines so the columns are not too wide:
library("shinydashboard")
library("shiny")
library(DT)
test.table <- data.frame(lapply(1:8, function(x) {1:10}))
names(test.table) <- paste0('This_is_a_very_long_name_', 1:8)
body <- dashboardBody(
fluidPage(
column(width = 8,
box(
title = "Box title", width = NULL, status = "primary",
div(style = 'overflow-x: scroll', dataTableOutput('table'))
)
)
)
)
ui <- dashboardPage(
dashboardHeader(title = "Column layout"),
dashboardSidebar(),
body
)
server <- function(input, output) {
output$table <- renderDataTable({
names(test.table) <- gsub("_"," ",names(test.table))
datatable(test.table, options = list(columnDefs = list(list(width = '100px', targets = c(1:8)))))
})
}
# Preview the UI in the console
shinyApp(ui = ui, server = server)
[UPDATE] --> Column text rendering
Here is a one solution which can be usefull for You. There is no scrolling, however Your row text displays only first three characters (the number of characters displayed can be changed) and ..., with mouse over the row You get the pop up with whole variable name in this row:
library("shinydashboard")
library("shiny")
library(DT)
x <- c("aaaaaaaaaaaaaa", "bbbbbbbbbbbb", "ccccccccccc")
y <- c("aaaaaaaaaaaaaa", "bbbbbbbbbbbb", "ccccccccccc")
z <- c(1:3)
data <- data.frame(x,y,z)
body <- dashboardBody(
fluidPage(
column(width = 4,
box(
title = "Box title", width = NULL, status = "primary",
div(style = 'overflow-x: scroll', dataTableOutput('table'))
)
)
)
)
ui <- dashboardPage(
dashboardHeader(title = "Column layout"),
dashboardSidebar(),
body
)
server <- function(input, output) {
output$table <- renderDataTable({
datatable(data, options = list(columnDefs = list(list(
targets = c(1:3),
render = JS(
"function(data, type, row, meta) {",
"return type === 'display' && data.length > 3 ?",
"'<span title=\"' + data + '\">' + data.substr(0, 3) + '...</span>' : data;",
"}")),list(width = '100px', targets = c(1:3)))))
})
}
# Preview the UI in the console
shinyApp(ui = ui, server = server)
I would like some advice on how to update a "statusField" after
clicking on a "panel".
The following program demonstrates the problem. The program draws two
frames. You can imagine the left frame to be some kind of drawing area
and the right frame contains the buttons "Red" and "Green".
After clicking on the button labeled "Red" the text of the statusField is
updated to "Current color: Red". The button labeled "Green" updates the text to "Current color: Green".
How to change the text of the statusField after the user clicked on
the left panel? E.g. change it to "You successfully clicked on the
drawing panel."
Why can't I do it in "on click" the same way as in "on command" for
the buttons? (See annotation in the source below.)
Thank you very much.
module Main where
import Graphics.UI.WX
-- | NOP (= No Operation)
data Command = Nop
| Red
| Green
deriving (Eq)
main :: IO ()
main
= start hello
hello :: IO ()
hello
= do currentCommand <- varCreate $ Nop -- current command performed on next click on "pDrawingarea"
status <- statusField [text := "Welcome."]
-- Frames and Panels
f <- frame [ text := "Demo"
, bgcolor := lightgrey ]
pButtons <- panel f [ bgcolor := lightgrey]
pDrawingarea <- panel f [ on paint := draw
, bgcolor := lightgrey
]
set pDrawingarea [on click := do drawingAreaOnClick status currentCommand pDrawingarea
-- set status [text := "User clicked on the panel."]
-- Problem: uncommenting the line above shows the problem
]
bRed <- button pButtons [text := "Red", on command := do varSet currentCommand Red
set status [text := "Current color: Red"]
]
bGreen <- button pButtons [text := "Green", on command := do varSet currentCommand Green
set status [text := "Current color: Green"]
]
set pButtons [ layout := column 1 [ hstretch.expand $ widget bRed
, hstretch.expand $ widget bGreen
]
]
set f [ statusBar := [status]
, layout := row 3 [
minsize (sz 600 500) $ stretch.expand $ widget pDrawingarea
, vstretch.expand $ rule 3 500
, minsize (sz 200 500) $ vstretch.expand $ widget pButtons
]
]
return ()
draw :: DC a -> Rect -> IO ()
draw dc viewArea
= do putStrLn "Imagine some code to repaint the screen."
drawingAreaOnClick :: statusField -> Var Command -> Panel () -> Point -> IO ()
drawingAreaOnClick sf command panel pt
= do c <- varGet command
case c of
Red -> do putStrLn "Imagine some code to do red painting"
Green -> do putStrLn "Imagine some code to do green painting"
After spending lots of time on this problem I found a solution.
The solution is to change the definition of
drawingAreaOnClick :: statusField -> Var Command -> Panel () -> Point -> IO ()
to
drawingAreaOnClick :: Textual x => x -> Var Command -> Panel () -> Point -> IO ()
Because "statusField" itself is a member of the class "Textual" I don't understand the problem.
For the sake of completeness I will mention that I also switched GHC verions.The original problem occurred with GHC 7.8.4 and the solution I found works with GHC 7.10.3. I can't say if the GHC version affects the problem.
For reference the complete working code:
module Main where
import Graphics.UI.WX
-- | NOP (= No Operation)
data Command = Nop
| Red
| Green
deriving (Eq)
main :: IO ()
main
= start hello
hello :: IO ()
hello
= do currentCommand <- varCreate Nop -- current command performed on next click on "pDrawingarea"
status <- statusField [text := "Welcome."]
-- not needed: currentStatus <- varCreate status
-- Frames and Panels
f <- frame [ text := "Demo"
, bgcolor := lightgrey ]
pButtons <- panel f [ bgcolor := lightgrey]
pDrawingarea <- panel f [ on paint := draw
, bgcolor := lightgrey
]
set pDrawingarea [on click := do drawingAreaOnClick status currentCommand pDrawingarea
-- set status [text := "User clicked on the panel."]
-- Problem: uncommenting the line above shows the problem
]
bRed <- button pButtons [text := "Red", on command := do varSet currentCommand Red
set status [text := "Current color: Red"]
]
bGreen <- button pButtons [text := "Green", on command := do varSet currentCommand Green
set status [text := "Current color: Green"]
--sf <- varGet currentStatus
-- set sf [text := "yyy"]
]
set pButtons [ layout := column 1 [ hstretch.expand $ widget bRed
, hstretch.expand $ widget bGreen
]
]
set f [ statusBar := [status]
, layout := row 3 [
minsize (sz 600 500) $ stretch.expand $ widget pDrawingarea
, vstretch.expand $ rule 3 500
, minsize (sz 200 500) $ vstretch.expand $ widget pButtons
]
]
return ()
draw :: DC a -> Rect -> IO ()
draw dc viewArea
= do putStrLn "Imagine some code to repaint the screen."
drawingAreaOnClick :: Textual x => x -> Var Command -> Panel () -> Point -> IO ()
drawingAreaOnClick sf command panel pt
= do c <- varGet command
set sf [text := "Drawing on the screen."]
case c of
Red -> do putStrLn "Imagine some code to do red painting"
Green -> do putStrLn "Imagine some code to do green painting"