I'm working on a school project where I have a few dyplr queries where I need to make them interactive using the Shiny library. Now I have a query in dyplr where i can see the Revenue for every country from a Indian takeaway restaurant. I have the following query:
```
df %>%
group_by(Origin, Jaar = year(Order.Date), Maand = month(Order.Date, label = TRUE), Kwartaal = quarter(Order.Date)) %>%
summarize(omzet = sum(regelomzet)) %>%
ggplot(aes(reorder(Origin, omzet), omzet)) +
facet_grid(~Jaar) +
geom_col() +
ggtitle("Omzet per land van herkomst") +
coord_flip() +
theme(axis.text.x = element_text(angle = 90))
```
This gives me the following output:
Now if I change the facet_grid to for example kwartaal (quarters in a year) I get the following output:
Now I would like to apply this plot in a interactive Shiny app. i use the following code for this:
```
```{r}
library(shiny)
ui <- fluidPage(
titlePanel("Indian takeaway"),
navlistPanel("Kies een plot",
tabPanel("Omzet per land van herkomst",
selectInput(inputId = "Select_unit",
label = "Selecteer op basis van wat je de grafiek wilt zien",
choices = c("Jaar", "Kwartaal", "Maand"),
selected = "Jaar"
),
plotOutput(outputId = "plot3")
)
)
)
server <- function(input, output, session) {
output$plot3 <- renderPlot({
df %>%
group_by(Origin, Jaar = year(Order.Date), Maand = month(Order.Date, label = TRUE), Kwartaal = quarter(Order.Date)) %>%
summarize(omzet = sum(regelomzet)) %>%
ggplot(aes(reorder(Origin, omzet), omzet)) +
facet_grid(~input$Select_unit) +
geom_col() +
ggtitle("Omzet per land van herkomst") +
coord_flip() +
theme(axis.text.x = element_text(angle = 90))
})
}
shinyApp(ui, server)
```
Here I make a slicer and show the plot on the Shiny app. To change the facet_wrap I use the input function that is linked to the input box now if I change the variable in the input box I would expect it to show the graph like in the above two pictures.
Now if I start the shiny app you can see that there isn't any facet_grid in the first place not even on the variable that is standard. My question is why does it do this because I did include it in my query. Because I have no clue I don't know how I could fix this and already did google this but couldn't find anything helpfull
I found the following information on another stack overflow post:
R Shiny: Issue with facet_wrap in ggplot
I use facet_grid(~input$Select_unit), using the following line of code the problem will be sovled
facet_grid(~get(input$Select_unit))
With the introduction of the .data pronoun, the best way to turn a string into a variable for ggplot would be with .data[[variable]]. Also we can use the non-formula syntax for facet_grid which allows specifying rows= and cols= as vars() lists. For example
facet_grid(cols=vars(.data[[input$Select_unit]]))
Related
I am learning how to use Shiny, and I tried to create a very simple barchart in ggplot2, with a dropdown menu, that allows the user to select a class from a school using the dropdown, and it is supposed to create a barchart with exam result percentages on the y-axis and names on the x-axis. The code I have is as follows:
ui = fluidPage(selectInput(inputId = "Class", label = "Pick a Class", choices = levels(fulldata$Class), plotOutput("bar"), multiple = FALSE, selectize = FALSE))
server = function(input, output){
output$bar = renderPlot({
plotdata = reactive({data %>% filter(Class == input$Class)})
ggplot(plotdata(), aes(x = Name, y = Percent_full) + geom_bar())
})
}
shinyApp(ui = ui, server = server)
The end result correctly renders the dropdown menu, but it does not render the plot whatsoever. I have tried changing the ggplot call to a simple hist(rnorm(1000)) but it does not render either.
I solved the problem: the plotOutput function in the fluidPage function was defined as an argument of the input function, not as an argument of fluidPage. It works now!
It might be that your code needs to declare the reactive data before you create your ggplot.
Try this:
plotdata = reactive(
data %>% filter(Class == input$Class)
)
output$bar = renderPlot({
ggplot(plotdata(), aes(x = Name, y = Percent_full) + geom_bar())
})
Here is more example code from a functioning shiny app using reactive data for ggplot:
data <- reactive(
merged_clean_data %>% filter(between(date, as.POSIXct(input$dateRange[1]),
as.POSIXct(input$dateRange[2])))
)
#Output plot for any selected variable
output$timePlot <- renderPlot({
ggplot(data(), aes(x = date, !!input$selection)) +
theme_classic() + geom_line() +
coord_cartesian(xlim = as.POSIXct(ranges$x, origin = "1970-01-01"), expand = FALSE) +
theme(text = element_text(size = 16), axis.title.x=element_blank(), axis.text.y = element_text(angle=90, vjust=1, hjust=1)) + {if(input$hlineadd)geom_hline(yintercept = input$hline)} +
{if(input$smoothingadd)geom_smooth()}
}, res = 80)
I am trying to combine two things into 1 violin plot.
In my Shiny app user can choose variable he want to have in violin plot (X variable).
As a Y variable I have age which can have range chosen by user:
There is no problem if I just pick a variable and stay with full range of income.
However, when I want to have a different range I get an error.
To show the problem more visible:
- this works ok, I can change the X variable and it is fine with the full range of income
if I change the income range I get error
I think that the problem appears because the X variable has different range.
I don't know how and where to change it to make it work.
If I drop the X interactivity it works:
renderPlot({
data %>%
filter(
between(Age, input$income[1], input$income[2])) %>%
ggplot(aes(x=Sex, y=Age)) +
geom_violin(aes(fill=Sex), trim=FALSE) +
geom_boxplot(width=0.3)+
stat_summary(fun=mean, geom="point", shape=20, size=5)+
ggtitle(input$var)})
But I want it to stay interactive. Do you have any solution for that?
Here is a code with titanic dataset showing the same problem which you can copy-paste:
---
title: "dataset"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
runtime: shiny
---
```{r global, include=FALSE}
#dataset, libraries and other global things
library(flexdashboard)
library(ggplot2)
library(tidyverse)
library(dplyr)
library(titanic)
data("titanic_train")
data<-na.omit(titanic_train)
```
Dashboard {data-orientation=rows}
=====================================
Inputs {.sidebar}
-------------------------------------
**Age by:**
```{r plot-option}
selectInput("var", label = "Please choose variable:",
choices = names(subset(data, select=c(Sex ,Pclass, SibSp))))
sliderInput("income", HTML("Income interval:"),
min = min(data$Age), max = max(data$Age), value = c(min(data$Age), max(data$Age)), step =1)
```
Row
-------------------------------------
### Age by: {data-width=450}
```{r}
selected <- reactive({ data[, c(input$var)] })
renderPlot({
data %>%
filter(
between(Age, input$income[1], input$income[2])) %>%
ggplot(aes(x=selected(), y=Age)) +
geom_violin(aes(fill=input$var), trim=FALSE) +
geom_boxplot(width=0.3)+
stat_summary(fun=mean, geom="point", shape=20, size=5)+
ggtitle(input$var)})
```
As #Ben says; there is a difference in the number of rows selected. I'm somewhat new with Shiny, but why do you need to subset the data in the reactive part? It's not needed (you filter it later on) and it's less efficient since you're effectively subsetting your data every time you change the selected variable (granted, not a big problem in a small dataset, but still..)
So I suggest:
renderPlot({
data %>%
filter(
between(MonthlyIncome, input$income[1], input$income[2])) %>%
ggplot(aes_string(x= input$var, y=data$MonthlyIncome)) +
geom_violin(aes(fill=input$var), trim=FALSE) +
geom_boxplot(width=0.3)+
stat_summary(fun=mean, geom="point", shape=20, size=5)+
ggtitle(input$var)})
Define UI for application
ui <- fluidPage(
# Application title
titlePanel("Iris"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput("Variables","Choose a Variable",
choices = c(colnames(iris)),
selected = "Species"),
sliderInput("PetalLength", "Length Interval",
min = 0.1, max = 10, value = c(0.5, 7.5), step = 0.1)
)
,
# Show a plot of the generated distribution
mainPanel(
plotOutput("irisPlot")
)
)
)
Define server logic
server <- function(input, output) {
data(iris)
output$irisPlot <- renderPlot({
iris_data <- filter(iris, between(Petal.Length, input$PetalLength[1], input$PetalLength[2]))
ggplot(iris_data, aes_string(x= input$Variables, y=iris_data$Petal.Length)) +
geom_violin(aes(fill=input$Variables), trim=FALSE) +
geom_boxplot(width=0.3)+
stat_summary(fun=mean, geom="point", shape=20, size=5)+
ggtitle(input$Variables)})
}
Run the application
shinyApp(ui = ui, server = server)
library(tidyverse)
library(ggplot2)
library(plotly)
data(mpg)
ggplotly(
mpg %>%
ggplot(aes(x=hwy)) +
geom_histogram(),
tooltip = ("all"))
When you hover over the bar, I'd like for the tooltip to show the start and stop of the bin (e.g. 20-21)
Thanks for the simple plot_ly answer. For other reasons, I'd like to preserve ggplot. Here's one possible solution I came up with that extracts the histogram elements from ggbuild_plot() and plots them as a bar graph.
ggplotly(
ggplot_build(
mpg %>%
ggplot(aes(x=hwy)) +
geom_histogram()
)$data[[1]] %>%
ggplot(aes(x=factor(x), y = count, text = paste0("range: ",round(xmin, 1), " - ", round(xmax,1)))) +
geom_bar(stat="identity") +
theme(axis.text.x = element_blank()),
tooltip = c("text"))
In case if it's not mandatory to use ggplot2, an easier fix is to use basic histogram plot:
plot_ly(x = mpg$hwy, type = "histogram")
I ran into this issue but also needed to label the x-axis with bin ranges, so I built on your answer (which was great!)
I broke it down into three steps: using ggplot to create the first histogram that generates the bin ranges, using ggplot again to create the second histogram that uses those ranges for labels, and then using plotly to make it interactive.
Here's a reprex that should be customizable for other use cases. Once you get the gist you can ditch the intermediate variables and run the whole thing at once with pipes.
library(tidyverse)
library(plotly)
# step 1: create a ggplot histogram, extract the internal data
plot_step1 <- ggplot_build(
mpg %>%
ggplot() +
geom_histogram(aes(x=hwy),
bins = 11 # set histogram parameters here
)
)$data[[1]]
# step 2: create a new plot, using the derived xmin and xmax values from the
# first plot, and set the labels and axes
plot_step2 <- plot_step1 %>% {
ggplot(data = .,
aes(x=factor(x),
y = count,
text = sprintf("Count: %d\nRange (MPG): %.1f-%.1f", y, round(xmin,1), round(xmax,1)))) +
scale_x_discrete(labels = sprintf("%.1f-%.1f", .$xmin, .$xmax)) +
geom_bar(stat="identity",
width = 1) +
labs(title = "Histogram: Highway Miles per Gallon",
x = "MPG",
y = "Count") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45 ))
}
# step 3: make this new plot interactive
plotly::ggplotly(plot_step2, tooltip = c("text"))
A solution using library(ggiraph):
library(tidyverse)
library(ggplot2)
library(ggiraph)
p1 <- mpg %>%
ggplot(., aes(x=hwy)) +
geom_histogram_interactive(bins = 20, aes(tooltip = paste0("[",round(..xmin..,2),",",round(..xmax..,2),"] count: ",..count..)))
ggiraph(ggobj = p1)
Example
I am trying to make a shiny app that plots a custom ggplot plot1 depending on the different dates.
The parameter of plot1 is days which plots by different dates as the days are specified accordingly. However, I just can't figure out how to integrate into the server part of shiny. Here is my best approach:
# create sample data frame with dates
set.seed(1)
date = seq(Sys.Date(), by = "day", length.out = 30)
number = 100 * rnorm(30)
df = data.frame(date = date, number = number)
head(df)
# Plot
library(ggplot2)
library(shiny)
library(dplyr)
plot1 <- function(days) {
df %>% filter(between(date, max(df$date) - days, max(df$date))) %>%
ggplot(aes(x = date, y = number)) +
geom_line() +
theme_classic()
}
# Shiny
ui <- fluidPage(
dateRangeInput(
inputId = "daterange",
label = "Select the date range",
start = min(df$date),
end = max(df$date),
),
plotOutput("plotA")
)
server <- function(input, output, session) {
output$plotA <- renderPlot({
plot1(input$daterange)
})
}
shinyApp(ui, server)
Is filter a masked function for you? You can try using a qualified function call to filter via dplyr::filter().
Other than that, as the other user pointed out, input$daterange is a vector with 2 values - a "beginning" (input$daterange[1]) and an "end" (input$daterange[2]). You mentioned that you tried changing input$daterange[1] or [2], but it doesn't work: I presume that means you changed your call to plot(input$daterange) in server, right? You should be changing and specifying that up in your declaration of that function. Since you define the date from input$daterange based on min(df$date) and max(df$date) already, you can just use days[1] and days[2] to refer to the min and max of the user input. Maybe like this?
plot1 <- function(days) {
df %>% dplyr::filter(between(date, days[1], days[2])) %>%
ggplot(aes(x = date, y = number)) +
geom_line() +
theme_classic()
}
Finally, I have had some issues displaying plots from ggplot in shiny apps myself. In order to show the plot, I store in a variable, and then explicity show the plot with print(). Example:
myPlot <- ggplot(df, aes(x=..., y=...)) + geoms_...
print(myPlot)
If I simply call ggplot without the print() after, it does not always work as intended.
I have a dataset with home values for all 50 states over ~30 years. Columns include State, Year, Value, etc. I am trying to create an interactive Shiny app where the user can select certain states so they will be the only ones displayed in the plot. I have successfully created the plot of all states independently where Year is on the x-axis and Value is on the y-axis, colored by State, and also have successfully subset the dataset so that only one state plots.
I am new to Shiny and having issues having anything other than the Input checkBox feature work in this. Is there something obvious I am missing?
ui <- fluidPage(
checkboxGroupInput(inputId = "state", label = "States", choices = levels(AllData2$STATE),
plotOutput(outputId = "hist")))
server <- function(input, output) {
output$hist <- renderPlot({
plot(data = subset(AllData2, AllData2 == input$state), mapping = aes(x = Date, y = HomeValue,
color = STATE) + geom_line(size=2, alpha=0.8) +
scale_y_continuous(breaks = seq(0, 1000000, 50000)) +
scale_x_continuous(breaks = seq(1975, 2020, 5)) +
ylab("Value in Dollars") + xlab("Year"))
})
}
shinyApp(ui = ui, server = server)
I get no output in my Shiny App except the checkbox options. Thank you for any help.
There are only syntax errors in your code. Many of them:
You have included plotOutput() inside the checkbox group, please place it outside it.
Use ggplot() instead of plot()
You have included everything inside plot() If you use ggplot() the syntax is: ggplot(data=AllData,mapping=aes())+geom_line()+scale_y_continuous()+scale_x_continuous()+labs(x="This is X label",y="This is ylab",color="This is the color legend label")
Your code will work after fixing these problems
Just copy paste this if you want instant result:
library(shiny)
library(ggplot2)
library(dplyr)
ui <- fluidPage(
column(12,checkboxGroupInput(inputId = "state", label = "States", choices = c(1,2,3,4,5))),
column(12,plotOutput(outputId = "hist")))
server <- function(input, output) {
output$hist <- renderPlot({
ggplot(data = subset(AllData2, AllData2$STATE %in% input$state), mapping = aes(x = Date, y = HomeValue,
color = STATE)) + geom_line(size=2, alpha=0.8) +
scale_y_continuous(breaks = seq(0, 1000000, 50000)) +
scale_x_continuous(breaks = seq(1975, 2020, 5)) +labs(x="Value in Dollars",y="Year")
})
}
shinyApp(ui = ui, server = server)