Slider with years for barplot - r

I am trying to get a slider within my barplot page to make the data interactive per year.
#library
library(dplyr)
library(shiny)
library(shinythemes)
library(ggplot2)
#Source
dataset <- read.csv("Wagegap.csv")
SFWage <- dataset %>%
group_by(gender,JobTitle, Year) %>%
summarise(averageBasePay = mean(BasePay, na.rm=TRUE)) %>%
select(gender, JobTitle, averageBasePay, Year)
clean <- SFWage %>% filter(gender != "")
#UI
ui <- fluidPage(
theme = shinytheme("united"),
navbarPage("San Fransisco Wages",
tabPanel("Barplot",
mainPanel(
plotOutput("barplot")
)) ,
tabPanel("Table",
mainPanel(
dataTableOutput("table")
))
)
)
#server
server <- function(input, output){
output$barplot <- renderPlot({
ggplot(clean, aes(x = JobTitle, y = averageBasePay ))+
geom_bar(stat="Identity", width = 0.3, fill="orange")+
labs(x= "Jobs", y = "Wage", title = "Wage per job")
})
output$table <- renderDataTable({
clean
})
}
#Run App
shinyApp(ui = ui, server = server)
I don't fully understand it yet how to put this input in.
I have tried sliding it into the navbarpage but I can't figure out how it works.
I also tried making year reactive but with no success.

It's not the year that has to be reactive; it's the whole data frame. Therefore, in your ui, you can do:
[...]
tabPanel("Barplot",
mainPanel(
sliderInput("year", label = "Which year should be displayed?", min = 1900, max = 2020, step = 5, value = 2000) # new
plotOutput("barplot")
)) ,
[...]
I put it there for convenience; the layout is yours. I tried do change as little as possible.
The server would then have:
server <- function(input, output){
# NEW ########################################
clean <- reactive({
SFWage <- dataset %>%
group_by(gender,JobTitle, Year) %>%
summarise(averageBasePay = mean(as.numeric(BasePay), na.rm=TRUE)) %>% # Notice the as.numeric()
select(gender, JobTitle, averageBasePay, Year)
SFWage %>% filter(gender != "" & Year == input$year)
})
# OLD ########################################
output$barplot <- renderPlot({
ggplot(clean(), aes(x = JobTitle, y = averageBasePay ))+ # Parenthesis
geom_bar(stat="Identity", width = 0.3, fill="orange")+
labs(x= "Jobs", y = "Wage", title = "Wage per job")
})
output$table <- renderDataTable({
clean() # Parenthesis
})
}
Don't forget to add the parenthesis, as I did here.
This should work, but I might have mistyped something or got it completely wrong. Since I don't have your data, I can't test it.
EDIT: Due to your comment, I added the as.numeric() term, as you can see above. However, if your data is not only not numeric but also with ,, you can do:
[...]
summarise(averageBasePay = mean(as.numeric(gsub(",", ".", BasePay)), na.rm=TRUE)) %>% # Notice the as.numeric() and the gsub()
[...]

Related

Loading online xml data to slider in R shiny dashboard

My friend and I built an R shiny dashboard using downloaded data. The code is as follows:
library(shiny)
library(shinydashboard)
library(dplyr)
library(tidyverse)
library(reshape)
library(scales)
ecd <- read.csv("ecd-figures.csv")
c(
"No of case" = "no_of_case",
"Minor Case" = "minor_case",
"All Non Fatal Case" = "all_non_fatal_case",
"Fatal Case" = "fatal_case"
) -> vec
ui <- fluidPage(sidebarLayout(
sidebarPanel
(
checkboxGroupInput("feature",
"Feature",
vec),
sliderInput(
"year",
"Year",
min = min(ecd$year),
max = max(ecd$year),
value = range(ecd$year),
sep = "",
step = 1
)
),
mainPanel(tabsetPanel(
tabPanel("Plot", plotOutput("correlation_plot")),
tabPanel("Table", tableOutput("ecd"))
))
))
server <- function(input, output) {
yearrange <- reactive({
ecd %>%
subset(year %in% input$year[1]:input$year[2]) %>%
select(c(year, input$feature))
})
output$correlation_plot <- renderPlot({
ecdsubset <- yearrange()
ecdsubset <- melt(ecdsubset, id = "year")
validate(need(input$feature, 'Check at least one item.'))
ggplot(ecdsubset, aes(x = year, y = value, color = variable)) + geom_line(size = 1) + scale_x_continuous(breaks =
seq(input$year[1], input$year[2], by = 1))
})
output$ecd <- renderTable({
yearrange()
})
}
shinyApp(ui, server)
The simple data file is here: https://drive.google.com/file/d/1pZQe89wxw14lirW2mRIgi9h29yPyc7Fs/view?usp=sharing
Then, I want to make everything online, i.e. calling api and not to download the csv file. It seems ok to read the contents rather simply, as follows:
library(xml2)
ecd_xml <-"https://www.labour.gov.hk/datagovhk/resource/ecd/ecd-figures.xml"
read_ecd <- read_xml(ecd_xml)
xml_find_all(read_ecd, ".//year")
{xml_nodeset (5)}
[1] <year>2015</year>
[2] <year>2016</year>
[3] <year>2017</year>
[4] <year>2018</year>
[5] <year>2019</year>
The problem is: how to parse every piece of information from the xml contents onto the dashboard?
Take the slidebar as an example. How to display the slidebar labels (i.e. 2015 and 2019) from parsing the <year> tag and selecting the max and min values?
And, can you recommend some reading materials for me to learn the whole process from xml to dashboard? Many many thanks in advance.
(P.S. I've tried to use xml package instead, since there are some standard arguments to find the max, min, and avg values of attributes. But I ran into another big error -
Error in function (type, msg, asError = TRUE) :
error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
What should I do? My R version is 4.0.5 (2021-03-31).)
You can use xml2::as_list to create a tibble from the XML tree.
Moreover, you can use shiny::updateSliderInput to update UI slider ranges:
library(shiny)
library(shinydashboard)
library(dplyr)
library(tidyverse)
library(reshape)
library(scales)
library(xml2)
vec <- c(
"No of case" = "no_of_case",
"Minor Case" = "minor_case",
"All Non Fatal Case" = "all_non_fatal_case",
"Fatal Case" = "fatal_case"
)
ui <- fluidPage(sidebarLayout(
sidebarPanel(
checkboxGroupInput(
"feature",
"Feature",
vec
),
sliderInput(
"year",
"Year",
min = 2000,
max = 2001,
value = c(2000, 2001),
sep = "",
step = 1
)
),
mainPanel(tabsetPanel(
tabPanel("Plot", plotOutput("correlation_plot")),
tabPanel("Table", tableOutput("table"))
))
))
server <- function(input, output, session) {
xml <-
"https://www.labour.gov.hk/datagovhk/resource/ecd/ecd-figures.xml" %>%
read_xml()
table <-
xml %>%
xml_find_all(".//item") %>%
map(as_list) %>%
map(~ .x %>% as_tibble()) %>%
bind_rows() %>%
unnest(everything()) %>%
type_convert()
years <- table$year %>% unique()
updateSliderInput(
session = session,
inputId = "year",
min = min(years),
max = max(years),
value = c(min(years), max(years)) # current selected range
)
sub_table <- reactive({
table %>%
filter(year %in% input$year[1]:input$year[2]) %>%
select(c(year, input$feature))
})
output$correlation_plot <- renderPlot({
validate(need(input$feature, "Check at least one item."))
sub_table() %>%
pivot_longer(-year) %>%
ggplot(aes(x = year, y = value, color = name)) +
geom_line(size = 1) +
scale_x_continuous(
breaks = seq(input$year[1], input$year[2], by = 1)
)
})
output$table <- renderTable({
sub_table()
})
}
shinyApp(ui, server)

Bar Plot does not Display in Shiny App, I have developed a code

I am new to the shiny app and trying to write simple code.
I changed the codes so many times but my bar chart doesn't work.
I have this problem in my other trying. That the UI works and also the codes in the server work part by part in R but not in the shiny app. I know some part of my server is not what I want, I just want to know what should I do to run the code which works in R but not in Shiny
#Graph 2
server<-#Graph 2
library("tidyverse")
library("leaflet")
library("leaflet.extras")
library("rnaturalearthdata")
library("sf")
library("DT")
library("ggplot2")
N <- read.csv("https://data.ontario.ca/dataset/f4112442-bdc8-45d2-be3c-12efae72fb27/resource/455fd63b-603d-4608-8216-7d8647f43350/download/conposcovidloc.csv")
function(input, output, session){
if(input$data.Gender){
City ="Ottawa"
N %>% subset(Reporting_PHU_City == City) %>%
select(Case_Reported_Date, Age_Group, Client_Gender, Outcome1)%>%
rename(Date = Case_Reported_Date)%>%
arrange(Age_Group, Client_Gender, Outcome1)
Data.N <- as.data.frame(N)
Data.N %>%
group_by(Age_Group) %>%
summarise(Age = n_distinct(Age_Group)) %>%
arrange(desc(Age))
w = table(N$Age_Group)
t = as.data.frame(w)
}
output$Plot <- renderPlot({
ggplot() +
geom_bar(stat = "identity",data = t,mapping = aes(x = N$Client_Gender , y =Freq))
})
}
Sorry, the site didn't let me share the UI too. This is Ui
UI<- #Graph 2
library("leaflet")
library("DT")
fluidPage(
sidebarLayout(
sidebarPanel(
radioButtons("data.Gender", "",
c("Data female" = "Gender.Female",
"Data male" = "Gender.Male"))
),
enter code here
mainPanel(
plotOutput("Plot")
)
))
Perhaps you should group by Client_Gender. Your present group by of Age_Group will give 1 as the value for all groups.
Try this
server <- function(input, output, session){
t <- reactive({
input$data.Gender
City ="Toronto"
aa <- N %>% subset(Reporting_PHU_City == City) %>%
select(Case_Reported_Date, Age_Group, Client_Gender, Outcome1)%>%
rename(Date = Case_Reported_Date)%>%
arrange(Age_Group, Client_Gender, Outcome1)
bb <- aa %>%
group_by(Client_Gender) %>%
summarise(Age = n_distinct(Age_Group)) %>%
arrange(desc(Age))
bb
})
output$Plot <- renderPlot({
ggplot(data=t()) +
geom_bar(stat = "identity",mapping = aes(x = Client_Gender , y = Age))
})
}

Show average value in table R by categories

I want to clean up my data but I'm quite new to R.
my code:
#library
library(dplyr)
library(shiny)
library(shinythemes)
library(ggplot2)
#Source
dataset <- read.csv("Wagegap.csv")
SFWage <- dataset %>% select(gender,JobTitle,BasePay,Year,)
clean <- na.omit(SFWage)
#UI
ui <- fluidPage(
theme = shinytheme("united"),
navbarPage("San Fransisco Wages",
tabPanel("Barplot",
mainPanel(
plotOutput("barplot")
)) ,
tabPanel("Table",
mainPanel(
dataTableOutput("table")
))
)
)
#server
server <- function(input, output){
output$barplot <- renderPlot({
ggplot(clean, aes(x = JobTitle, y = BasePay ))+
geom_bar(stat="Identity", width = 0.3, fill="orange")+
labs(x= "Jobs", y = "Wage", title = "Wage per job")
})
output$table <- renderDataTable({
clean
})
}
shinyApp(ui = ui, server = server)
I get this output for the table:
Table Output
What I would like is the Jobtitle to be bundled and only seperated by the gender, and showing the average of the BasePay.
M - account clerk - averageBasePay - Year
F - account clerk - averageBasePay - Year
and so on for every job.
This data will be used to compare the wagegap between genders for every job given in the dataset.
P.S.
If someone also could tell me why the na.omit didnt work to clean up the empty genders, that would be amazing :)
You could use group_by() and summarise()
SFWage <- dataset %>%
group_by(gender,JobTitle, Year) %>%
summarise(averageBasePay = mean(BasePay, na.rm=TRUE) %>%
select(gender, JobTitle, averageBasePay, Year)

Trying to create Shiny time-series graph of gapminder using ggplot

I am new to Shiny and, as an exercise, I am trying to assess global trends over time using the Gapminder data set. My aim is to produce a basic app that allows me to plot life expectancy (lifeExp), population (pop) and GDP per capita (gdpPercap) over time (year) separately. However, when I enter the following code all I get is a ggplot with a straight line. What am I missing?
Thanks in advance!
library(shiny)
library(gapminder)
library(dplyr)
library(ggplot2)
ui <- fluidPage(
selectInput(inputId = "variables",
label = "Select Variable",
choices = names(gapminder[, 4:6])),
plotOutput("plot")
)
server <- function(input, output){
output$plot <- renderPlot({
data <- gapminder %>% group_by(year) %>%
summarise(lifeExp = mean(lifeExp, na.rm = T),
pop = mean(pop, na.rm = T),
gdpPercap = mean(gdpPercap, na.rm = T))
ggplot(data, aes(x = year, y = input$variables)) + geom_line()
})
}
shinyApp(ui = ui, server = server)
With modern versions of ggplot2 rlang, we may modify the aes call to aes(x = year, y = .data[[input$variables]])) and do:
library(shiny)
library(gapminder)
library(dplyr)
library(ggplot2)
ui <- fluidPage(
selectInput(inputId = "variables",
label = "Select Variable",
choices = names(gapminder[, 4:6])),
plotOutput("plot")
)
server <- function(input, output){
output$plot <- renderPlot({
data <- gapminder %>% group_by(year) %>%
summarise(lifeExp = mean(lifeExp, na.rm = TRUE),
pop = mean(pop, na.rm = TRUE),
gdpPercap = mean(gdpPercap, na.rm = TRUE))
ggplot(data, aes(x = year, y = .data[[input$variables]])) +
geom_line()
})
}
shinyApp(ui = ui, server = server)
Reference: https://ggplot2.tidyverse.org/dev/articles/ggplot2-in-packages.html#using-aes-and-vars-in-a-package-function

Using validate in Shiny to hide plot without relevant data when using reactive function (R)

I have created an app using Shiny that displays data dependent on two different inputs. I'm filtering the data in a reactive function and then passing this through to the plots.
I can't work out how to simply hide the plots (and ideally show a helpful explanation) when there is no relevant data based on the inputs. I could do this if my data was in a dataframe, but as I have filtered it using a reactive function, this doesn't work.
I currently have the validate function nested in the renderPlot function, referencing the dataframe that is filtered by the reactive function...
Does anybody have any thoughts?
Reproducible code (if you select "Bristol" with the default date range, that demonstrates the issue):
library("tidyverse")
location <- as.character(c("London", "London", "Birmingham", "Bristol", "Birmingham", "Birmingham", "London", "Birmingham"))
dog_birthday <- as.POSIXct(c("01-01-2016", "02-02-2016", "03-03-2016", "04-04-2017", "05-05-2017", "06-06-2017", "08-08-2018", "07-07-2018"), format = "%d-%m-%Y")
dog_type <- as.character(c("Poodle", "Pug", "Labrador", "Poodle", "Poodle", "Labrador", "Pug", "Pug"))
dog_data <- data.frame(location, dog_birthday, dog_type)
ui<-
fluidPage(
sidebarLayout(
sidebarPanel(
dateRangeInput(
"dates", label = h3("Birthdate range"), start = ("01-06-2018"),
format = "dd-mm-yyyy", startview = "year"
),
selectInput(
"location", label = h3("Location"), choices = unique(dog_data$location),
multiple = T, selectize = T
)
),
mainPanel(
plotOutput(outputId = "dog_type")
)
)
)
server <- function(input, output) {
city_selection <- reactive({
req(input$location)
choose_city <- subset(dog_data, dog_data$location %in% input$location)
choose_city <- droplevels(choose_city)
return(choose_city)
})
output$dog_type <- renderPlot({
validate(
need(nrow(dog_data) > 0, "No data for this selection.")
)
dog_type_plot <- city_selection() %>%
filter(dog_birthday >= input$dates[1] & dog_birthday <= input$dates[2]) %>%
count(dog_type) %>%
arrange(-n) %>%
mutate(dog_type = factor(dog_type, dog_type)) %>%
ggplot(aes(dog_type, n)) +
geom_bar(stat = "identity")
dog_type_plot
})
}
shinyApp(ui, server)
You need to move the dates filter to the city_selection reactive and update the need condition in validate -
server <- function(input, output) {
city_selection <- reactive({
req(input$location)
choose_city <- subset(dog_data, dog_data$location %in% input$location) %>%
filter(dog_birthday >= input$dates[1] & dog_birthday <= input$dates[2])
choose_city <- droplevels(choose_city)
return(choose_city)
})
output$dog_type <- renderPlot({
validate(
need(nrow(city_selection()) > 0, "No data for this selection.")
)
dog_type_plot <- city_selection() %>%
count(dog_type) %>%
arrange(-n) %>%
mutate(dog_type = factor(dog_type, dog_type)) %>%
ggplot(aes(dog_type, n)) +
geom_bar(stat = "identity")
dog_type_plot
})
}
I also got an error trying to run the code:
Warning: Error in count: Argument 'x' must be a vector: list
A few other things that I noticed:
For me, choose_city <- droplevels(choose_city) doesn't do anything, I think you need choose_city$location <- droplevels(choose_city$location) if you're trying to remove the un-selected factor levels from location
I think #Shree's suggestion will help, but this method still only checks for the location, not the dates. (The reason your version doesn't do anything is because dog_data is your reference data.frame, and it doesn't get changed by your subsetting) #Shree's updated answer moved the date subset and now is probably better than this one :)
I changed your code a decent amount to get it to work for me (just because I don't use pipes and am most familiar with data.table). Obviously you can just remove the data.table dependency and filter with pipes!
The main thing is just that you want to check what dog_type_plot looks like right before making the plot. I added a reactiveVal to hold a message that's output in the sidebar:
library("tidyverse")
library("data.table")
location <- as.character(c("London", "London", "Birmingham", "Bristol", "Birmingham", "Birmingham", "London", "Birmingham"))
dog_birthday <- as.POSIXct(c("01-01-2016", "02-02-2016", "03-03-2016", "04-04-2017", "05-05-2017", "06-06-2017", "08-08-2018", "07-07-2018"), format = "%d-%m-%Y")
dog_type <- as.character(c("Poodle", "Pug", "Labrador", "Poodle", "Poodle", "Labrador", "Pug", "Pug"))
dog_data <- data.frame(location, dog_birthday, dog_type)
ui<-
fluidPage(
sidebarLayout(
sidebarPanel(
dateRangeInput(
"dates", label = h3("Birthdate range"), start = ("01-06-2018"),
format = "dd-mm-yyyy", startview = "year"
),
selectInput(
"location", label = h3("Location"), choices = unique(dog_data$location),
multiple = T, selectize = T
),
textOutput(outputId = "noDataMsg")
),
mainPanel(
plotOutput(outputId = "dog_type")
)
)
)
server <- function(input, output) {
## Subset base data.frame by user-selected location(s)
city_selection <- reactive({
req(input$location)
choose_city <- subset(dog_data, dog_data$location %in% input$location)
choose_city$location <- droplevels(choose_city$location)
return(choose_city)
})
## Value to hold message
message_v <- reactiveVal(); message_v("blank")
## Make Histogram
output$dog_type <- renderPlot({
print("city_selection():")
print(city_selection())
cat("\n")
## Change to data.table
data_dt <- as.data.table(city_selection())
print("original data_dt:")
print(data_dt)
cat("\n")
## Subset by birthday
dog_type_plot <- data_dt[dog_birthday >= input$dates[1] &
dog_birthday <= input$dates[2],]
print("subset by birthday")
print(dog_type_plot)
cat("\n")
## Get counts and sort
dog_type_plot[, N := .N, by = dog_type]
dog_type_plot <- dog_type_plot[order(-N)]
print("add count:")
print(dog_type_plot)
cat("\n")
## Change dog type to factor
dog_type_plot$dog_type <- factor(dog_type_plot$dog_type, levels = unique(dog_type_plot$dog_type))
print("refactor of dog_type:")
print(dog_type_plot$dog_type)
cat("\n")
## Check for data to plot
if (nrow(dog_type_plot) == 0) {
message_v("No dogs to plot using these parameters")
return(NULL)
} else {
## Make plot
plot_gg <- ggplot(data = dog_type_plot, aes(x = dog_type, y = N)) +
geom_bar(stat = "identity")
## Return
return(plot_gg)
} # fi
}) # renderPlot
## Message to user
output$noDataMsg <- renderText({ if (message_v() == "blank") { return(NULL) } else { message_v() } })
}
shinyApp(ui, server)

Resources