I'm new to Shiny and have run into problems when I try to render a ggplot. I want to render a plot with multiple lines but I get the error: Warning: Error in : Aesthetics must be either length 1 or the same as the data (1)
It work fine when I render a single line, but not multiple. There are earlier questions on Stack Overflow adressing similar issues, but I'm afraid I dont fully understand their soulutions.
Help would be much appreciated. :)
library(tidyverse)
library(shiny)
url <- paste("https://www.ecdc.europa.eu/sites/default/files/documents/COVID-19-geographic-disbtribution-worldwide-",format(Sys.time(), "%Y-%m-18"), ".xlsx", sep = "")
GET(url, authenticate(":", ":", type="ntlm"), write_disk(tf <- tempfile(fileext = ".xlsx")))
df <- read_excel(tf)
df <- df %>%
rename(country = countriesAndTerritories) %>%
arrange(country, dateRep) %>%
group_by(country) %>%
mutate(Cumulative_Death = cumsum(deaths)) %>%
ungroup() %>%
filter(Cumulative_Death > 9) %>%
group_by(country) %>%
mutate(numbers_of_days = row_number(),
First_Death_Date = min(dateRep)) %>%
select(country, numbers_of_days, deaths, Cumulative_Death)
ui <- fluidPage(
titlePanel("Statistik Covid-19"),
sidebarLayout(
sidebarPanel(
selectInput("cou", "Country:", choices = unique(df$country), selected = "SWeden", multiple = TRUE),
selectInput("var", "Variable:", choices = c("deaths", "Cumulative_Death"))),
mainPanel(
plotOutput("covid"))
))
server <- function(input, output, session){
selected <- reactive(filter(df, country %in% input$cou))
output$covid <- renderPlot({
ggplot(selected(), aes(x=numbers_of_days, input$var, colour = input$cou)) +
geom_line(size = 1.5) +
labs(title = "Covid-19: Antal döda per 100 000 invånare",
x = "DAGAR SEDAN ANTAL DÖDSFALL ÖVERSTEG TIO",
y = paste0(input$var),
caption = "Source: European Centre for Disease Prevention and Control") +
guides(colour = guide_legend(title=NULL))
})
}
shinyApp(ui, server)
Try this. As #SusanSwitzer already mentioned. Main issue is that you use input$land. So. Simply replace with input$country. Second. Map colour on country which is the varname in the df. Third I switched to aes_string instead of aes to use the character inputs:
library(shiny)
library(ggplot2)
library(dplyr)
url <- paste("https://www.ecdc.europa.eu/sites/default/files/documents/COVID-19-geographic-disbtribution-worldwide-",format(Sys.time(), "%Y-%m-18"), ".xlsx", sep = "")
httr::GET(url, httr::authenticate(":", ":", type="ntlm"), httr::write_disk(tf <- tempfile(fileext = ".xlsx")))
df <- readxl::read_excel(tf)
df <- df %>%
rename(country = countriesAndTerritories) %>%
arrange(country, dateRep) %>%
group_by(country) %>%
mutate(Cumulative_Death = cumsum(deaths)) %>%
ungroup() %>%
filter(Cumulative_Death > 9) %>%
group_by(country) %>%
mutate(numbers_of_days = row_number(),
First_Death_Date = min(dateRep)) %>%
select(country, numbers_of_days, deaths, Cumulative_Death)
ui <- fluidPage(
titlePanel("Statistik Covid-19"),
sidebarLayout(
sidebarPanel(
selectInput("country", "Country:", choices = unique(df$country), selected = "Sweden", multiple = TRUE),
selectInput("var", "Variable:", choices = c("deaths", "Cumulative_Death"))),
mainPanel(
plotOutput("covid"))
))
server <- function(input, output, session){
# input$country instead input$land
selected <- reactive(filter(df, country %in% input$country))
output$covid <- renderPlot({
# switch to aes_string. map colour on country instead of input$land
ggplot(selected(), aes_string(x = "numbers_of_days", y = input$var, colour = "country")) +
geom_line(size = 1.5) +
labs(title = "Covid-19: Antal döda per 100 000 invånare",
x = "DAGAR SEDAN ANTAL DÖDSFALL ÖVERSTEG TIO",
y = paste0(input$var),
caption = "Source: European Centre for Disease Prevention and Control") +
guides(colour = guide_legend(title=NULL))
})
}
shinyApp(ui, server)
Related
I have a dataset on which I want to apply 3 filters (City, Type and Name of Universities) consecutively. I want to apply a sequential filtering such that when I choose a specific City, other select inputs (Type and Name of universities) be updated accordingly.
I have tried many variations also through stackoverflow solution, however, unable to come up with a solution.
library(shiny)
library(shinythemes)
library(dplyr)
library(tidyr)
library(readxl)
data <- read_excel("foreign_students_by_nationality_2021_2022.xlsx")
colnames(data) <- c("name", "type", "city", "country", "male", "female", "total")
data$male <- as.numeric(data$male)
data$female <- as.numeric(data$female)
data$total <- as.numeric(data$total)
print(str(data))
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Foreign Students in Turkish Universities"),
theme = shinythemes::shinytheme("superhero"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput("uni_city",
"Select a City",
choices = data$city %>% unique() %>% sort()
),
selectInput("uni_type",
"Select a University Type",
choices = ""
),
selectInput("uni_name",
"Select a University",
choices = ""
)
),
mainPanel(
plotOutput("barplot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output, session) {
type_var <- reactive({data %>% filter(city == input$uni_city) %>%
select(type) %>% unique()})
observe({
updateSelectInput(session, "uni_type", choices = type_var())
})
name_var <- reactive({data %>% filter(city == input$uni_city, type == input$uni_type) %>%
select(name) %>% unique() %>% sort()})
observe({
updateSelectInput(session, "uni_name", choices = name_var())
})
output$barplot <- renderPlot({
highest_country <-
data %>%
filter(city == input$uni_city,
type == input$uni_type,
name == input$uni_name) %>%
group_by(country) %>%
summarise(female = sum(female),
male = sum(male),
total = sum(total)) %>%
arrange(desc(total)) %>%
pivot_longer(c(-country, -total), names_to = "gender", values_to = "value") %>%
slice_max(total, n = 20)
ggplot(highest_country,
aes(x = reorder(country, total),
y = value,
fill = gender)
) +
geom_col() +
coord_flip() +
labs(x = "Nationality", y = "Number of Students")
})
}
# Run the application
shinyApp(ui = ui, server = server)
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)
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()
[...]
When I try to produce a highcharter barplot within Shiny using a dataset that is grouped and summarized based on the selectInput values and these same values are referenced within hcaes() I get the error "object 'input' not found"
I have also tried hcaes_string() and then I get "object 'My.Variable' not found" but when I just put in My.Variable, it will produce the hchart so it can interact with the dataset being created within the server function. Obviously I'd like to switch between My.Variable and My.Variable2 with the dropdown. I've tried assigning the summarized dataset to a reactive object, but then I get the error "Objects of class/type reactiveExpr/reactive/function are not supported by hchart (yet)."
I've been at this for hours and this is my first question on StackOverflow. I rigged up a sample dataset so that the code is reproducible and I've updated R and RStudio to the latest versions.
library('highcharter')
library('plyr')
library('dplyr')
library('tidyr')
library('lubridate')
library('stringr')
library('tools')
library('shiny')
#demo <- read.csv("data/name-change-analysis.csv",stringsAsFactors = FALSE)
indiv <- rep(c('p1','p2','p3','p4','p5'),4)
Name.Change <- rep(c('yes','yes','no','yes','no'),4)
Overall.Category <- rep(c('against','support','support','neutral','against'),4)
Race <- rep(c('Black','White','White','Asian','White'),4)
Gender <- rep(c('Male','Male','Male','Female','Male'),4)
demo <- as.data.frame(cbind(indiv,Name.Change,Overall.Category,Race,Gender))
ui <-
navbarPage(
"Responses by demographics",
tabPanel(
"Manual labels",
fluidPage(
fluidRow(
column(
selectInput(
"category",
label = "Select a demographic category:",
choices = c("Race",
"Gender" = "gender")
),
width = 6
),
column(
selectInput(
"name_or_overall",
label = "Response Category",
choices = c(
"Name Change" = "Name.Change",
"Overall Category" = "Overall.Category"
),
width = "100%"
),
width = 6
)
),
highchartOutput("hcontainer")
)
),
collapsible = TRUE
)
server <- function(input, output, session) {
output$hcontainer <- renderHighchart({
demo %>%
group_by(input$category,input$name_or_overall) %>%
summarise(count = n()) %>%
hchart(type = "bar",
hcaes(y = "count",
x = as.name(input$category),
group = as.name(input$name_or_overall))) %>%
hc_plotOptions(bar = list(stacking = "percent")) %>%
hc_tooltip(shared = TRUE)
})
}
shinyApp(ui,server)
Try this
output$hcontainer <- renderHighchart({
df1 <- demo %>% mutate(var1=demo[[as.name(input$category)]], var2=demo[[as.name(input$name_or_overall)]])
df <- df1 %>% group_by(var1,var2) %>% summarise(count = n())
highchart() %>%
hc_add_series(df, type = "bar",
hcaes(y = "count",
x = "var1",
group = "var2")) %>%
hc_plotOptions(bar = list(stacking = "percent")) %>%
hc_tooltip(shared = FALSE)
})
You will get the following output:
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