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.
Related
In R - I am trying to create a dropdown for my plot built using ggplot. I decided to use the shiny package to do this. I am not sure why, but when the server has launched the dropdown and everything is there but when I try to click different options the plot won't show up. I know my list is currently static - I am just trying to get this to work first before I update that part. I will show my code below. I am trying to filter on the Event column. The event column will have values like "Iowa # Ohio State", etc.
library(tidyverse)
library(tcltk)
library(latticeExtra)
library(patchwork)
rm(list=ls())
#new code
# Start with a usual ggplot2 call:
server = function(input, output, session) {
#load data
df3 = read.csv("C:/Users/X/Desktop/Data/Consolidated_Data_2020-12-27.csv")
#summarize data
data = reactive({
req(input$Event)
df3 <- filter(df3, Side == "Favorite")
df3 <- df3 %>% filter(Event %in% input$Event)
})
#plot
output$plot = renderPlot({
g = ggplot(data(), aes(x=-(Time.Remaining))) +
geom_line(color = "steelblue", size = 1, aes(y=Original.Line)) +
geom_line(color = "#69b3a2", size = 1, aes(y=Live.Line)) +
labs(title = "Live Line vs Original",
subtitle = df3$Event,
y = "Original Line", x = "Time Remaining") +
facet_wrap(~ Half)
})
}
ui = basicPage(
h1("Game"),
selectInput(inputId = "Event",
label = "Choose Game",
list("Alcorn State Braves # Vanderbilt Commodores", "DePaul Blue Demons # Providence Friars")),
plotOutput("plot")
)
shinyApp(ui = ui, server = server)
I am teaching myself r and shiny and trying to make an interactive bar chart where the user can change the chart based on columns. I keep getting errors with this code. Any help would be appreciated! My data has four columns: v, one, two, three. The first column is characters and the last three are numbers. I want to change the bar chart based on the y axis (columns: one, two and three). Right now, the error I am getting is: missing value where TRUE/FALSE needed.
library(shiny)
library(readr)
library(ggplot2)
data <- read.csv('scoring.csv')
data$v <- as.character(data$v)
ui <- fluidPage(
titlePanel("Scoring"),
sidebarPanel(
selectInput(inputId = "scoring", label = "Select a score:", c("Scoring Method 1", "Scoring Method 2", "Scoring Method 3"))),
mainPanel(
plotOutput(outputId = "bar")
)
)
#browser()
server <- function(input, output) {
new_data <- reactive({
selected_score = as.numeric(input$"scoring")
if (selected_score == "Scoring Method 1"){(data[data$one])}
if (selected_score == "Scoring Method 2"){(data[data$two])}
if (selected_score == "Scoring Method 3"){(data[data$three])}
})
#browser()
output$bar <- renderPlot({
newdata <- new_data()
p <- ggplot(newdata, aes(x=reorder(v, -selected_score), selected_score, y = selected_score, fill=v)) +
geom_bar(stat = 'identity', fill="darkblue") +
theme_minimal() +
ggtitle("Sports")
barplot(p, height = 400, width = 200)
})
}
Run the application
shinyApp(ui = ui, server = server)
You have a few errors in your code. In the server part, please use input$scoring, instead of input$"scoring".
First, in ui selectInput could be defined as
selectInput(inputId = "scoring", label = "Select a score:", c("Scoring Method 1"="one",
"Scoring Method 2"="two",
"Scoring Method 3"="three")))
Second, your reactive dataframe new_data() could be defined as shown below:
new_data <- reactive({
d <- data %>% mutate(selected_score = input$scoring)
d
})
Third, ggplot could be defined as
output$bar <- renderPlot({
newdata <- new_data()
p <- ggplot(newdata, aes(x=v, y = newdata[[as.name(selected_score)]], fill=v)) +
geom_bar(stat = 'identity', position = "dodge", fill="blue") +
theme_bw() +
#scale_fill_manual(values=c("blue", "green", "red")) +
scale_y_continuous(limits=c(0,10)) +
ggtitle("Sports")
p
})
Please note that you had an extra selected_score variable within aes. My suggestion would be to play with it to reorder x, and review some online or youtube videos on R Shiny.
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().
My shiny code has an rhandstontable that the user can edit. This leads to an update of the rightmost columns, based on a custom function. the code also plots values from the table on two ggplots, which also get updated when the table values change. All of this works except that there is a funny double refresh that makes Shiny slow; my table isn't big, about 50rows by 23 columns where only 4 columns are used in the plots but about 12 columns go into my custom function.
Is there a way to make shiny faster using observe(), reactiveValues, or other related functions?
I'm new at reactive expressions and I've been reading that it might be possible to make the app faster by caching data properly.
library(shiny)
library(rhandsontable)
library(tidyverse)
library(ggthemes)
library(ggrepel)
## Create the dataset
DF <- readRDS("data/DF2.Rds")
numberofrows <- nrow(DF)
# weighting variables
w1 = (c(4,3,1))
w2 = (c(1,1,1,1))
w3 = (c(2,2,1,2,1,1,2))
# Function to calculate scores
ScoresTbl <- function(data, w1, w2, w3){
Description <- data[,1:9]
Potential <- crossprod(t(data[,10:12]), w1)/sum(w1)
Setting <- crossprod(t(data[,13:16]), w2)/sum(w2)
Risk <- crossprod(t(data[,17:23]),w3)/sum(w3)
data.frame(data[1:23],Potential,Setting,Risk) %>%
mutate(
SOP = rowMeans(data.frame(Potential,Setting,Risk)))
}
ui = fluidPage(
fluidRow(column(12,
rHandsontableOutput('hotable1', width = "100%", height = "25%")#,
# actionButton("go", "Plot Update")
)),
fluidRow(column(6, plotOutput("plot1")),
column(6, plotOutput("plot2")))
)
server <- shinyServer(function(input, output) {
indat <- reactiveValues(data=ScoresTbl(DF,w1, w2, w3))
observe({
if(!is.null(input$hotable1))
indat$data <- hot_to_r(input$hotable1)
})
output$hotable1 <- renderRHandsontable({
rhandsontable(ScoresTbl(indat$data,w1, w2, w3))
})
output$plot1 <- renderPlot({
ggplot(data = indat$data,
aes(x=Potential,
y=Setting, label = Project)) +
geom_point(alpha = 0.5) +
scale_size(range = c(2,15)) +
geom_text_repel(colour = "black",size = 2.5) +
theme_minimal()
})
output$plot2 <- renderPlot({
ggplot(data = indat$data,
aes(x=Potential,
y=Setting, label = Project)) +
geom_point(alpha = 0.5) +
scale_size(range = c(2,15)) +
geom_text_repel(colour = "black",size = 2.5) +
theme_minimal()
})
})
shinyApp(ui, server)
I am learning Shiny and wanted help on a app that I am creating. I am creating an app that will take dynamic inputs from the user and should generate bar and line charts. I managed to create the bar chart but it is generating incorrect result.
What I am looking for is variable selected in row should be my x-axis and y-axis should be percentage. scale to be 100%. column variable should be the variable for comparison and for that I am using position = "dodge". My data is big and I have created a sample data to depict the situation. Since actual data is in data.table format I am storing the sample data as data.table. Since I am not sure how I can include this data which is not in a file format, I create it first so that it is in R environment and then run the app -
Location <- sample(1:5,100,replace = T)
Brand <- sample(1:3,100,replace = T)
Year <- rep(c("Year 2014","Year 2015"),50)
Q1 <- sample(1:5,100,replace = T)
Q2 <- sample(1:5,100,replace = T)
mydata <- as.data.table(cbind(Location,Brand,Year,Q1,Q2))
Below is the Shiny code that I am using -
library("shiny")
library("ggplot2")
library("scales")
library("data.table")
library("plotly")
ui <- shinyUI(fluidPage(
sidebarPanel(
fluidRow(
column(10,
div(style = "font-size: 13px;", selectInput("rowvar", label = "Select Row Variable", ''))
),
tags$br(),
tags$br(),
column(10,
div(style = "font-size: 13px;", selectInput("columnvar", "Select Column Variable", ''))
))
),
tabPanel("First Page"),
mainPanel(tabsetPanel(id='charts',
tabPanel("charts",tags$b(tags$br("Graphical Output" )),tags$br(),plotlyOutput("plot1"))
)
)
))
server <- shinyServer(function(input, output,session){
updateTabsetPanel(session = session
,inputId = 'myTabs')
observe({
updateSelectInput(session, "rowvar", choices = (as.character(colnames(mydata))),selected = "mpg")
})
observe({
updateSelectInput(session, "columnvar", choices = (as.character(colnames(mydata))),selected = "cyl")
})
output$plot1 <- renderPlotly({
validate(need(input$rowvar,''),
need(input$columnvar,''))
ggplot(mydata, aes(x= get(input$rowvar))) +
geom_bar(aes(y = ..prop.., fill = get(input$columnvar)), position = "dodge", stat="count") +
geom_text(aes( label = scales::percent(..prop..),
y= ..prop.. ), stat= "count", vjust = -.5) +
labs(y = "Percent", fill=input$rowvar) +
scale_y_continuous(labels=percent,limits = c(0,1))
})
})
shinyApp(ui = ui, server = server)
If you see the problem is -
All bars are 100%. Proportions are not getting calculated properly. Not sure where I am going wrong.
If I try to use the group parameter it gives me error saying "input" variable not found. I tried giving group as group = get(input$columnvar)
I believe I need to restructure my data for line chart. Can you help with how I can dynamically restructure the data.table and then re-use for the line chart. How can I generate the same bar chart as a line chart.
I am using renderplotly so that I use the features of plotly to have the percentages displayed with the mouse movement / zoom etc. However I can see input$variable on mouse movement. How can I get rid of it and have proper names.
Have tried to detail out the situation. Do suggest some solution.
Thank you!!
To properly group variables for plotting, geom_bar requires that the x values be numeric and the fill values be factors or that the argument group be used to explicitly specify grouping variables. However, plotly throws an error when group is used. The approach below converts x variables to integer and fill variables to factor so that they are properly grouped. This retains the use of geom_bar to calculate the percentages.
First, however, I wonder if mydata is specified correctly. Given that the data is a mix of character and integer, cbind(Location, Brand, Year, Q1, Q2) gives a character matrix which is then converted to a data.table where all variables are character mode. In the code below, I've defined mydata directly as a data.table but have converted Q1 to character mode so that mydata contains a mix of character and numeric.
The approach used below is to create a new data frame, plotdata, containing the x and fill data. The x data is converted to numeric, if necessary, by first making it a factor variable and then using unclass to get the factor integer codes. The fill data converted to a factor. plotdata is then used generate the ggplot plot which is then displayed using plotly. The code includes a couple of other modifications to improve the appearance of the chart.
EDIT
The code below has been updated to show the name of the row variable beneath it's bar. Also the percentage and count for each bar are only shown when the mouse pointer hovers above the bar.
library("shiny")
library("ggplot2")
library("scales")
library(plotly)
library(data.table)
Location <- sample(1:5,100,replace = T)
Brand <- sample(1:3,100,replace = T)
Year <- rep(c("Year 2014","Year 2015"),50)
Q1 <- sample(1:5,100,replace = T)
Q2 <- sample(1:5,100,replace = T)
Q3 <- sample(seq(1,3,.5), 100, replace=T)
mydata <- data.table(Location,Brand,Year,Q1,Q2, Q3)
#
# convert Q1 to character for demonstation purposes
#
mydata$Q1 <- as.character(mydata$Q1)
ui <- shinyUI(fluidPage(
sidebarPanel(
fluidRow(
column(10,
div(style = "font-size: 13px;", selectInput("rowvar", label = "Select Row Variable",
choices=colnames(mydata)))),
tags$br(),
tags$br(),
column(10,
div(style = "font-size: 13px;", selectInput("columnvar", label="Select Column Variable",
choices=colnames(mydata))))
)
),
tabPanel("First Page"),
mainPanel(tabsetPanel(id='charts',
tabPanel("charts",tags$b(tags$br("Graphical Output" )),tags$br(),plotlyOutput("plot1"))
)
)
))
server <- shinyServer(function(input, output,session){
updateTabsetPanel(session = session
,inputId = 'myTabs')
observe({
updateSelectInput(session, "rowvar", choices = colnames(mydata), selected=colnames(mydata)[1])
})
observe({
updateSelectInput(session, "columnvar", choices = colnames(mydata), selected=colnames(mydata)[2])
})
output$plot1 <- renderPlotly({
#
# create data frame for plotting containing x variables as integer and fill variables as factors
#
if(is.numeric(get(input$rowvar))) {
rowvar_brks <- sort(unique(get(input$rowvar)))
rowvar_lbls <- as.character(rowvar_brks)
plotdata <- data.frame(get(input$rowvar), factor(get(input$columnvar)) )
}
else {
rowvar_factors <- factor(get(input$rowvar))
rowvar_brks <- 1:nlevels(rowvar_factors)
rowvar_lbls <- levels(rowvar_factors)
plotdata <- data.frame(unclass(rowvar_factors), factor(get(input$columnvar)) )
}
colnames(plotdata) <- c(input$rowvar, input$columnvar)
validate(need(input$rowvar,''),
need(input$columnvar,''))
col_width <- .85*mean(diff(rowvar_brks))
sp <- ggplot(plotdata, aes_(x = as.name(input$rowvar), fill = as.name(input$columnvar))) +
geom_bar( aes(y= ..prop..), stat="count", position=position_dodge(width=col_width)) +
geom_text(aes( label = paste(scales::percent(..prop..),"<br>", "count:",..count..,"<br>"), y= ..prop.. + .01),
stat= "count", position=position_dodge(width=col_width), size=3, alpha=0) +
labs(x= input$rowvar, y = "Percent", fill=input$columnvar) +
scale_y_continuous(labels=percent) +
scale_x_continuous(breaks=rowvar_brks, labels=rowvar_lbls)
ggplotly(sp, tooltip="none")
})
})
shinyApp(ui = ui, server = server)