I am trying to implement a slider in a very simple Shiny application. The main idea is to change the values with the slider and see the visualized result in Chart 2. Below you can see my code
---
title: "Test App"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
library(shiny)
library(tidyverse)
# Data Set 1
df<-data.frame( cyl=c("4","6","8"),
Multiplier=c(2,4,6))
# Data Set 2
df1 <- mtcars
df1$cyl <- as.factor(df1$cyl)
```
Column {.sidebar}
-----------------------------------------------------------------------
```{r}
selectInput("clusterNum",
label = h4("Charts"),
choices = list("Chart1" = "Chart1", "Chart2" = "Chart2"),
selected = "Chart1"
)
# Sidebar to demonstrate various slider options ----
sidebarPanel(
# Input: Simple integer interval ----
sliderInput("integer", "Integer:",
min = 0, max = 8,
value = 1),)
```
Column {data-width=650}
-----------------------------------------------------------------------
### Chart
```{r}
# First chart
Chart1 <- ggplot(df1, aes(x = wt, y = mpg)) +
geom_point()
# Second chart
Chart2_df1<-df1%>%
dplyr::left_join(df,df1,by = c("cyl"="cyl"))
Chart2_df1<-Chart2_df1%>%
dplyr::mutate(mpg_new=(mpg*Multiplier))
Chart2 <- ggplot(Chart2_df1, aes(x = wt, y = mpg_new)) + geom_point()
# Visualization of the selected chart
renderPlot({
switch(input$clusterNum,
"Chart1" = Chart1,
"Chart2" = Chart2
)
})
```
With the values from the slider, I want to change the value in df for column Multiplier. These values, after changing, are part of the formula of the second chart for multiplying with the value from df1, with column mpg. After this operation, the next step is showing result on chart 2.
So can anybody help me how to implement this similar as picture below ?
In order for the plot to be reactive to an input, we need it to be within reactive or processing within the renderPlot (which is reactive in nature).
One way to do this is to make Chart2 a reactive plot, and then "call" it with Chart2() (the way to get at reactive data/plots):
# First chart
Chart1 <- ggplot(df1, aes(x = wt, y = mpg)) +
geom_point()
# Second chart
Chart2 <- reactive({
dplyr::left_join(df, df1, by = c("cyl" = "cyl")) %>%
dplyr::mutate(mpg_new = (mpg * Multiplier * input$integer)) %>%
ggplot(aes(x = wt, y = mpg_new)) +
geom_point()
})
# Visualization of the selected chart
renderPlot({
switch(input$clusterNum,
"Chart1" = Chart1,
"Chart2" = Chart2()
)
})
Note that Chart1 is unmodified, and since it is not a reactive component, we continue to reference it as Chart1 (no ()) just as we would any other regular R object. Since Chart2 is a shiny-reactive object, though, we need the () to get at the updated value.
If you will be doing something with this data in addition to plotting it, one might choose to make the altered data available as one reactive component and then use it in the other(s).
# Second chart data
Chart2_dat <- reactive({
dplyr::left_join(df, df1, by = c("cyl" = "cyl")) %>%
dplyr::mutate(mpg_new = (mpg * Multiplier * input$integer))
})
# Second chart
Chart2 <- reactive({
Chart2_dat() %>%
ggplot(aes(x = wt, y = mpg_new)) +
geom_point()
})
# Visualization of the selected chart
renderPlot({
switch(input$clusterNum,
"Chart1" = Chart1,
"Chart2" = Chart2()
)
})
and any other components (e.g., tables, additional plots) can also use Chart2_dat() to see the joined/updated data.
Related
I am trying to implement an option with a drop-down menu in a simple Shiny application. With this drop-down list, you can choose between two charts. The first chart is prepared with the ggplot2 library, while the second is with the Plotly library. Below you can see my code :
---
title: "Test App"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
library(shiny)
library(tidyverse)
library(plotly)
# Data
data <- mtcars
data$cyl <- as.factor(data$cyl)
```
Column {.sidebar}
-----------------------------------------------------------------------
```{r}
selectInput("clusterNum",
label = h4("Charts"),
choices = list("Chart1" = "Chart1", "Chart2" = "Chart2"),
selected = "Chart1"
)
```
Column {data-width=650}
-----------------------------------------------------------------------
### Chart
```{r}
Chart1 <- ggplot(data, aes(x = wt, y = mpg)) +
geom_point()
Chart2 <- plot_ly(data, x = ~wt, y = ~mpg, type = 'bar')
renderPlot({
switch(input$clusterNum,
"Chart1" = Chart1,
"Chart2" = Chart2
)
})
```
After executing this code, I saw that Chart 1, which was prepared with ggplot2, works well, while Chart 2 with Plotly is not displayed. So can anybody help me how to solve this problem and to see Chart 2 after selection with the drop-down list?
To render the plotly chart you have to use renderPlotly, while for the ggplot we have to stick with renderPlot. As a consequence switching conditionally between the two render functions requires some more effort and involves wrapping in renderUI and displaying the chart via uiOutput:
---
title: "Test App"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
library(shiny)
library(tidyverse)
library(plotly)
# Data
data <- mtcars
data$cyl <- as.factor(data$cyl)
```
Column {.sidebar}
-----------------------------------------------------------------------
```{r}
selectInput("clusterNum",
label = h4("Charts"),
choices = list("Chart1" = "Chart1", "Chart2" = "Chart2"),
selected = "Chart1"
)
```
Column {data-width=650}
-----------------------------------------------------------------------
### Chart
```{r}
Chart1 <- ggplot(data, aes(x = wt, y = mpg)) +
geom_point()
Chart2 <- plot_ly(data, x = ~wt, y = ~mpg, type = 'bar')
```
```{r}
observeEvent(input$clusterNum, {
output$plot <- switch(input$clusterNum,
"Chart1" = renderUI({renderPlot(Chart1)}),
"Chart2" = renderUI({renderPlotly(Chart2)})
)
})
uiOutput("plot")
```
I am trying to format my ggplotly tooltip in a percent stacked barchart. In order to do this, I am editing a 'text' parameter in 'aes'
library(scales)
library(tidyverse)
library(plotly)
library(ggplot2)
library(shiny)
#Define dataframe
weeknum <- c(1,1,1,1,2,2,2,2,3,3,3,3)
channel <- rep(c("a", "b"), 6)
product <- c(rep("x",6), rep("y",6))
data <- data.frame(weeknum, channel, product)
# Define UI
ui <- fluidPage(theme = shinytheme("flatly"),
mainPanel(
h1("plot"),
plotlyOutput(outputId = "plot_1", height = "800px")
))
# Define server function
server <- function(input, output) {
output$plot_1 <- renderPlotly({
p1 <- data %>%
ggplot(aes(x=weeknum, fill=channel, text = paste('share:', percent(..count..), "<br> channel", fill))) +
geom_bar(position = "fill", stat ='count')+
facet_grid(rows = vars(product))
fig1 <- ggplotly(p1, tooltip = "text")
fig1
})
}
# Create Shiny object
shinyApp(ui = ui, server = server)
so here I got only count * 100% in the tooltip. I know I need to divide it by a dynamic height of a bar, because in this dashboard I'm gonna use some filters. Question is how can I do this? (..count..)/sum(..count..) doesn't work.
Since you did not provide a reproducible example, I created one using mtcars data set. This is how I would approach your task.
library(ggplot2)
library(dplyr)
plot <- mtcars %>%
count(cyl, am, name = "count") %>%
mutate(across(c(cyl, am), as.character)) %>%
ggplot(
aes(x = cyl, fill= am, y = count,
text = paste('share:', scales::percent(count/sum(count)), '<br>AM:', am)
)
) +
geom_col(position = "fill")
plotly::ggplotly(plot, tooltip = "text")
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)
I'm creating a simple line chart which renders correctly in Shiny.
I've now added a selectInput with the names of 2 different measures, written as they appear in my data set. I'd like my y variable to change accordingly.
p <- plot_ly(data = LineChartData(), x= Calendar.Month, y = input$Measure, type = "line", group = Calendar.Year, col = Calendar.Year)
Unfortunately, the chart renders with just one point. It's not taking input$Measure and finding that field in my data set.
I know when using ggplot, i'd switch my aes to aes_string. Is there a similar solution in plotly?
EDIT: here's some reproducible code
Here's the ui.R file
#ui.R
shinyUI(
fluidPage(
titlePanel("Inbound Intermediary Performance"),
sidebarLayout(
sidebarPanel(
h4("Parameters"),
br(),
selectInput("Measure", "Measure", c("Var1","Var2"))
),
mainPanel(
plotlyOutput("lineChart")
)
)
)
)
server.R
#server.R
library(plotly)
library(shiny)
library(ggplot2)
#Create data
data <- data.frame(Month = c(1,2,3,4,5,6,7,8,9,10,11,12), Var1 = c(36,33,30,27,24,21,18,15,12,9,6,3), Var2 = c(4,8,12,16,20,24,28,32,36,40,44,48))
shinyServer(function(input, output) {
#Create plot
output$lineChart <- renderPlotly({
#using ggplot
p <- ggplot(data=data, aes_string(x='Month', y = input$Measure)) +geom_line(size = 1.5) + theme_minimal()
ggplotly(p)
#Using PLotly
#p <- plot_ly(data = data, x= Month, y = input$Measure, type = "line")
})
})
In the example above, I can use my drop down to switch between Var1 and Var2. My plot changes accordingly. The code uses ggplot and it's aes_string function to take an input. This is then converted into a plotly interactive plot using the ggplotly function.
Is there a way I can do this natively with plotly?
Use base::get() function:
p <- plot_ly(data = data, x = ~Month, y = ~get(input$Measure), type = "line")
or the same using ggplot:
p <- ggplot(data = data, aes(x = Month, y = get(input$Measure))) +
geom_line(size = 1.5) +
theme_minimal()
ggplotly(p)
Simply just use data[ ,input$Measure] as Your y variable:
p <- plot_ly(data = data, x= Month, y = data[ ,input$Measure], type = "line")
Just discovering shiny apps but this is driving me insane.......I have looked at numerous examples of server.R and ui.R code and cannot figure out what I am doing wrong. Apologies in advance if it's something very basic..........
Taking the iris dataset as an example, I want to plot one column against another, something simple using qplot or preferably ggplot
However, using qplot I get this:
and using ggplot2, I get the error:
I don't think I need the reactive function as I'm not subsetting the dataset, just extracting columns to plot.
server.R code
library(shiny)
library(shinyapps)
library(ggplot2)
shinyServer(function(input, output, session) {
output$text1 <- renderText({input$id1})
output$text2 <- renderText({input$select1})
output$plot1 <- renderPlot({
g <- qplot(Sepal.Length, input$select1, data = iris)
print(g)
})
})
or using ggplot function to replace the qplot call
g <- ggplot(iris, aes(x = Sepal.Length, y = input$select1))
g <- g + geom_line(col = "green", lwd =1) +
labs(x = "Date", y = "Ranking") +
theme_bw() + scale_y_reverse()
ui.R code
library(shiny)
library(shinyapps)
data(iris)
opts <- unique(colnames(iris))
opts <- opts[-1] ## want to keep Sepal.Length as the x values
shinyUI(pageWithSidebar(
headerPanel('test with iris database'),
sidebarPanel(
selectInput(inputId = "select1", label = "select",
choices = opts),
textInput(inputId = "id1", label = "Input Text", "")
),
mainPanel(
p('Output text1'),
textOutput('text1'),
textOutput('text2'),
plotOutput('plot1')
)
))
Change your aes statement to aes_string and make x a string. This should fix the problem.
g <- ggplot(iris, aes_string(x = "Sepal.Length", y = input$select1))
g <- g + geom_line(col = "green", lwd =1) +
labs(x = "Date", y = "Ranking") +
theme_bw() + scale_y_reverse()