I'm trying to speed up an R Shiny app I'm building to explore a set of data. The app displays sentences for some of the data and a series of bar charts for other data. Each of these is rendered separately.
Every time I click one of the checkboxes, however, to filter the data, the whole thing re-renders and this takes about a second to complete. How can I isolate the rendering of all of the charts until a "go" button is clicked? I've tried a few other suggestions on SO but haven't found out how to do it with multiple renderPlots.
Sorry for the mess of code. I didn't want to shorten it too much or I'll lose some of the context.
library(ggplot2)
d <- read.csv("dm_survey.csv")
server <- function(input, output) {
output$surveyPlot1 <- renderPlot({
d <- d[d$"Frequency of Games" %in% c(input$frequency),]
d <- d[d$"Length of Games" %in% c(input$length),]
d <- d[d$"Primary Locations" %in% c(input$locations),]
d <- d[d$"Campaign Worlds" %in% c(input$worlds),]
d <- d[d$"Adventures" %in% c(input$adventures),]
d <- d[d$"Preferred Combat Type" %in% c(input$combat),]
d <- d[d$"Preparation Time" %in% c(input$preptime),]
output$textmain <- renderUI(HTML("<h1>2016 D&D Dungeon Master Survey</h1>"))
table_columns <- c("Campaign Worlds", "Primary Locations",
"Adventures",
"Preferred Combat Type")
total_sentences <- c()
for (table_column in table_columns) {
num_respondents <- nrow(d[table_column])
question_title <- tolower(table_column)
sentence <- paste("Of", num_respondents, "respondents on", question_title, collapse = "")
tbl <- data.frame(sort(table(d[table_column]),decreasing = TRUE))
if (nrow(tbl) == 1) {
tbl <- data.frame("Activity" = d[table_column][1,], "Count" = nrow(d),"Freq" = 100)
print(tbl)
} else {
tbl["Percentage"] <- round(tbl["Freq"] / colSums(tbl["Freq"]) * 100, 0)
}
for(i in 1:nrow(tbl)) {
answer_title <- tolower(as.character(tbl[i,1]))
answer_percentage <- tbl[i,3]
sentence <- paste(sentence, ", ", answer_percentage, "% answered ", answer_title, collapse="", sep = '')
}
sentence <- paste(sentence, ".", collapse="", sep = '')
total_sentences <- c(total_sentences, sentence)
}
output$text1 <- renderUI(HTML(paste(total_sentences[1],"<br/><br/>")))
output$text2 <- renderUI(HTML(paste(total_sentences[2],"<br/><br/>")))
output$text3 <- renderUI(HTML(paste(total_sentences[3],"<br/><br/>")))
output$text4 <- renderUI(HTML(paste(total_sentences[4],"<br/><br/>")))
column_name <- "Frequency of Games"
factor_labels <- c("Less than monthly","Monthly","Twice monthly","Weekly","Twice a week","More than twice weekly")
d[,column_name] <- factor(d[,column_name], levels = factor_labels)
p1 <- ggplot(d, aes(factor(d[,column_name])))
p1 + geom_bar() + coord_flip() + theme_minimal() +
scale_y_continuous(expand=c(.1, 0)) +
labs(x = "",
title = column_name,
y=paste("Number of Respondants out of",nrow(d))) +
geom_text(aes(label = scales::percent((..count..)/sum(..count..))),
stat= "count", hjust=-.1)
})
output$surveyPlot2 <- renderPlot({
d <- d[d$"Frequency of Games" %in% c(input$frequency),]
d <- d[d$"Length of Games" %in% c(input$length),]
d <- d[d$"Primary Locations" %in% c(input$locations),]
d <- d[d$"Campaign Worlds" %in% c(input$worlds),]
d <- d[d$"Adventures" %in% c(input$adventures),]
d <- d[d$"Preferred Combat Type" %in% c(input$combat),]
d <- d[d$"Preparation Time" %in% c(input$preptime),]
column_name <- "Length of Games"
factor_labels <- c("Longer than eight hours","About eight hours","About six hours","About four hours","About three hours","About two hours","About an hour")
d[,column_name] <- factor(d[,column_name], levels = factor_labels)
p2 <- ggplot(d, aes(factor(d[,column_name])))
p2 + geom_bar() + coord_flip() + theme_minimal() +
scale_y_continuous(expand=c(.1, 0)) +
labs(x = "",
title = column_name,
y=paste("Number of Respondants out of",nrow(d))) +
geom_text(aes(label = scales::percent((..count..)/sum(..count..))),
stat= "count", hjust=-.1)
})
output$surveyPlot3 <- renderPlot({
d <- d[d$"Frequency of Games" %in% c(input$frequency),]
d <- d[d$"Length of Games" %in% c(input$length),]
d <- d[d$"Primary Locations" %in% c(input$locations),]
d <- d[d$"Campaign Worlds" %in% c(input$worlds),]
d <- d[d$"Adventures" %in% c(input$adventures),]
d <- d[d$"Preferred Combat Type" %in% c(input$combat),]
d <- d[d$"Preparation Time" %in% c(input$preptime),]
column_name <- "Preparation Time"
factor_labels <- c("More than four hours","About four hours","About three hours","About two hours","About an hour","About 30 minutes","About 15 minutes","I don't prepare at all")
d[,column_name] <- factor(d[,column_name], levels = factor_labels)
p3 <- ggplot(d, aes(factor(d[,column_name])))
p3 + geom_bar() + coord_flip() + theme_minimal() +
scale_y_continuous(expand=c(.1, 0)) +
labs(x = "",
title = column_name,
y=paste("Number of Respondants out of",nrow(d))) +
geom_text(aes(label = scales::percent((..count..)/sum(..count..))),
stat= "count", hjust=-.1)
})
output$surveyPlot4 <- renderPlot({
d <- d[d$"Frequency of Games" %in% c(input$frequency),]
d <- d[d$"Length of Games" %in% c(input$length),]
d <- d[d$"Primary Locations" %in% c(input$locations),]
d <- d[d$"Campaign Worlds" %in% c(input$worlds),]
d <- d[d$"Adventures" %in% c(input$adventures),]
d <- d[d$"Preferred Combat Type" %in% c(input$combat),]
d <- d[d$"Preparation Time" %in% c(input$preptime),]
# Set up a bunch of facets to show bar plots
l <- reshape(d,
varying = c("Campaign and Worldbuilding","Story and Adventures","Combat Encounters","NPC Development","Exploration and Roleplay","Treasure and Magic Items","Prop and Handouts"),
v.names = "Times",
timevar = "Activities",
times = c("Campaign and Worldbuilding","Story and Adventures","Combat Encounters","NPC Development","Exploration and Roleplay","Treasure and Magic Items","Prop and Handouts"),
direction = "long")
keeps <- c("Activities", "Times")
l <- l[keeps]
l[l=="None"] <- "None"
l[l=="About 5 minutes"] <- "5 min"
l[l=="About 15 minutes"] <- "15 min"
l[l=="About 30 minutes"] <- "30 min"
l[l=="About an hour"] <- "1 hr"
l[l=="About two hours"] <- "2 hrs"
l[l=="More than two hours"] <- "> 2 hrs"
factor_labels <- c("None","5 min","15 min","30 min","1 hr","2 hrs","> 2 hrs")
factor_charts <- c("Campaign and Worldbuilding","Story and Adventures","Combat Encounters","NPC Development","Exploration and Roleplay","Treasure and Magic Items","Prop and Handouts")
l[,"Times"] <- factor(l[,"Times"], levels = factor_labels)
l[,"Activities"] <- factor(l[,"Activities"], levels = factor_charts)
row_count <- nrow(d)
ggplot(l, aes(x=Times)) + geom_bar() + facet_wrap(~Activities, nrow = 2, scales="free_x") +
xlab(paste("Preparation Time for Specific Activities out of",nrow(d),"Respondants")) +
ylab("Number of respodants") +
scale_y_continuous(expand=c(.1, 0)) +
theme(axis.text.x = element_text(angle = 90, hjust = 1),
axis.title.x=element_text(),
axis.title.x=element_text()) +
geom_text(aes(row_count=row_count, label = paste(round((..count../row_count)*100,0),"%", sep="")),
stat= "count", vjust=-.2, size=3)
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
actionButton("recalculate", "Recalculate"),
checkboxGroupInput("frequency", "Frequency of Games",
unique(d[,2]), selected = unique(d[,2])),
checkboxGroupInput("length", "Length of Games",
unique(d[,3]), selected = unique(d[,3])),
checkboxGroupInput("locations", "Primary Locations",
unique(d[,5]), selected = unique(d[,5])),
checkboxGroupInput("worlds", "Campaign Worlds",
unique(d[,6]), selected = unique(d[,6])),
checkboxGroupInput("adventures", "Adventures",
unique(d[,7]), selected = unique(d[,7])),
checkboxGroupInput("combat", "Combat Type",
unique(d[,8]), selected = unique(d[,8])),
checkboxGroupInput("preptime", "Preparation Time",
unique(d[,9]), selected = unique(d[,9]))
),
mainPanel(htmlOutput("textmain"),
htmlOutput("text1"),
htmlOutput("text2"),
htmlOutput("text3"),
htmlOutput("text4"),
plotOutput("surveyPlot1"),
plotOutput("surveyPlot2"),
plotOutput("surveyPlot3"),
plotOutput("surveyPlot4"))
)
)
shinyApp(ui = ui, server = server)
Related
I am attempting to rbind two data frames in shiny so I can plot them together but I receive an error "replacement has 1 row, data has 0" when I do. I've included my full app below - and I'm assuming the error occurs in these server lines:
# create new cases and new deaths data frames and add a New "NAME" value to each row
ncdf <- cd[,c(1,4)]
nddf <- cd[,c(1,5)]
ncdf$name <- "New Cases"
nddf$name <- "New Deaths"
The app works locally even with the error but when I try to upload it to shinyapps it does not.
global.R
library(RCurl)
library(dplyr)
library(ggplot2)
library(data.table)
library(ggthemes)
library(plotly)
library(DT)
# Pull the data from NYT github and turn it into a data frame
x <- getURL("https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-counties.csv")
csv <- read.csv(text=x)
df <- as.data.frame(csv)
# Create a sorted list of unique states
state_list <- as.character(levels(df$state))
ui.R
ui <- fluidPage(
title = "Covid-19 Cases/Deaths",
headerPanel("Covid-19 Cases/Deaths by County"),
fluidRow(
column(3, uiOutput("sel_state")),
column(4, conditionalPanel(condition = "input.state.length > 0", uiOutput("sel_county")))
),
plotOutput("plot"),
hr(),
)
server.R
function(input, output, session) {
# create a list of selected counties based on which State is selected
selected_state_counties <- reactive({
req(length(input$state) > 0)
df %>%
filter(state == input$state) %>%
pull(county) %>%
as.character()
})
# create the state drop down menu based on the state list from global
output$sel_state <- renderUI({
selectizeInput('state', 'Select a State', choices=c("Choose One" = "", state_list))
})
# create the county drop down menu based on the selected state above
output$sel_county <- renderUI({
selectizeInput('county', 'Select a County', choices=c("Choose One" = "", selected_state_counties()))
})
# create a new data frame of just the selected county data
tab <- reactive({
df %>%
filter(state == input$state) %>%
filter(county == input$county)
})
# plot the specific county data
output$plot <- renderPlot({
# turn tab into a data table for calculation
cd <- as.data.table(tab())
## add and calculate 'new_cases' & 'new_deaths' columns
cd[, new_cases := cases - c(0, head(cases, -1))]
cd[, new_deaths := deaths - c(0, head(deaths, -1))]
# convert to data frame
cd <- as.data.frame(cd)
# strip unused data
cd <- cd[, c(1,5,6,7,8)]
# mutate fields to as date > date, and all other columns as numerical
cd <- mutate(cd, date=as.Date(date), cases=as.numeric(cases), deaths=as.numeric(deaths), new_cases=as.numeric(new_cases), new_deaths=as.numeric(new_deaths))
# create new cases and new deaths data frames and add a New "NAME" value to each row
ncdf <- cd[,c(1,4)]
nddf <- cd[,c(1,5)]
ncdf$name <- "New Cases"
nddf$name <- "New Deaths"
#rename the column with the count in it to cases for both data frames
ncdf <- ncdf %>% rename(cases = new_cases)
nddf <- nddf %>% rename(cases = new_deaths)
# rbind both 'new' data frames together to be plotted as ndf
ndf <- rbind(ncdf, nddf)
## line plot colors/legend
lines <- c('Cases' = 'lightskyblue3', 'Deaths' = 'lightcoral')
## bar plot colors/legend
bars <- c('New Cases' = 'steelblue1', 'New Deaths' = 'tomato1')
## chart title
chart_title <- paste(input$county, "County - Covid-19 Cases/Deaths")
## define plot formats as 'p' and add formatting/titles
p <- ggplot() + labs(title=chart_title, x="Date", color="Legend", fill="") + theme(legend.position="bottom", plot.title = element_text(hjust = 0.5), axis.text.x = element_text(angle = 90)) + scale_color_manual(values = lines) + scale_fill_manual(values = bars) + scale_y_continuous(name="Cases", labels = scales::number_format(accuracy = 1))
## plot cases/deaths as lines from cd data frame; plot new_cases/new_deaths as bar from ndf data frame; using 'p' for formatting
p + geom_line(data=cd, aes(date, cases, group=1, color='Cases'), size=1) + geom_line(data=cd, aes(date, deaths, group=2, color='Deaths'), size=1) + geom_bar(data=ndf, aes(date, cases, fill=name), stat="identity", width=0.5, position = 'dodge') + geom_bar(data=ndf, aes(date, cases, fill=name), stat = 'identity', width=0.5, position="dodge")
})
}
Why do I keep getting the error message: Warning: Error in as.character: cannot coerce type 'environment' to vector of type 'character'? I also updated later and httpuv, but they did not help. I wonder if the server has some functions. This is the code from 2014, and I am trying to reproduce it with updated data. Thank you!
Main server function
server = shinyServer(function(input, output){
# Session selector
Select_session <- reactive({
df <- session
# Search function
# TODO: change from "or" to "and"
if (length(input$keywords!=0)){
kwd_pattern <- gsub(",", "|", input$keywords)
df <- subset(df, grepl(kwd_pattern, df$descr, ignore.case = T))
}
# Date selector
if (!is.null(input$DateRange)){
df <- subset(df,
as.Date(df$date)>=input$DateRange[1] & as.Date(df$date)<=input$DateRange[2])
}
# Area of interest selector
if ("me" %in% input$IssueArea){
df <- subset(df, df$me==1)
}
if ("nu" %in% input$IssueArea){
df <- subset(df, df$nu==1)
}
if ("di" %in% input$IssueArea){
df <- subset(df, df$di==1)
}
if ("hr" %in% input$IssueArea){
df <- subset(df, df$hr==1)
}
if ("hr" %in% input$IssueArea){
df <- subset(df, df$hr==1)
}
if ("ec" %in% input$IssueArea){
df <- subset(df, df$ec==1)
}
if ("us" %in% input$IssueArea){
df <- subset(df, df$importantvote==1)
}
return(df)
})
# Session table output
output$sessionTable <- renderDataTable({
df <- Select_session()
if (length(input$keywords!=0)){
kwd_pattern <- gsub(",", "|", input$keywords)
df <- subset(df, grepl(kwd_pattern, df$descr, ignore.case = T))
}
# Vote title selector
if (!is.null(input$voteTitle)){
df <- subset(df, df$unres_title==input$voteTitle)
}
# Date selector
df <- subset(df,
as.Date(df$date)>=input$DateRange[1] & as.Date(df$date)<=input$DateRange[2])
# Select only following variables in data table and downloaded csv file
vars <- c("date","session","unres_title", "descr", "yes", "no", "abstain")
df <- df[,vars]
return(df)},
# Turn off searching function
# List length, 3 options: 5/25/all
# Page Length, default = 5
options = list(searching = FALSE,
lengthMenu = list(c(5, 25, -1), c('5', '25', 'All')),
pageLength = 5)
)
# Voting selector
Select_voting <- reactive({
df <- vote
# Link session data and vote data by rcid
if (length(Select_session()$rcid)!=0){
df <- subset(df, df$rcid %in% unique(Select_session()$rcid))
}
# Vote title selector
if (!is.null(input$voteTitle)){
df <- subset(df, df$unres_title==input$voteTitle)
}
return(df)
})
# Voting table output
output$votingTable <- renderDataTable({
df <- vote
# Select voting data based on rcid
df <- subset(df, df$rcid %in% unique(Select_voting()$rcid))
vars <- c("unres", "Country", "CABB", "Vote")
df <- df[,vars]
},
options = list(pageLength = 10)
)
# Session data downloader
output$downloadSession <- downloadHandler(
filename = "UN_Session.csv",
content = function(file){
vars <- c("session","unres_title", "date", "yes", "no", "abstain")
df <- Select_session()[,vars]
write.csv(df, file)
}
)
# Voting data downloader
output$downloadVoting <- downloadHandler(
filename = "UN_Voting.csv",
content = function(file){
vars <- c("unres", "Country", "CABB", "Vote")
df <- Select_voting()[,vars]
write.csv(df, file)
}
)
# Map visualization
output$map <- renderPlot({
if (length(unique(Select_voting()$rcid))==1){
# Load map color
color_for_map <- subset(colormatrix,
colormatrix$breaksvalue %in% unique(Select_voting()$vote))
# Main function to visualize map
ggplot()+
# Base map, fill=white, line=gray
geom_map(data=World.points,
map = World.points,
aes(map_id=region),
fill="#ecf0f1", color="white")+
# Vote map, fill based on vote, line = white
geom_map(data=Select_voting(),
map = World.points,
aes(map_id=region,
fill = as.character(vote)),
color="white")+
# Expand axis
expand_limits(x = world_map$long, y = world_map$lat)+
# Change fill color based on color matrix file
scale_fill_manual(
values=color_for_map$colors,
name="Vote",
breaks=color_for_map$breaksvalue,
labels=color_for_map$breakslabel)+
# Stephen Few plot theme, require(ggthemes)
theme_few()+
# No axis line, axis text, axis title
theme(axis.line=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
#legend.position="none",
panel.background=element_blank(),
panel.border=element_blank(),
panel.grid.major=element_blank(),
panel.grid.minor=element_blank(),
plot.background=element_blank())
}
# Map without vote title selection
else if (length(input$voteTitle)==0){
ggplot()+
geom_map(data=World.points,
map = World.points,
aes(map_id=region),
fill="#ecf0f1",
color="white")+
expand_limits(x = world_map$long, y = world_map$lat)+
theme_few()+
geom_text(aes(x = mean(world_map$long),
y = mean(world_map$lat)),
label="Select the vote title to view map",
size = 10, colour = "#3498db")+
theme(axis.line=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.position="none",
panel.background=element_blank(),
panel.border=element_blank(),
panel.grid.major=element_blank(),
panel.grid.minor=element_blank(),
plot.background=element_blank())
}
# Map without voting data
else{
ggplot() +
geom_map(data=World.points, map = World.points, aes(map_id=region), fill="#ecf0f1", color="white") +
expand_limits(x = world_map$long, y = world_map$lat)+
theme_few()+
geom_text(aes(x = mean(world_map$long), y = mean(world_map$lat)), label="VOTING DATA UNAVAILABLE", size = 10, colour = "#3498db")+
theme(axis.line=element_blank(),axis.text.x=element_blank(),
axis.text.y=element_blank(),axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),legend.position="none",
panel.background=element_blank(),panel.border=element_blank(),panel.grid.major=element_blank(),
panel.grid.minor=element_blank(),plot.background=element_blank())
}
}, height = 400, width = 800)
# Title selection
output$TitleSelectUI <- renderUI({
selectizeInput("voteTitle", "Select the vote title",
choices = Select_session()$unres_title,
multiple = T,
options = list(maxItems = 1))
})
# Official document link
official_doc_link <- reactive({
df <- Select_session()
if (!is.null(input$voteTitle)){
df <- subset(df, df$unres_title==input$voteTitle)
if (df$session>30){
# UN official document URL
url_string <- paste("http://www.un.org/en/ga/search/view_doc.asp?symbol=%20A/RES/",
gsub("\\D", "",strsplit(df$unres[1], split = "/")[[1]][2]),
"/",
gsub("\\D", "",strsplit(df$unres[1], split = "/")[[1]][3]),
sep = "")
}
else {url_string = NULL}
}
else {
url_string = NULL
}
return(url_string)
})
# Official document hyperlink
output$OfficialDoc <- renderUI({
h5(a("Click to view official document at un.org", href = official_doc_link(), target="_blank"))
})
})
This is my first shiny app. I would like for the user to be able to update the number of facet columns and the dimensions of downloaded plot. readNWISuv, the function to download data can take a long time if multiple years are queried. Currently, the app downloads the data each time the user wants to change the plot format or plot dimensions. Not sure if I need to use reactiveValues, but I would assume that I want the data to be downloaded and manipulated outside of renderPlot. Thanks!
library(shiny)
library(dataRetrieval)
library(lubridate)
library(tidyverse)
library(plotly)
#flow wrecker
ui <- pageWithSidebar( #fluidPage(
# Application title
titlePanel("Flow Record"),
# Sidebar with a date input
#sidebarLayout
sidebarPanel(
dateRangeInput("daterange", "Date range: (yyyy-mm-dd)",
start = Sys.Date()-10,
min = "1980-10-01"),
textInput("gage", "USGS Gage #", "11532500"),
#actionButton("dload","Download data"),
selectInput("facet_x", "Facet Column #:", 2, choices =1:4),
submitButton("Update View", icon("refresh")),
helpText("When you click the button above, you should see",
"the output below update to reflect the values you",
"entered above:"),
#verbatimTextOutput("value"),
downloadButton('downloadImage', 'Download figure'),
numericInput("fig_x", "Fig. Dim. x:", 10, min = 3, max = 16),
numericInput("fig_y", "Fig. Dim. y:", 10, min = 3, max = 16),
width = 3
),
# Show a plot of the generated WY
mainPanel(
plotlyOutput("WYfacet")
)
)
# Define server draw WY facets
server <- function(input, output) {
parameterCd <- "00060" # discharge
#water year
wtr_yr <- function(dates, start_month=10) {
# Convert dates into POSIXlt
dates.posix = as.POSIXlt(dates)
# Year offset
offset = ifelse(dates.posix$mon >= start_month - 1, 1, 0)
# Water year
adj.year = dates.posix$year + 1900 + offset
# Return the water year
adj.year
}
output$WYfacet <- renderPlotly({
#progress bar
withProgress(readNWISuv(input$gage, parameterCd, input$daterange[1], input$daterange[2],tz="America/Los_Angeles") %>% addWaterYear,
message = 'Download in progress',
detail = 'This may take a while...', value = 1)
#download
temperatureAndFlow <- readNWISuv(input$gage, parameterCd, input$daterange[1], input$daterange[2],tz="America/Los_Angeles") %>% addWaterYear
names(temperatureAndFlow)<-c("agc","site","date","WY", "flow","a","tzone")
temperatureAndFlow$commonDate <- as.Date(format(temperatureAndFlow$date, format="2000-%m-%d"))
tf.df<-temperatureAndFlow %>%
filter(WY<=max(WY) & WY>=if_else(month(min(date))<10,min(WY)+1,min(WY)))
tf.df$date.d<-format(tf.df$date, format="%Y-%m-%d")
#mutate commonDate
df4 <- tf.df %>%
mutate(WY=factor(wtr_yr(date.d))) %>%
#seq along dates starting with the beginning of your water year
mutate(commonDate=as.Date(paste0(ifelse(month(date.d) < 10, "2001", "2000"),
"-", month(date.d), "-", day(date.d))), Date=date.d)
#plot
ploty<-ggplot(data = df4,mapping = aes(x = commonDate, y = flow,label=Date, colour = factor(WY))) +
geom_line() +
labs(x = " ", y = "Discharge (cfs)") +
facet_wrap(facets = ~WY,ncol=as.integer(input$facet_x)) +
scale_y_log_eng()+
annotation_logticks(sides = "l")+
theme_bw()+
theme(panel.grid.minor.x = element_blank())+
scale_x_date(labels = function(x) format(x, "%b"),date_breaks = "1 months")+
guides(colour=FALSE)
ggplotly(ploty, tooltip=c("flow","Date"))
})
#fig dimensions
output$fig_x <- renderText({ input$fig_x })
output$fig_y <- renderText({ input$fig_y })
#facet columns
output$facet_x <- renderText({ input$facet_x })
#download to computer
output$downloadImage <- downloadHandler(
filename = function(){paste("plot",'.png',sep='')},
content = function(file){
ggsave(file,width = input$fig_x,height = input$fig_y, dpi = 600, units = "in", device='png')
print(ggplot(data = df4,mapping = aes(x = commonDate, y = flow, colour = factor(WY))) +
geom_line() +
#geom_point()+
#geom_vline(data = trip,aes(xintercept=commonDate),trip_df,color="black")+
labs(x = " ", y = "Discharge (cfs)") +
facet_wrap(facets = ~WY,ncol=as.integer(input$facet_x)) +
scale_y_log_eng()+
annotation_logticks(sides = "l")+
theme_bw()+
theme(panel.grid.minor.x = element_blank())+
scale_x_date(labels = function(x) format(x, "%b"),date_breaks = "1 months")+
guides(colour=FALSE))
})
}
# Run the application
shinyApp(ui = ui, server = server)
There are a few changes to make to your sever section to make this work. Primarily:
splitting the creation of the dataframe into a new eventReactive function, dependent on an actionButton.
referring to the function inside the renderPlotly call
Try this:
## Within ui function call ############################################
# submitButton("Update View", icon("refresh")), # line to replace
actionButton(inputId = "update", "Update View", icon("refresh")),
## (if you want to keep a button to control when data is downloaded ##
server <- function(input, output) {
parameterCd <- "00060" # discharge
#water year
wtr_yr <- function(dates, start_month=10) {
# Convert dates into POSIXlt
dates.posix = as.POSIXlt(dates)
# Year offset
offset = ifelse(dates.posix$mon >= start_month - 1, 1, 0)
# Water year
adj.year = dates.posix$year + 1900 + offset
# Return the water year
adj.year
}
# New part here - use `reactive` to make df4 a new thing, which is processed separately. The `eventReactive` function waits till it sees the button pressed.
df4 <- eventReactive(input$update, ignoreNULL = FALSE, {
#progress bar
withProgress(readNWISuv(input$gage, parameterCd, input$daterange[1], input$daterange[2],tz="America/Los_Angeles") %>% addWaterYear,
message = 'Download in progress',
detail = 'This may take a while...', value = 1)
#download
temperatureAndFlow <- readNWISuv(input$gage, parameterCd, input$daterange[1], input$daterange[2],tz="America/Los_Angeles") %>% addWaterYear
names(temperatureAndFlow)<-c("agc","site","date","WY", "flow","a","tzone")
temperatureAndFlow$commonDate <- as.Date(format(temperatureAndFlow$date, format="2000-%m-%d"))
tf.df<-temperatureAndFlow %>%
filter(WY<=max(WY) & WY>=if_else(month(min(date))<10,min(WY)+1,min(WY)))
tf.df$date.d<-format(tf.df$date, format="%Y-%m-%d")
#mutate commonDate
tf.df %>%
mutate(WY=factor(wtr_yr(date.d))) %>%
#seq along dates starting with the beginning of your water year
mutate(commonDate=as.Date(paste0(ifelse(month(date.d) < 10, "2001", "2000"),
"-", month(date.d), "-", day(date.d))), Date=date.d)
})
output$WYfacet <- renderPlotly({
# req will pause plot loading till new data downloaded above, but changes to display will render without new download
req(df4())
#plot
ploty<-ggplot(data = df4(), # Put brackets here to refer to df4 as a reactive input!!!
mapping = aes(x = commonDate, y = flow, label=Date, colour = factor(WY))) +
geom_line() +
labs(x = " ", y = "Discharge (cfs)") +
facet_wrap(facets = ~WY,ncol=as.integer(input$facet_x)) +
scale_y_log10()+
# annotation_logticks(sides = "l")+
theme_bw()+
theme(panel.grid.minor.x = element_blank())+
scale_x_date(labels = function(x) format(x, "%b"),date_breaks = "1 months")+
guides(colour=FALSE)
ggplotly(ploty, tooltip=c("flow","Date"))
})
#fig dimensions
output$fig_x <- renderText({ input$fig_x })
output$fig_y <- renderText({ input$fig_y })
#facet columns
output$facet_x <- renderText({ input$facet_x })
#download to computer
output$downloadImage <- downloadHandler(
filename = function(){paste("plot",'.png',sep='')},
content = function(file){
ggsave(file,width = input$fig_x,height = input$fig_y, dpi = 600, units = "in", device='png')
print(ggplot(data = df4() ,mapping = aes(x = commonDate, y = flow, colour = factor(WY))) +
geom_line() +
#geom_point()+
#geom_vline(data = trip,aes(xintercept=commonDate),trip_df,color="black")+
labs(x = " ", y = "Discharge (cfs)") +
facet_wrap(facets = ~WY,ncol=as.integer(input$facet_x)) +
scale_y_log10()+
annotation_logticks(sides = "l")+
theme_bw()+
theme(panel.grid.minor.x = element_blank())+
scale_x_date(labels = function(x) format(x, "%b"),date_breaks = "1 months")+
guides(colour=FALSE))
})
}
I am trying to build a shiny app where the sidebar is dynamic based on the tab that is selected. The sidebar is populated by a csv file. Right now it is just reading a CSV file named machines.csv. I would want that to be able to read for example austin.csv, dallas.cav based on the tab name. There will be 7 tabs total. Also I am having trouble with the plot area. I want the plot to render to the correct tab (which is always the selected tab).
The code I have is here. The app is at http://45.55.208.171:3838/
Only the first two machines have data right now. And the Dallas tab I can not get to work because it seems I can't use the same render plot ID. Not sure how to make that dynamic based on the tab as well.
library(shiny)
library(ggplot2)
library(scales)
library(grid)
library(RColorBrewer)
library(lubridate)
library(ggrepel)
library(plyr)
library(dplyr)
library(DT)
library(RCurl)
library(readr)
library(stringr)
Machine <-read.csv("machines.csv")
Sys.setenv(TZ="US/Central")
SDate <- Sys.Date()
ui <- fluidPage(
titlePanel("Printer Utilization"),
sidebarLayout(
sidebarPanel(width = 2,
radioButtons("typeInput", "Machine", t(Machine[1]) , width = 4),
dateInput("RepDate", "Date of Report",format = "mm-dd-yyyy",value = "08-03-2016"),
downloadButton("downloadplot", "Download")),
mainPanel(
tabsetPanel(id = "plants",
tabPanel("Austin",value = "Austin", plotOutput("plants",width = "120%",height = "600px")),
tabPanel("Dallas",value = "Dallas", plotOutput("Dallas",width = "120%",height = "600px")),
tabPanel("Table", div(DT::dataTableOutput("log"), style = "font-size:50%")))
)))
server <- function(input, output) {
output$plants <-renderPlot({
Sys.setenv(TZ="US/Central")
SDate <- Sys.Date()
SDate <-as.POSIXct(SDate,format="%Y%m%d")+18000
RepDate.1 <- reactive({ as.POSIXct(input$RepDate,format="%Y%m%d", tz="US/Central")}+18000)
typeInput.1 <- reactive({input$typeInput})
RDate <- RepDate.1()
Machine.1<-reactive({subset(Machine,MNames.i==typeInput.1())})
Serial = Machine.1()$Serial.i
IP = Machine.1()$IP.i
Type = Machine.1()$Type.i
if (Type=="b"){
if (SDate==RepDate.1())
{
extension <- ".ACL"
logdata <- (read.csv(paste(Serial, as.character(RDate,format="%Y%m%d"), extension, sep = "") , sep = ';'))
RDate <- RDate-86400
extension <- ".CSV"
logdata <- (rbind(read.csv(paste(Serial, as.character(RDate,format="%Y%m%d"), extension, sep = "") , sep = ';'),logdata))
}
if (SDate!=RepDate.1())
{
extension <- ".CSV"
try(logdata <- (read.csv(paste(Serial, as.character(RDate,format="%Y%m%d"), extension, sep = "") , sep = ';')))
RDate <-RDate-86400
logdata <- (rbind(read.csv(paste(Serial, as.character(RDate,format="%Y%m%d"), extension, sep = "") , sep = ';'),logdata))
RDate <-RDate+172800
if (RDate==SDate)
{extension <- ".ACL"}
try(logdata <- (rbind(read.csv(paste(Serial, as.character(RDate,format="%Y%m%d"), extension, sep = "") , sep = ';'),logdata)))
}
logdata <- subset(logdata, (startdate == as.character(input$RepDate,format="%Y-%m-%d")) | (readydate == as.character(input$RepDate,format="%Y-%m-%d")))
logdata$jobname <- sub(":.*", "", logdata$jobname)
logdata$starttime.ct <- as.POSIXct(paste(logdata$startdate, logdata$starttime, sep = " ", format = "%Y%m%d %H:%M:%S", tz="US/Central"))
logdata$starttime.ct <- force_tz(logdata$starttime.ct,tzone="US/Central")
logdata$readytime.ct <- as.POSIXct(paste(logdata$readydate, logdata$readytime, sep = " ", format = "%Y%m%d %H:%M:%S", tz="US/Central"))
logdata$readytime.ct <- force_tz(logdata$readytime.ct,tzone="US/Central")
logdata$idletime.ct <- as.POSIXct(logdata$idletime, format = "%H:%M:%S")
logdata$idletime.hour <-as.POSIXlt(logdata$idletime.ct)$hour + as.POSIXlt(logdata$idletime.ct)$min/60 + as.POSIXlt(logdata$idletime.ct)$sec/3600
logdata$activetime.ct <- as.POSIXct(logdata$activetime, format = "%H:%M:%S")
logdata$activetime.hour <-as.POSIXlt(logdata$activetime.ct)$hour + as.POSIXlt(logdata$activetime.ct)$min/60 + as.POSIXlt(logdata$activetime.ct)$sec/3600
Sreadytime <- (strptime(logdata$readytime.ct,format="%Y-%m-%d %H:%M:%S"))
Sstarttime <- (strptime(logdata$starttime.ct,format="%Y-%m-%d %H:%M:%S"))
Rtime <- (Sreadytime-Sstarttime)/3600
Idletime <- (strptime(logdata$idletime.ct,format="%Y-%m-%d %H:%M:%S"))
Utilization <- sum(logdata$activetime.hour/24)
Utilization <- paste(round(Utilization*100,digits=1),"%",sep="")
output <- format(sum(logdata$nofprinteda4bw)+sum(logdata$nofprinteda3bw*2), big.mark=",")
ymax.r = (logdata$idletime.hour/(logdata$idletime.hour+logdata$activetime.hour))
logdata$jobname <- strtrim(logdata$jobname, 18)
}
if (Type=="c"){
url <- paste("http://",IP,"/xjutil/log.csv", sep="")
dat <- readLines(url)
dat <- dat[-1]
dat <- dat[-1]
varnames <- unlist(strsplit(dat[1], ","))
nvar <- length(varnames)
varnames<-make.names(varnames, unique=TRUE)
k <- 1
dat1 <- matrix(NA, ncol = nvar, dimnames = list(NULL, varnames))
while(k <= length(dat)){
k <- k + 1
#if(dat[k] == "") {k <- k + 1
#print(paste("data line", k, "is an empty string"))
if(k > length(dat)) {break}
#}
temp <- dat[k]
# checks if there are enough commas or if the line was broken
while(length(gregexpr(",", temp)[[1]]) < nvar-1){
k <- k + 1
temp <- paste0(temp, dat[k])
}
temp <- unlist(strsplit(temp, ","))
message(k)
dat1 <- rbind(dat1, temp)
}
dat1 = dat1[-1,]
logdata<-as.data.frame(dat1)
logdata$starttime.ct <-strptime(logdata$timestamp.printing,format="%Y %m %d %H %M %S", tz="US/Central")
logdata$readytime.ct <-strptime(logdata$timestamp.done.printing,format="%Y %m %d %H %M %S", tz="US/Central")
logdata$date.timestamp.printing <- as.character(substr(logdata$timestamp.printing, 1, 10))
logdata$date.timestamp.done.printing <- as.character(substr(logdata$timestamp.done.printing, 1, 10))
logdata <- subset(logdata, (date.timestamp.printing == as.character(RepDate.1(), format = "%Y %m %d")) | (date.timestamp.done.printing == as.character(RepDate.1(), format = "%Y %m %d")))
logdata$title <- sub(":.*", "", logdata$title)
logdata$activetime <- logdata$readytime.ct - logdata$starttime.ct
Utilization <- sum(logdata$activetime/86400)
Utilization <- paste(round(Utilization*100,digits=1),"%",sep="")
output<-format(sum(as.numeric(logdata$total.pages.printed)),big.mark = ",")
output<-""
ymax.r = 0
logdata$jobname <- logdata$title
logdata$jobname <- strtrim(logdata$jobname, 18)
}
if (Type=="a"){
url <- paste("http://",IP,"/logs/","?C=M;O=D", sep="")
html <- paste(readLines(url), collapse="\n")
matched <- str_match_all(html, "<a href=\"(1100.*?)\"")
links <- matched[[1]][, 2]
print(links)
for (i in links[1:15])
{
url <- paste("http://",IP,"/logs/", sep="")
url.a <- paste(url,as.character(i) ,sep = "")
print(url.a)
if (exists("logdata")){
logdata <- rbind(read.csv(url.a, header=TRUE, fill = TRUE, sep = ","), logdata)
}
else{
logdata <- read.csv(url.a, header=TRUE, fill = TRUE, sep = ",")
print(url.a)
}
}
logdata$size <- logdata$SqFt
logdata <- logdata %>% distinct(Start.time, .keep_all = TRUE)
logdata$Start.time <- strptime(logdata$Start.time, format="%a %b %d %H:%M:%S %Y")
logdata$Total.time <- as.POSIXlt(logdata$Total.time, format = "%H:%M:%S")
logdata$Total.time <- as.POSIXlt(logdata$Total.time)$hour + as.POSIXlt(logdata$Total.time)$min/60 + as.POSIXlt(logdata$Total.time)$sec/3600
logdata$readytime.ct <- as.POSIXct(logdata$Start.time)+(logdata$Total.time * 3600)
logdata$starttime.ct <- as.POSIXct(logdata$Start.time)
logdata$starttime <- strptime(logdata$starttime.ct,format="%Y-%m-%d")
logdata$End.time <- as.POSIXct(logdata$Start.time)+(logdata$Total.time * 3600)
logdata <- subset(logdata, as.character(starttime,format="%Y-%m-%d") == as.character(RepDate.1(),format="%Y-%m-%d") | (strptime(End.time,format="%Y-%m-%d") == as.character(RepDate.1(),format="%Y-%m-%d")))
Utilization <- (sum(logdata$Total.time))/60
Utilization <- paste(round(Utilization*100,digits=1),"%",sep="")
output<-0
#ymax.r = logdata$SqFt.hr/300
ymax.r = 0
logdata$jobname <- logdata$File.name
}
p<-ggplot(logdata, aes(xmin = starttime.ct, xmax = readytime.ct, ymin = 0, ymax = 1-ymax.r, fill = factor(jobname))) + geom_rect(alpha = .9) +
labs(title=paste(typeInput.1(),RepDate.1(), Utilization, output,sep=" "),x="Time of day",y="Run Time") + theme(legend.position="bottom", legend.title = element_blank(), legend.title = element_text(size=10),legend.title=element_blank()) + guides(fill=guide_legend(nrow=5)) +
scale_x_datetime(labels = date_format("%H:%M", tz="US/Central"),breaks = date_breaks("1 hour"),expand=c(0,0)) +
coord_cartesian(xlim = as.POSIXct(c(RepDate.1()+86400,RepDate.1()),format="%Y%m%d %H:%M:%S", tz="US/Central")) +
scale_y_continuous(labels=percent,expand=c(0,0),limits=c(0,1))
print(p)
file<-ggsave("myplot.pdf",device = "pdf",plot = p,width=16, height=10,paper="special")
})
output$downloadplot <- downloadHandler(
filename="myplot.pdf", # desired file name on client
content=function(con) {
file.copy("myplot.pdf", con)
}
)
outputOptions(output, "downloadplot", suspendWhenHidden=FALSE)
}
shinyApp(ui = ui, server = server)
How about this?
Here, I hardcoded choice_set variable, but I suppose you can define it using external data file.
Keys.
Keep your data in reactiveValues, so it can be referred to from server operations.
Use observeEvent(input$tabset, ...) to trigger server operation only when the tabset value has been changed.
Use updateRadioButtons to change the properties of the input components.
R
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(radioButtons("radio", "radio", c("A", "B"))),
mainPanel(
tabsetPanel(id = "tabset",
tabPanel("alphabet", value = "alpha"),
tabPanel("number", value = "number"))
)))
server <- function(input, output, session)
{
RV <- reactiveValues(
choise_set = list(
alpha = c("A", "B"),
number = c("1", "2", "3")
)
)
observeEvent(input$tabset, {
updateRadioButtons(session, "radio",
choices = RV$choise_set[[input$tabset]])
})
}
runApp(list(ui = ui, server = server))
I want the values of the height or weight to show when I hover over a point in the graph. I already tried to make this work by using the plotly package and the example of this link. But I got all kind of errors and I do not know how to make it work.
I've included my whole code so I hope someone can help me with this problem.
library("shiny")
library("ggplot2")
library('readxl')
library('gridExtra')
ui<- fluidPage(
titlePanel("Animals"),
sidebarLayout(
sidebarPanel(
helpText("Create graph of height and/or weight animals"),
selectInput("location",
label = "Choose a location",
choices = list("New York"="New York", "Philadelphia" = "Philadelphia"),
selected = "New York"),
uiOutput("animal"),
checkboxGroupInput("opti",
label = "Option",
choices = c("weight", "height"),
selected = "weight")
),
mainPanel(plotOutput("graph"))
))
server <- function(input, output){
animal <- read_excel('data/animals.xlsx', sheet =1)
var <- reactive({
switch(input$location,
"New York" = list("Cat1", "Dog2"),
"Philadelphia"= list("Cat4","Dog3"))
})
output$animal <- renderUI({
checkboxGroupInput("anim", "Choose an animal",
var())
})
output$graph <- renderPlot({
if (length(input$anim)==1){
p <- ggplot(subset(animal, Name %in% input$anim & Location %in% input$location), aes(x=date))
if ("weight" %in% input$opti){
p <- p + geom_line(aes(y=weight)) + geom_point(aes(y=weight))
}
if ("height" %in% input$opti){
p <- p + geom_line(aes(y=height)) + geom_point(aes(y=height))
}
print(p)
}
if (length(input$anim)==2){
p1 <- ggplot(subset(animal, Name %in% input$anim[1] & Location %in% input$location), aes(x=date))
p2 <- ggplot(subset(animal, Name %in% input$anim[2] & Location %in% input$location), aes(x=date))
if ("weight" %in% input$opti){
p1 <- p1 + geom_line(aes(y=weight)) + geom_point(aes(y=weight))
p2 <- p2 + geom_line(aes(y=weight)) + geom_point(aes(y=weight))
}
if ("height" %in% input$opti){
p1 <- p1 + geom_line(aes(y=height)) + geom_point(aes(y=height))
p2 <- p2 + geom_line(aes(y=height)) + geom_point(aes(y=height))
}
grid.arrange(p1,p2, ncol = 2)
}
})
}
shinyApp(ui=ui, server= server)
A part of the data:
Location Name date weight height
New York Cat1 Mar-16 34,20 22,50
New York Cat1 Apr-16 35,02 23,02
New York Cat1 May-16 35,86 23,55
New York Cat1 Jun-16 36,72 24,09
New York Dog2 Mar-16 33,55 22,96
New York Dog2 Apr-16 33,62 23,42
New York Dog2 May-16 33,68 23,89
New York Dog2 Jun-16 33,75 24,37
Philadelphia Cat4 Mar-16 20,33 16,87
I used this tooltip and customised it a little bit.
Your plots initially don't show up because you don't return any plot. I return an ggplot object p without calling print function.
In general, I heavily modified your code and this is the result:
As the function nearPoints needs the same dataset that you pass to ggplot, I had to create a new reactive, in which I did some subsetting and reshaping of your data.
Instead of grid.arrange to create two seperate plots I used facet_grid (and hence I had to transform the data). I also used colours to differentiate lines.
Everything works fine with the example data you provided.
Full example:
rm(ui)
rm(server)
library("shiny")
library("ggplot2")
library('readxl')
library('gridExtra')
library(reshape) # for "melt"
ui<- fluidPage(
titlePanel("Animals"),
sidebarLayout(
sidebarPanel(
helpText("Create graph of height and/or weight animals"),
selectInput("location",
label = "Choose a location",
choices = list("New York"="New York", "Philadelphia" = "Philadelphia"),
selected = "New York"),
uiOutput("animal"),
checkboxGroupInput("opti",
label = "Option",
choices = c("weight", "height"),
selected = "weight")
),
mainPanel(
# this is an extra div used ONLY to create positioned ancestor for tooltip
# we don't change its position
div(
style = "position:relative",
plotOutput("graph",
hover = hoverOpts("plot_hover", delay = 100, delayType = "debounce")),
uiOutput("hover_info")
)
)
))
server <- function(input, output){
animal <- read_excel('data/animals.xlsx', sheet =1)
#animal <- read_excel("~/Downloads/test2.xlsx")
var <- reactive({
switch(input$location,
"New York" = c("Cat1", "Dog2"),
"Philadelphia"= c("Cat4","Dog3"))
})
output$animal <- renderUI({
checkboxGroupInput("anim", "Choose an animal",
var())
})
output$graph <- renderPlot({
req(input$anim, sub())
if (length(input$anim) == 1) {
p <- ggplot(sub(), aes(x = date, colour = variable))
p <- p + geom_line(aes(y = value)) +
geom_point(aes(y = value)) +
guides(colour = guide_legend(title = NULL))
return(p) # you have to return the plot
}
if (length(input$anim) == 2) {
p <- ggplot(sub(), aes(x = date, colour = variable)) +
geom_line(aes(y = value)) +
geom_point(aes(y = value)) +
facet_grid(~ Name) +
guides(colour = guide_legend(title = NULL))
return(p) # you have to return the plot
}
})
observe({
print(sub())
})
sub <- reactive({
req(input$anim)
if (length(input$anim) == 1) {
df <- animal[animal$Name %in% input$anim & animal$Location %in% input$location, ]
df <- melt(as.data.frame(df), measure.vars = c("weight", "height"))
df <- subset(df, df$variable %in% input$opti)
return(df)
}
if (length(input$anim) == 2) {
df <- animal[animal$Name %in% input$anim & animal$Location %in% input$location, ]
df$Name <- factor(df$Name)
df <- melt(as.data.frame(df), measure.vars = c("weight", "height"))
df <- subset(df, df$variable %in% input$opti)
return(df)
}
})
output$hover_info <- renderUI({
hover <- input$plot_hover
point <- nearPoints(sub(), hover, threshold = 5, maxpoints = 1, addDist = TRUE)
if (nrow(point) == 0) return(NULL)
left_pct <- (hover$x - hover$domain$left) / (hover$domain$right - hover$domain$left)
top_pct <- (hover$domain$top - hover$y) / (hover$domain$top - hover$domain$bottom)
left_px <- hover$range$left + left_pct * (hover$range$right - hover$range$left)
top_px <- hover$range$top + top_pct * (hover$range$bottom - hover$range$top)
style <- paste0("position:absolute; z-index:100; background-color: rgba(245, 245, 245, 0.85); ",
"left:", left_px + 2, "px; top:", top_px + 2, "px;")
wellPanel(
style = style,
p(HTML(paste0("<b>", point$variable, ": </b>", point$value)))
)
})
}
shinyApp(ui = ui, server = server)