I am relatively new to R, and I'm trying to build a reactive ggplot in Shiny where the X-axis (dates) is reactive to a dateRangeInput in the UI. I've been googling everywhere, but every thing I try returns an error.
In the ggplot, the aes() calls from a dataset called datecorrected_totals, where x is the dates, and y=load are the two values that I would like to be reactive to the dateRangeInput so the ggplot will adjust the scale based on the period within the daterangeinput.
library(tidyverse)
library(shiny)
library(tidyr)
library(lubridate)
library(zoo)
data <- read_csv("--")
# Define UI ----
ui <- fluidPage(
titlePanel("--"),
sidebarLayout(
sidebarPanel(
h3("Calculator"),
dateRangeInput("dates", label = "Dates",
start = ("10-18-2018"),
end = max("05-29-2019"),
min = min("10-18-2018"),
max = max("05-29-2019"),
format = "mm-dd-yyyy"),
sliderInput("slider_a", label = "--",
min = 0,
max = 7,
value = 0),
sliderInput("slider_c", label = "--",
min = 7,
max = 42,
value = 7)
),
mainPanel(plotOutput('bar_chart'))
)
)
# Define server logic ----
server <- function(input, output, session) {
RE <- reactive({
})
output$bar_chart <- renderPlot(
ggplot(data = datecorrected_totals, aes(x = x, y = load)) +
geom_bar(stat = "identity")
)
}
# Run the app ----
shinyApp(ui = ui, server = server)
You need to filter the original dataset by the input dates. In this example data would be your original dataset.
RE <- reactive({
data %>%
filter(x>=input$dates[1] & x<=input$dates[2])
})
output$bar_chart <- renderPlot(
ggplot(data = RE(), aes(x = x, y = load)) +
geom_bar(stat = "identity")
There is no need to create a separate reactive() expression (unless required otherwise). The filter can be applied directly in renderPlot(). Thus, output$bar_chart becomes
output$bar_chart <- renderPlot(
datecorrected_totals %>%
filter(between(x, input$dates[1], input$dates[2])) %>%
ggplot(aes(x = x, y = load)) +
geom_bar(stat = "identity")
)
Below is a self-contained minimal reproducible example:
library(tidyverse)
library(lubridate)
library(shiny)
datecorrected_totals <- tibble(x = seq(as.Date("2018-10-18"), as.Date("2019-05-29"), length.out = 10L),
load = day(x))
# Define UI ----
ui <- fluidPage(
titlePanel("--"),
sidebarLayout(
sidebarPanel(
h3("Calculator"),
dateRangeInput("dates", label = "Dates",
start = mdy("10-18-2018"),
end = mdy("05-29-2019"),
min = mdy("10-18-2018"),
max = mdy("05-29-2019"),
format = "mm-dd-yyyy"),
),
mainPanel(plotOutput('bar_chart'))
)
)
# Define server logic ----
server <- function(input, output, session) {
output$bar_chart <- renderPlot(
datecorrected_totals %>%
filter(between(x, input$dates[1], input$dates[2])) %>%
ggplot(aes(x = x, y = load)) +
geom_col()
)
}
# Run the app ----
shinyApp(ui = ui, server = server)
Note that the date strings have been coerced to valid Date objects by calling mdy() to avoid error messages.
In addition, geom_bar(stat = "identity") has been replaced by geom_col().
Related
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
I have created the following dataframe and shiny app
# Import packages
library(readxl)
require(ggplot2)
require(janitor)
require(lubridate)
require(shiny)
require(plotly)
require(reshape2)
Function from stack overflow
#generate date randomly
rdate <- function(x,
min = paste0(format(Sys.Date(), '%Y'), '-01-01'),
max = paste0(format(Sys.Date(), '%Y'), '-12-31'),
sort = TRUE) {
dates <- sample(seq(as.Date(min), as.Date(max), by = "day"), x, replace
= TRUE)
if (sort == TRUE) {
sort(dates)
} else {
dates
}
}
Next we create a dataframe
DF<-as.data.frame("Date"<-rdate(100))
DF$variable<-LETTERS[seq( from = 1, to = 10 )]
DF$Value<-round(runif(1:nrow(DF),min = 10, max = 50))
# subset the dataframe
DF<-DF[,c(2:4)]
DF
# Write to csv
write.csv(DF, file = "Book1.csv", col.names = F)
Next we create the shiny APP
# UI creation
UI<-fluidPage(fileInput("file", "Browse",
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")),
#selectInput(inputId = "Speciesname", label = "Name",choices =
#NULL,selected = NULL),
plotOutput(outputId = "plot1" ))
# Server creation
Server<-function(input, output, session){
output$plot1<-renderPlot({
infile <- input$file
if (is.null(infile)) {
# User has not uploaded a file yet
return(NULL)
}
Book1 <- read.csv(input$file$datapath, stringsAsFactors = F)
Book1<-data.frame(Book1)
Book2<-remove_empty_rows(Book1)
ggplot(DF, aes(x = Date, y = Value, colour = variable)) +
geom_line() +
ylab(label="Number of Sales") +
xlab("Sales Week")
}
)
}
shinyApp(UI, Server)
In this App, the graph is generated in the output namey the Shiny UI.
When I make the following change in the server, the plot is generated not in the shiny UI but in the r studio console
Book2<-remove_empty_rows(Book1)
P<- ggplot(DF, aes(x = Date, y = Value, colour = variable)) +
geom_line() +
ylab(label="Number of Sales") +
xlab("Sales Week")
return(ggplotly(p))
I am unable to obtain the plot in the shiny UI console. I request someone to help me. I am unable to locate the error
If you're using plotly in shiny apps, plotOutput() and renderPlot() won't work. You need to have plotlyOutput() and renderPlotly(). The code below should work. I've taken out the fileInput and read.csv to make it a bit easier.
library(shiny)
library(plotly)
rdate <- function(x,
min = paste0(format(Sys.Date(), '%Y'), '-01-01'),
max = paste0(format(Sys.Date(), '%Y'), '-12-31'),
sort = TRUE) {
dates <- sample(seq(as.Date(min), as.Date(max), by = "day"), x, replace
= TRUE)
if (sort == TRUE) {
sort(dates)
} else {
dates
}
}
UI<-fluidPage(
plotlyOutput(outputId = "plot1" ))
# Define server logic required to draw a histogram
server <- function(input, output) {
output$plot1 <- renderPlotly({
# generate bins based on input$bins from ui.R
DF<-data.frame(Date = rdate(100))
DF$variable<-LETTERS[seq( from = 1, to = 10 )]
DF$Value<-round(runif(1:nrow(DF),min = 10, max = 50))
g <- ggplot(DF, aes(x = Date, y = Value, colour = variable)) +
geom_line() +
ylab(label="Number of Sales") +
xlab("Sales Week")
ggplotly(g)
})
}
# Run the application
shinyApp(ui = UI, server = server)
I would like two plots to appear. First, a scatter plot and then a line graph. The graphs aren't important. This is my first time using Shiny. What is the best way to have both
plotOutput("needles"),
plotOutput("plot")
use the data from the same needles data frame? I think I'm getting confused as to how to pass the "needles" data frame between the plotOutput functions.
library(shiny)
library(tidyverse)
library(scales)
# Create the data frame ________________________________________________
create_data <- function(num_drops) {
needles <- tibble (
x = runif(num_drops, min = 0, max = 10),
y = runif(num_drops, min = 0, max = 10)
)
}
# Show needles ________________________________________________
show_needles <- function(needles) {
ggplot(data = needles, aes(x = x, y = y)) +
geom_point()
}
# Show plot __________________________________________________
show_plot <- function(needles) {
ggplot(data = needles, aes(x = x, y = y)) +
geom_line()
}
# Create UI
ui <- fluidPage(
sliderInput(inputId = "num_drops",
label = "Number of needle drops:",
value = 2, min = 2, max = 10, step = 1),
plotOutput("needles"),
plotOutput("plot")
)
server <- function(input, output) {
output$needles <- renderPlot({
needles <- create_data(input$num_drops)
show_needles(needles)
})
output$plot <- renderPlot({
show_plot(needles)
})
}
shinyApp(ui = ui, server = server)
We could execute the create_data inside a reactive call in the server and then within the renderPlot, pass the value (needles()) as arguments for show_needles and show_plot
server <- function(input, output) {
needles <- reactive({
create_data(input$num_drops)
})
output$needles <- renderPlot({
show_needles(needles())
})
output$plot <- renderPlot({
show_plot(needles())
})
}
shinyApp(ui = ui, server = server)
-output
I'm teaching myself Shiny and I am stuck on my ggplot2 graph not being able to use the reactive dateRangeInput as my x-axis. I have a few questions:
Is there a way to use my data frame to grab the min, max values for date range input instead of having to hardcode them in so that when I add more tweets to the data frame I don't have to hardcode the values each time?
I am getting the error: Aesthetics must be either length 1 or the same as the data (33108): x, y when I try to use input$date as my aes(x = input$date...
library(shiny)
library(tidyr)
library(ggplot2)
tweets <- read.csv(file.choose())
colnames(tweets)[1] <- "Content"
tweets <- separate(tweets, created_at, c("Date", "Time"), sep = " ")
tweets$Date <-as.Date(tweets$Date, "%m/%d/%Y")
ui <- fluidPage(
dateRangeInput(inputId = "date",
strong("Date Range"),
start = "2009-05-04", end = "2018-02-28",
min = "2009-05-04", max ="2018-02-28" ),
plotOutput("Graph")
)
server <- function(input, output) {
output$Graph <- renderPlot({
ggplot(tweets, aes(x = input$date, y = count)) +
geom_bar(stat = "identity", position = "stack") +
#scale_y_continuous(name = "Retweet Count", limits = c(0,370000), breaks=seq(0,370000,10000)) +
theme(panel.background = element_rect(fill = "white", colour = "grey50"))
})
}
shinyApp(ui = ui, server = server)
#Pete900's answer summarizes the use of updateDateRangeInput well, for further information you can refer to this part of the shiny documentation.
About your second problem: input$date will return a vector of length 2 with the first element beeing the lower and the second being the upper part of the selected range. You will most likely not use this directly as x-aesthetics but rather subset your data with this and then plot the newly subsettet data. You can e.g. write
library(dpylr) # alternatevly library(tidyverse)
newtweets <- reactive({
filter(tweets, between(date ,input$date[1], input$date[2]))
})
then, in your ggplot, use newtweets() as your data.
Update
The functions filter and between() (which is a shortcut for x is greater than ... and lesser then ...) come fromt the package dplyr, which is great for working with dataframes and part of a collection of packages that play very nicely with each other called tidyverse (see here).
When you refer to the newly created reactive object newtweets(), make sure to not forget the paranthesis because it is now a function call, that enables shiny to update the dataframe should the input change.
Update
A full working example in which I create some artificial data:
library(shiny)
library(tidyverse)
library(lubridate)
# tweets <- read.csv(file.choose())
st <- ymd("2009-05-01")
en <- ymd("2018-02-28")
dates <- seq.Date(from = st, to = en, by = 1)
tweets <- tibble(date = dates, count = rnorm(length(dates), mean = 5, sd = 3))
ui <- fluidPage(
dateRangeInput(inputId = "date",
strong("Date Range"),
start = "2009-05-04", end = "2018-02-28",
min = "2009-05-04", max ="2018-02-28" ),
plotOutput("Graph")
)
server <- function(input, output) {
newtweets <- reactive({
filter(tweets, between(date ,input$date[1], input$date[2]))
})
output$Graph <- renderPlot({
ggplot(newtweets(), aes(x = date, y = count)) +
geom_bar(stat = "identity", position = "stack") +
#scale_y_continuous(name = "Retweet Count", limits = c(0,370000), breaks=seq(0,370000,10000)) +
theme(panel.background = element_rect(fill = "white", colour = "grey50"))
})
}
shinyApp(ui = ui, server = server)
For the first question you can use updateDateRangeInput see here. So you would find your min and max dates in tweets outside of the server function then pass them to the input. Make sure to add session to your function:
server <- function(input, output, session) {
observe({
updateDateRangeInput(session, "date", min = myMinDate, max = myMaxDate)
})
}
For the second question you need to use aes_string to pass variables to ggplot, see here or here.
I am new to R and Shiny package. I have a csv file with 4 col and 600 rows and I am trying to plot some graphs using ggplot2.
My ui and server codes are like:
dt<-read.csv('file.csv')
server <- function(input, output) {
output$aPlot <- renderPlot({
ggplot(data = dt, aes(x = Col1, y = Col2, group = 'Col3', color = 'Col4')) + geom_point()
})
}
ui <- fluidPage(sidebarLayout(
sidebarPanel(
sliderInput("Obs", "Log FC", min = 1, max = 600, value = 100)
),
mainPanel(plotOutput("aPlot")) ))
Here, I can get the ggplot output but I don't know how to use this slider input to control the number of rows to be read i.e., I want this "Obs" input to define the size of Col1 to be used in the graph.
Try something like this, example here is with mtcars dataset:
library(shiny)
library(ggplot2)
dt <- mtcars[,1:4]
ui <- fluidPage(
sidebarPanel(
sliderInput("Obs", "Log FC", min = 1, max = nrow(dt), value = nrow(dt)-10)
),
mainPanel(plotOutput("aPlot"))
)
server <- function(input, output) {
mydata <- reactive({
dt[1:as.numeric(input$Obs),]
})
output$aPlot <- renderPlot({
test <- mydata()
ggplot(data = test, aes(x = test[,1], y = test[,2], group = names(test)[3], color = names(test)[4])) + geom_point()
})
}
shinyApp(ui = ui, server = server)
Change your server to:
server <- function(input, output) {
observe({
dt_plot <- dt[1:input$Obs,]
output$aPlot <- renderPlot({
ggplot(data = dt_plot, aes(x = Col1, y = Col2, group = 'Col3', color = 'Col4')) + geom_point()
})
})
}