I'm having trouble reading in .csv files dynamically based on ui input.
I have observations of precipitation and temperature data for different areas (ID'd here as zone). For a subset of those same areas, I also have frequency data of elevation observations. The app is meant as a CDF plotter of these three measurements for a region with many areas.
The problem is the elevation frequency data is much higher resolution than the precipitation and temperature data (together as p_t) and my code is too inefficient for good Shiny performance whenever users select elevation data (elev).
Instead of making one globally available filterable data.frame of all the data, I'm hoping to have Shiny go and grab individual .csvs of the frequency elevation data on the fly for just the area(s) that are selected in input$zone (via SelectizeInput). Once they're read in, uncount() so they're in observation format, then bundle together into one data.frame data() with any precipitation or temperature (observation) data that's selected, for ggplot's handy stat_ecdf() function with aesthetics set to data()$param and data()$zone.
Does this sound like an OK approach? If so, could you help me? In summary the main need is, if the parameter elev is selected, have Shiny find and read in those elevation .csvs whose file names match the areas selected. Thanks in advance for any help.
library(tidyverse)
library(shiny)
library(shinydashboard)
# generate sample observation data - precipitation and temperature
# not many megabytes so will just read in all at once at startup as one .csv
zone <- c(rep("abcde", 6), rep("fghij", 6), rep("klmno", 6),
rep("pqrst", 6))
set.seed(1)
val <- rnorm(24, 12, 18)
param <- rep(c("p", "t"), 12)
p_t <- data.frame(zone, val, param, stringsAsFactors = FALSE)
#head(p_t)
# zone val param
#1 abcde 0.7238314 p
#2 abcde 15.3055798 t
#3 abcde -3.0413150 p
#4 abcde 40.7150544 t
#5 abcde 17.9311399 p
#6 abcde -2.7684309 t
# generate sample elevation frequency data with many more observations -
# need to be stored as individual .csvs, too big to read in, uncount, and
# filter by input - too slow
# just want to bind and uncount as they're selected by user
setwd(./elevdata) # separate folder from which to pick out the elev data .csv in the main wd
# and prevent reading in of other app .csv data
val <- c(503, 506, 513, 689)
count <- c(32282, 53172, 45237, 34534)
data.frame(val, count) %>% mutate(zone = "abcde", param = "elev") %>%
write_csv("abcde.csv")
val <- c(-36, -39, -51, -98)
count <- c(52220, 5175, 299237, 100034)
data.frame(val, count) %>% mutate(zone = "fghij", param = "elev") %>%
write_csv("fghij.csv")
val <- c(2, 7, 13, 110)
count <- c(99222, 883172, 114237, 8347633)
data.frame(val, count) %>% mutate(zone = "klmno", param = "elev") %>%
write_csv("klmno.csv")
#only a subset of p_t zones have elev data - variable not currently used
#zoneswithelevdata <- list.files(pattern = "*.csv$")
#zoneswithelevdata <- gsub(".csv", "", zoneswithelevdata)
#shiny app using the above sample data
shinyApp(
ui = fluidPage(
sidebarLayout(sidebarPanel(
selectizeInput(
"zone", "zone", choices = unique(p_t$zone),
selected = c("a"),
multiple = TRUE),
checkboxGroupInput("param", "parameter",
choices = c("elev", "p", "t"), selected =c("elev", "p"))
),
mainPanel(
tabsetPanel(position=c("right"),
tabPanel(strong("static cdf"),
br(),
plotOutput("reg_plot", height = "750px")) )))
),
server = function(input, output) {
# elev_csv_counts_tobind <- reactive({
# if `elev` parameter is checked:
#* read_csv() of the csv(s) with csv file name %in% input$zone **
# * and bind together
#})
data <- reactive({
p_t_e <- p_t %>%
#first subset p_t by the zone(s) and param(s) selected
filter(param %in% input$param,
zone %in% input$zone) %>%
#now attach and uncount the elevation data
bind_rows({elev_csv_counts_tobind %>%
uncount(count)})
})
output$reg_plot <- renderPlot({
ggplot(data(), aes(val, color = param, linetype = zone)) +
labs(y = "proportion of total", x = NULL) +
stat_ecdf(pad = FALSE) + coord_flip()
})
}
)
Related
I am trying to create a dashboard using R Shiny from NYC Tree Census 2015. The dashboard should look something like in the picture here > Dashboard in Shiny Picture
My code is mentioned below:
library(shiny)
library(tidyverse)
library(ggplot2)
my_data <- read.csv("/Users/abhikpaul/Documents/Documents/Github/Fiverr/2015_Street_Tree_Census_-_Tree_Data.csv")
ui <- fluidPage(
titlePanel("The Dashboard of Tree Distribution in New York City"),
sidebarLayout(
sidebarPanel(
# Description ----
helpText("In this page you can get information about the tree distribution, status, health conditions, and species rank in New York City. Please choose the borough that you want to check. It may take 10 seconds for the graphics to load. Thank you for your patience!"),
#Input: Check boxes for Boroughs ----
checkboxGroupInput("checkboxInput",
label = "Borough",
choices = list("Bronx",
"Brooklyn",
"Manhattan",
"Queens",
"Staten Island"),
selected = "Bronx"),
),
# Main panel for displaying outputs ----
mainPanel(
# Tabs panel for displaying outputs ----
tabsetPanel(type = "tabs",
#Output: About ----
tabPanel("About",
h3("About this dataset", align = "left"),
p("The dataset displays the information of trees (including health, status, species, etc.) within the five boroughs in New York City. The dataset is organized by NYC parks & Recreation and partner organizations."),
h3("How to make NYC an urban forest?", align = "left"),
p("As a group, we are concerned about planting tree and green environments. Therefore, we will focus on identifying the locations that require more taking care of trees, the top species that have the most number of trees in each borough, the health conditions of those species, and the distribution of trees in each borough."),
HTML("<p>For more information, visit: <a href='https://data.cityofnewyork.us/Environment/2015-Street-Tree-Census-Tree-Data/uvpi-gqnh'>2015 NYC Tree Census</a></p>")
),
#Output: Status ----
tabPanel("Status", plotOutput(outputId = "statusplot")),
)
)
)
)
)
server <- function(input, output) {
my_data <- as_tibble(my_data)
my_data <- my_data[my_data$borough %in% checkboxInput,]
my_data <- data.frame(table(my_data$borough,my_data$status))
my_data <- my_data[apply(my_data!=0, 1, all),]
my_data <- my_data %>%
group_by(Var1) %>%
mutate(Percent = (Freq/sum(Freq) * 100))
output$statusplot <- renderPlot({
ggplot(my_data, aes(fill = Var2, y = Percent, x = Var1)) +
geom_bar(position = "dodge", stat = "identity")
})
}
shinyApp(ui = ui, server = server)
However, while running the app, I am getting an error as mentioned below
Warning: Error in match: 'match' requires vector arguments 50: %in% 47: server [/Users/abhikpaul/Documents/Documents/GitHub/Fiverr/my_app.R#90]Error in match(x, table, nomatch = 0L) : 'match' requires vector arguments
Can someone help me fix this issue as I am a newbie in R Shiny?
Try this
server <- function(input, output) {
output$statusplot <- renderPlot({
my_data <- as_tibble(my_data)
my_data <- my_data[my_data$borough %in% input$checkboxInput,]
my_data <- data.frame(table(my_data$borough,my_data$status))
my_data <- my_data[apply(my_data!=0, 1, all),]
my_data <- my_data %>%
group_by(Var1) %>%
mutate(Percent = (Freq/sum(Freq) * 100))
ggplot(my_data, aes(fill = Var2, y = Percent, x = Var1)) +
geom_bar(position = "dodge", stat = "identity")
})
}
I assume this is a duplicate question (sorry in advance), but I seem not able to resolve this issue. I created a shiny app, which implements a random forest model (party package) returning results with the caret package and visualizing a conditional interference tree with the ctree function. The app works fine locally. However, when I try the publish it no output gets displayed and I get the error "An error has occurred. Check your logs or contact the app author for clarification." What am I missing here?
The data I use for the model comes from a csv file stored on a dropbox account perhaps this might be the issue? I first used RData and switched to csv file, since this seems to be more often used with shiny.
I followed the steps described on: https://support.rstudio.com/hc/en-us/articles/229848967-Why-does-my-app-work-locally-but-not-on-shinyapps-io-, to see if this resoveld the issue. I restarted R and the computer. This did not work.
There is no code or data stored in the local environment when running locally.
All packages are loaded via library().
I load the data via a relative path df <- read.csv("data/df.csv"). All files are stored in a map called shinyapp as app.R and the df.csv in the file data.
The dataset is relatively large so I cannot display it here, but I created a dummy dataset which can be used to run the app.
library(shiny)
library(party)
library(caret)
#==============================================================================
#Use dummy dataset in stead of original data
#df <- read.csv("data/df.csv")
#df <- df[df$Taxon %in% names(table(df$Taxon))[table(df$Taxon) >= 50],]
#Create dummy dataset
df <- data.frame(Sample = 1:500, Taxon = paste0("spec", rbinom(n = 500, 2, 0.5)), SO4 = rnorm(500, 300, 50), pH = rnorm(500, 7, 1), NO3 = rnorm(500, 10, 3))
ui <- fluidPage(
titlePanel("Random Forest classification"),
sidebarPanel(
selectInput(inputId = "Spec", label = "Select species:", unique(df$Taxon)),
selectInput(inputId = "NAN", label = "Select how to use data:", c("All data (Also NAs)", "Complete data (No NAs)")),
numericInput(inputId = "SO4", label = "Choose value for SO4 (mg/l)", value = median(df$SO4, na.rm = T), min = 0, max = 15000),
numericInput(inputId = "pH", label = "Choose value for pH", value = median(df$pH, na.rm = T), min = 5, max = 10),
numericInput(inputId = "NO3", label = "Choose value for NO3 (mg/l)", value = median(df$NO3, na.rm = T), min = 0, max = 50),
h3("Validation parameters"),
textOutput("Validation"),
h3("Voting percentage"),
textOutput("Votingperc"),
h3("Remarks"),
h5("Note that every time the output of the model is different from
the previous. Samples with the absence of species are more
prevalent. Therefore, Every time the code is run, samples where a species
was present are the same. However, samples with absences are randomly
selected in equall amount and combines this with samples where the.
species was present Further, the random forest model randomly creates
trees by bootstrapping the dataset a 100 times. Each time a different
model is created. This model is a course estimation, since many more
important factors are absent. Validation of the model is performed by
training the model on 75% of the dataset and validating on the other 25%.
The predicting model is based on the total dataset. The Error:
replacement has 1 row, data has 0, occurs when the data for a species
has no measurements if complete data (without NAs) is used.")),
mainPanel(plotOutput("Imp"),
plotOutput("Tree")))
server <- function(input,output){
#Create model dataset
modpred <- reactive({
present <- df[df$Taxon == input$Spec,]
present$Spec <- 1
df1 <- df[!duplicated(df$Sample),]
df1 <- df1[!df1$Sample %in% present$Sample,]
if(input$NAN == "Complete data (No NAs)"){
present <- na.omit(present)
df1 <- na.omit(df1)}
if((nrow(df)-nrow(present)) > nrow(present)){
absent <- df1[sample(1:nrow(df1), nrow(present), replace = F),]}
else{
absent <- df1[sample(1:nrow(df1), nrow(present), replace = T),]}
absent$Spec <- 0
model.data <- rbind(present, absent)
model.data$Spec <- as.factor(model.data$Spec)
#Select 75% as training data
prestrain <- present[sample(1:nrow(present), floor(nrow(absent)*0.75), replace = F),]
abstrain <- absent[sample(1:nrow(absent), floor(nrow(present)*0.75), replace = F),]
train.data <- rbind(prestrain, abstrain)
train.data$Spec <- as.factor(train.data$Spec)
#Select the other 25% as validation data
val.data <- model.data[!rownames(model.data) %in% rownames(train.data),]
#Create nice conditional interference tree on all data
ct <- party::ctree(Spec~SO4+pH+NO3, data = model.data)
#Train and validate model
train.model <- party::cforest(Spec~SO4+pH+NO3, data=train.data, controls = party::cforest_classical(mtry = 1, ntree = 100))
validation.mod <- predict(train.model, newdata = val.data)
conf.mat.val <- table(val.data$Spec, predict(train.model, newdata = val.data))
val.results <- caret::confusionMatrix(conf.mat.val)
sumval <- paste0("AUC=", round(val.results$overall[1],2), " (LCI=", round(val.results$overall[3],2),"; ",
"HCI=", round(val.results$overall[4],2), "), ",
"Cohen's kappa=", round(val.results$overall[2],2), ", ",
"n-validation=", nrow(val.data), ", ", "n-training=", nrow(train.data), ", ", "n-total (model)=", nrow(model.data))
#Extract relative importance parameters
relimp <- as.data.frame(party::varimp(train.model))
relimp <- cbind.data.frame(rownames(relimp), relimp)
colnames(relimp)<-c("Parameter", "Relative importance")
rownames(relimp)<- NULL
relimp[,2] <- relimp$`Relative importance`/sum(relimp$`Relative importance`)*100
relimp <- relimp[order(-relimp$`Relative importance`),]
#Apply model on data input user interface
model <- party::cforest(Spec~SO4+pH+NO3, data=model.data, controls = party::cforest_classical(mtry = 1, ntree = 100))
pred.data <- setNames(data.frame(as.numeric(input$SO4), as.numeric(input$pH), as.numeric(input$NO3)), c("SO4", "pH", "NO3"))
pred <- predict(model, newdata = pred.data, type = "prob")
prob <- paste0("Voting percentage (Probability of presence) = ", round(pred$`1`[2]*100,0),"%", ",",
" Majority vote indicates = ", ifelse(pred$`1`[2] > 0.5, "Present", "Absent"))
combo <- list(Probability = prob, Validation = sumval, Tree = ct, Importance = relimp)})
output$Votingperc <- renderText({
combo <- modpred()
combo$Probability})
output$Validation <- renderText({
combo <- modpred()
combo$Validation})
output$Imp <- renderPlot({
combo <- modpred()
bar <- combo$Imp
barplot(bar$`Relative importance`,
names.arg = bar$Parameter, ylab = "Relative importance (%)")})
output$Tree <- renderPlot({
combo <- modpred()
plot(combo$Tree, inner_panel=node_inner(combo$Tree, pval = FALSE))})
}
shinyApp(ui,server)
Thank you in advance for your help.
This is my first attempt at using Shiny.
I have a simulated patient-level dataset with 4 variables:
group: Categorical, takes on values A, B and C. Represents 3 different treatment types that were used in the study.
week: Numeric variable, takes on values 1, 4, 8.Represents follow-up week.
painscore: Numeric variable, score on scale of 1-10, with 1 indicating no pain, 10 indicating extreme pain.
dependscore: Numeric variable, score on scale of 1-10, with 1 indicating no dependency on pain meds, 10 indicating extreme dependency.
Trying to build a simple app that accepts two inputs: the week, and the variable, and provides two outputs:
A boxplot of distribution of scores for the selected variable for the selected week. The x axis would represent the 3 levels of group (A, B and C).
A summary table the shows the number of observations, median, 25th percentile, 75th percentile and number of missing.
I was able to create the interactive boxplot, but I am unable to create the summary table. I was able to create static versions of this table in RMarkdown using the summaryBy function from doBy, but I am not able to implement it in Shiny. Tried following the advice here and here but I'm missing something.
Here's my code for reproducibility. Excuse the extensive annotations, (I'm a complete beginner) they are more for myself than for anyone else.
#libraries--------------------
library(shiny)
library(tidyverse)
library(knitr)
library(doBy)
#----------------------------
#input data
set.seed(123)
mydf <- data.frame( group = rep(rep(c("A","B","C"), each = 3), times = 3),
week = rep(rep(c(1,4,8), each = 9)),
painscore = sample(1:10, 27, replace = TRUE),
dependscore = sample(1:10, 27, replace = TRUE) )
#--------------------------
#define custom function to calculate summary statistics for column of interest.
#function explained in a little more detail when applied in the server function.
fun <- function(x) {
c( n = length(x),
m = median(x),
firstq = round(quantile(x)[2], 1),
lastq = round(quantile(x)[4], 1),
missing = mean(is.na(x)))
}
#-------------------------
#UI
ui <- fluidPage(
titlePanel("Shiny Boxplot and Table"),
#User can provide two different inputs
sidebarLayout(
sidebarPanel(
#1. allow user to pick week using radiobuttons
radioButtons(inputId = "pickedwk",
label = "week you want to display",
choices = c(1,4,8),
selected = 1),
#2. user can pick variable to visualize using dropdownboxes
selectInput(inputId = "var",
label = "variable to visualize",
list("How much pain did you feel today?" = "painscore",
"How dependent are you on medication?" = "dependscore")),
#helpertext
helpText("Enter week/variable choices here")
),
#Spaceholders for output
mainPanel(
plotOutput("boxplot"), #boxplot placeholder
htmlOutput("descriptives") #kable html table placeholder
)
)
)
#-------------------------
#Server
server <- function(input, output) {
#create dataset that subsets down to the week picked by user.
weeksub <- reactive({
mydf %>% filter(week == input$pickedwk[1])
})
#1. use reactive datasubset to render boxplot.
output$boxplot <- renderPlot({
ggplot(weeksub(), aes_string(x = "group", y = input$var)) + #input$var works here
geom_boxplot(fill = "red", outlier.shape = 15, outlier.color = "black") +
scale_y_continuous(name = as.character(input$var)) +
scale_x_discrete(name = "group") +
ggtitle(paste("Distribution of", as.character(input$var), "by treatment group"))
})
#2. use same reactive datasubset to render kable descriptive statistics for the variable picked.
output$descriptives <- renderText({
kable(summaryBy(input$var ~ group, data = as.data.frame(weeksub()), FUN = fun),
#note: here, I'm using the summaryBy function from package doBy. It takes the form var~ categoricalvar
# so, either painscore ~ group, or dependscore ~ group depending on what the user picked, and uses
#my custom function to return a table of count, median, 25th percentile, 75th percentile and missing count for
#the 3 levels of the group variable (for A, B, and C)
col.names = c("Number", "Median", "1Q", "3Q", "Missing"))
})
}#server function ends
# Run the application
shinyApp(ui = ui, server = server)
There are a couple of problems in your code:
The formula notation doesn't know how to deal with input$var. summaryBy supports an alternate syntax that works better. (You could also use as.formula and paste to build a formula.)
You are missing the "Group" column in col.names
You have to generate HTML from kable and pass it as HTML to the UI.
Change your table output to this:
output$descriptives <- renderUI({
HTML(kable(summaryBy(list(input$var, 'group'), data = as.data.frame(weeksub()), FUN = fun),
col.names = c('Group', "Number", "Median", "1Q", "3Q", "Missing"),
format='html'
))
})
I'm having trouble using columns from data originating in wide format as dynamic inputs to a Shiny map app.
In the app I'm hoping to be able to:
select a parameter of point data (sample data below: 16 locations, 6 parameters) in a drop down type menu and adjust the symbol size to represent the selected parameter's absolute values with a slider (to help visualize positive and negative differences from zero)
with any parameter selected, retain ability to see all parameters (the columns) in mapview's popup feature (mapview turns the columns into rows for the popup). It seems a filtered long format data.frame would be missing data from the popup/viewing perspective
retain the (non absolute) original value on the mouseover hover label (eg the -7.3 in the image)
In addition to having those features, I don't know if/where I need to set reactive wrapper(s)? Or, maybe I could do everything more easily with another map-centric library (even though mapview is awesome for many things)?
My attempts are commented out below - the UI works as intended except without drop down selectability - the app is limited to only one working dropdown parameter with mapview(df["param1"] and cex = param1 * input$cex.
Here's the reproducible app.r:
library(tidyverse)
library(sf)
library(shiny)
library(shinydashboard)
library(leaflet)
library(mapview)
## sample earthquake data ##
set.seed(6)
lat <- rnorm(16,-34, 9)
lon <- rnorm(16,-67,.3)
param1 <- rnorm(16, 10, 40) %>% round(1)
param2 <- rnorm(16, 25, 3) %>% round(1)
param3 <- rnorm(16, -18, 10) %>% round(1)
param4 <- rnorm(16, -200, 93) %>% round(1)
param5 <- rnorm(16, 0.1, .09) %>% round(1)
param6 <- rnorm(16, 417, 33) %>% round(1)
df <- data.frame(lat, lon, param1, param2, param3, param4, param5,
param6)
df <- st_as_sf(df, coords = c("lon", "lat"), crs = 4326)
paramchoices <- colnames(df) %>% .[.!="geometry"]
colorpal = mapviewPalette("mapviewSpectralColors")
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
sliderInput("cex", "Symbol Size",
min = 0.000001, max = 10, value = 1, step = 0.000001
),
selectizeInput(
"parameter", "Earthquake Parameter", choices = paramchoices,
selected = c("param1"),
multiple = FALSE)
),
dashboardBody(
tags$style(type = "text/css", "#mapplot {height: calc(100vh - 80px) !important;}"),
leafletOutput("mapplot")
)
)
server <- function(input, output) {
# df <- reactive ({
# df %>% mutate(selectedparameter = input$parameter,
# selectedparameter_abs = abs(selectedparameter))
# })
output$mapplot <- renderLeaflet({
m <- mapview(df["param1"], #mouseover column
#m <- mapview(df["selectedparameter"],
cex = param1 * input$cex, #marker size column
#cex = df$selectedparameter_abs * input$cex,
col.regions = colorpal(100),
alpha.regions = 0.3,
legend = TRUE,
popup = popupTable(df),
layer.name = "selectedparam[unit]")
m#map
}
)}
shinyApp(ui, server)
more info related to the absolute value part - Point color and symbol size based on different variables in mapview
thank you.
I am attempting to use some public information to produce a heat-map of Canada for some labor statistics. Using the spacial files from the census, and data from Statistics Canada (these are large zip files that are not necessary to dig into). Below is a working example that illustrates both the problems I am having with little relative change between regions( though there may be a big absolute change between periods, and the slow draw time.To get this to work, you need to download the .zip file from the census link and unzip the files to a data folder.
library(shiny)
library(maptools)
library(ggplot2)
require(reshape2)
library(tidyr)
library(maptools)
library(ggplot2)
library(RColorBrewer)
ui <- fluidPage(
titlePanel("heatmap"),
# Sidebar with a slider input for year of interest
sidebarLayout(
sidebarPanel(
sliderInput("year",h3("Select year or push play button"),
min = 2000, max = 2002, step = 1, value = 2000,
animate = TRUE)
),
# Output of the map
mainPanel(
plotOutput("unemployment")
)
)
)
server <- function(input, output) {
#to get the spacial data: from file in link above
provinces<-maptools::readShapeSpatial("data/gpr_000a11a_e.shp")
data.p<- ggplot2::fortify(provinces, region = "PRUID")
data.p<-data.p[which(data.p$id<60),]
#dataframe with same structure as statscan csv after processing
unem <- runif(10,min=0,max=100)
unem1 <- unem+runif(1,-10,10)
unem2 <- unem1+runif(1,-10,10)
unemployment <- c(unem,unem1,unem2)
#dataframe with same structure as statscan csv after processing
X <- data.frame("id" = c(10,11,12,13,24,35,46,47,48,59,
10,11,12,13,24,35,46,47,48,59,
10,11,12,13,24,35,46,47,48,59),
"Unemployment" = unemployment,
"year" = c(rep(2000,10),rep(2001,10),rep(2002,10))
)
plot.data<- reactive({
a<- X[which(X$year == input$year),]
return(merge(data.p,a,by = "id"))
})
output$unemployment <- renderPlot({
ggplot(plot.data(),
aes(x = long, y = lat,
group = group , fill =Unemployment)) +
geom_polygon() +
coord_equal()
})
}
# Run the application
shinyApp(ui = ui, server = server)
Any help with either of the issues would be greatly appreciated
For this type of animation it is much faster to use leaflet instead of ggplot as leaflet allows you to only re-render the polygons, not the entire map.
I use two other tricks to speed up the animation:
I join the data outside of the reactive. Within the reactive it is just a simple subset. Note, the join could be done outside of the app and read in as a pre-processed .rds file.
I simplify the polygons with the rmapshaper package to reduce drawing time by leaflet. Again, this could be done outside the app to reduce loading time at the start.
The animation could likely be even more seamless if you use circles (i.e. centroid of each province) instead of polygons. Circle size could vary with Unemployment value.
Note, you need the leaflet, sf, dplyr and rmapshaper packages for this approach.
library(shiny)
library(dplyr)
library(leaflet)
library(sf)
library(rmapshaper)
ui <- fluidPage(
titlePanel("heatmap"),
# Sidebar with a slider input for year of interest
sidebarLayout(
sidebarPanel(
sliderInput("year",h3("Select year or push play button"),
min = 2000, max = 2002, step = 1, value = 2000,
animate = TRUE)
),
# Output of the map
mainPanel(
leafletOutput("unemployment")
)
)
)
server <- function(input, output) {
#to get the spacial data: from file in link above
data.p <- sf::st_read("input/gpr_000a11a_e.shp") %>%
st_transform(4326) %>%
rmapshaper::ms_simplify()
data.p$PRUID <- as.character(data.p$PRUID) %>% as.numeric
data.p <- data.p[which(data.p$PRUID < 60),]
lng.center <- -99
lat.center <- 60
zoom.def <- 3
#dataframe with same structure as statscan csv after processing
unem <- runif(10,min=0,max=100)
unem1 <- unem+runif(1,-10,10)
unem2 <- unem1+runif(1,-10,10)
unemployment <- c(unem,unem1,unem2)
#dataframe with same structure as statscan csv after processing
X <- data.frame("id" = c(10,11,12,13,24,35,46,47,48,59,
10,11,12,13,24,35,46,47,48,59,
10,11,12,13,24,35,46,47,48,59),
"Unemployment" = unemployment,
"year" = c(rep(2000,10),rep(2001,10),rep(2002,10))
)
data <- left_join(data.p, X, by = c("PRUID"= "id"))
output$unemployment <- renderLeaflet({
leaflet(data = data.p) %>%
addProviderTiles("OpenStreetMap.Mapnik", options = providerTileOptions(opacity = 1), group = "Open Street Map") %>%
setView(lng = lng.center, lat = lat.center, zoom = zoom.def) %>%
addPolygons(group = 'base',
fillColor = 'transparent',
color = 'black',
weight = 1.5) %>%
addLegend(pal = pal(), values = X$Unemployment, opacity = 0.7, title = NULL,
position = "topright")
})
get_data <- reactive({
data[which(data$year == input$year),]
})
pal <- reactive({
colorNumeric("viridis", domain = X$Unemployment)
})
observe({
data <- get_data()
leafletProxy('unemployment', data = data) %>%
clearGroup('polygons') %>%
addPolygons(group = 'polygons',
fillColor = ~pal()(Unemployment),
fillOpacity = 0.9,
color = 'black',
weight = 1.5)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I didn't find the drawing time to be unreasonably long at ~2-3 seconds, which for a 2.4mb shapefile seems about right. It takes just as long outside shiny as it does in the app on my machine, anyway.
To hold a constant colour gradient you can specify limits in scale_fill_gradient which will hold the same gradient despite changes to your maps:
output$unemployment <- renderPlot({
ggplot(plot.data(),
aes(x = long, y = lat,
group = group , fill =Unemployment)) +
geom_polygon() +
scale_fill_gradient(limits=c(0,100)) +
coord_equal()
})