Seeking explanation on renderPlot in Shiny - r

I have a trivial question after playing with this code
library(gapminder)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
checkboxInput("fit", "Add line of best fit", FALSE),
selectInput("continents", "Continents",
choices = levels(gapminder$continent),
multiple = TRUE,
selected = 'Europe')
),
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output) {
output$plot <- renderPlot({
data <- subset(gapminder,
continent %in% input$continents)
p <- ggplot(data, aes(gdpPercap, lifeExp)) +
geom_point() +
scale_x_log10()
if (input$fit) {
p <- p + geom_smooth(method = "lm")
}
p
})
}
shinyApp(ui = ui, server = server)
If I change part of the code above to this
if (input$fit) {
p + geom_smooth(method = "lm")
}
# p
})
}
The plot won't be displayed. Why do we have to be too verbose, that is, passing p + geom_smooth() to p, then calling p for it to work? Why can't we be more succinct by simply calling p + geom_smooth()? Is it because the code is wrapped inside renderPlot()?
Thankyou.

Based on your if statement, the plot is returned and then rendered in case input$fit is TRUE. If input$fit is FALSE, the if statement returns NULL and therefore no plot is rendered. This is the default behaviour of if() in R.
You can solve your problem by simply add else p to your if statement like
if (input$fit) p + geom_smooth(method = "lm") else p

Related

I would like to change a ggplot entirely to another one through a SelectInput

Image Currently I have a selectInput with two options which I want to link to their own ggplots entirely. What I currently have is only seeming to show the second ggplot when its Input in the drop down is selected, but the first plot for "Ordered_Product_Sales" is not showing. Wondering where I am going wrong whether it is in the IF statements or just my structure entirely. I am very new (couple of days) to R and R shiny so be kind.
library(shiny)
library(plotly)
ui <- fluidPage(
# Application title
titlePanel("Metric Tracker"),
sidebarLayout(
sidebarPanel(
selectInput("value", "Select Value" , choices = c("Ordered_Product_Sales", "Units_Ordered"), selected = NULL , multiple = FALSE, selectize = TRUE)
),
# Show plot
mainPanel(
plotlyOutput("valuePlot"),
plotlyOutput("value2Plot")
)
)
)
# Define server
server <- function(input, output) {
output$valuePlot <- renderPlotly({
if (input$value == "Ordered_Product_Sales")
metric2 %>%
ggplot(aes(x=Date, y = Ordered_Product_Sales, title = "Sales")) + geom_point() + geom_line( ) + geom_smooth(method = 'lm') + facet_wrap( ~ Brand)
output$value2Plot <- renderPlotly({
if (input$value == "Units_Ordered")
metric2 %>%
ggplot(aes(x=Date, y = Units_Ordered)) + geom_point() + geom_line() + geom_smooth(method = 'lm') + facet_wrap( ~ Brand)
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
My Data is called metric2 where the ggplots are plotting the date against either the sales column or units column.
Thanks
Please try the below simplified version of your code. You might need to provide some dummy data if this still doesn't work.
library(shiny)
library(plotly)
ui <- fluidPage(
# Application title
titlePanel("Metric Tracker"),
sidebarLayout(
sidebarPanel(
selectInput("value", "Select Value" , choices = c("Ordered_Product_Sales", "Units_Ordered"), selected = NULL , multiple = FALSE, selectize = TRUE)
),
# Show plot
mainPanel(
plotlyOutput("valuePlot")
)
)
)
# Define server
server <- function(input, output) {
output$valuePlot <- renderPlotly({
if (input$value == "Ordered_Product_Sales") {
metric2 %>%
ggplot(aes(x=Date, y = Ordered_Product_Sales)) + geom_point() + geom_line( ) + geom_smooth(method = 'lm') + facet_wrap( ~ Brand) + ggtitle("Sales")
}
if (input$value == "Units_Ordered") {
metric2 %>%
ggplot(aes(x=Date, y = Units_Ordered)) + geom_point() + geom_line() + geom_smooth(method = 'lm') + facet_wrap( ~ Brand)
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
Update
Based on the image of the data, I created a small sample set:
metric2 = data.frame(Date = c(as.POSIXct('2019-07-06'), as.POSIXct('2019-07-07'), as.POSIXct('2019-07-08'), as.POSIXct('2019-07-06'), as.POSIXct('2019-07-06')), Brand = c('A', 'B', 'A', 'C', 'B'), Units_Ordered = c(710, 253, 185, 107, 727), Ordered_Product_Sales = c(6551, 7226, 9007, 6003, 5964), stringsAsFactors = FALSE)
And then updated the app as follows:
library(shiny)
library(plotly)
ui <- fluidPage(
# Application title
titlePanel("Metric Tracker"),
sidebarLayout(
sidebarPanel(
selectInput("value", "Select Value" , choices = c("Ordered_Product_Sales", "Units_Ordered"), selected = NULL , multiple = FALSE, selectize = TRUE)
),
# Show plot
mainPanel(
plotlyOutput("valuePlot")
)
)
)
# Define server
server <- function(input, output) {
output$valuePlot <- renderPlotly({
if (input$value == "Ordered_Product_Sales") {
metric2_plot <- metric2 %>%
ggplot(aes(x=Date, y = Ordered_Product_Sales)) + geom_point() + geom_line( ) + geom_smooth(method = 'lm') + facet_wrap( ~ Brand) + ggtitle("Sales")
}
if (input$value == "Units_Ordered") {
metric2_plot <- metric2 %>%
ggplot(aes(x=Date, y = Units_Ordered)) + geom_point() + geom_line() + geom_smooth(method = 'lm') + facet_wrap( ~ Brand)
}
metric2_plot
})
}
# Run the application
shinyApp(ui = ui, server = server)
The key is that renderPlotly() returns the last object created/value returned within it (as do all renders and reactives). The second if statement comes after the ggplot creation and, as it's FALSE, it returns NULL which can't be plotted. (There is an implicit ...else{NULL} after an if statement.) So your options are either the above where I created metric2_plot and then explicitly return it at the end of the renderPlotly()or to use an if...else so that the ggplot creation occurs after the only if statement is evaluated as below:
output$valuePlot <- renderPlotly({
if (input$value == "Ordered_Product_Sales") {
metric2 %>%
ggplot(aes(x=Date, y = Ordered_Product_Sales)) + geom_point() + geom_line( ) + geom_smooth(method = 'lm') + facet_wrap( ~ Brand) + ggtitle("Sales")
}
else {
metric2 %>%
ggplot(aes(x=Date, y = Units_Ordered)) + geom_point() + geom_line() + geom_smooth(method = 'lm') + facet_wrap( ~ Brand)
}
})
Finally, please replace , title = "Sales" in aes with + ggtitle("Sales").

Add trendline data to Shiny Plotly on hover

I'm creating a plot using ggplotly() and I'd like the Spearman's rank correlation [I'm storing here as the reactive value rho] to appear when hovering over the line created with geom_smooth. I found an article on the plotly website for doing this in python (https://plot.ly/python/linear-fits/) but I'm unsure how to achieve this in R. Any help or leads appreciated!!
library(shiny)
library(plotly)
ui <- fluidPage(
mainPanel(plotlyOutput("line_plot"),
verbatimTextOutput("rho"))
)
# Define server logic required to draw a histogram
server <- function(input, output) {
# calculate rho to be printed over line
rho <- reactive({ cor.test(x = iris$Sepal.Length, y = iris$Sepal.Width, method='spearman')[[4]] })
output$rho <- renderText(paste0("rho: ", rho()))
output$line_plot <- renderPlotly({
p <- ggplotly(
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
# would I add the tooltip text here?
# I know I need it to be:
# "<b> rho: </b>", rho(), "<br/>",
geom_smooth(method=lm, se=FALSE, color = "red") +
theme_minimal()
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
You can add the unoffical aesthetics text:
library(shiny)
library(plotly)
ui <- fluidPage(
mainPanel(plotlyOutput("line_plot"),
verbatimTextOutput("rho"))
)
# Define server logic required to draw a histogram
server <- function(input, output) {
# calculate rho to be printed over line
rho <- reactive({ cor.test(x = iris$Sepal.Length, y = iris$Sepal.Width, method='spearman')[[4]] })
output$rho <- renderText(paste0("rho: ", rho()))
output$line_plot <- renderPlotly({
p <- ggplotly(
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
geom_smooth(method=lm, se=FALSE, color = "red", aes(text = paste0("<b> rho: </b>", rho()))) +
theme_minimal()
)
})
}
# Run the application
shinyApp(ui = ui, server = server)

Error: Evaluation error: argument of length 0. R Shiny 1:nrow

My file is one set of game data and PitchNo tells the pitch of the game. I am trying to generate plots of each batter's pitches seen in a game and I have labeled the PitchNo in the ggplot function. I want my plot for each batter to label the pitches seen, say 1-10, but instead it is still labeling them as the PitchNo of the game.
I added Batter$PitchNo <- 1:nrow(Batter) in my reactive filter. That line of code works in normal R but I cannot seem to find a way to make in work in a Shiny app.
library(shiny)
library(tidyverse)
GameDataFile <- read_csv("GameDataFile.csv")
GameDataFile %>% group_by(PitchNo)
playerlist <- sort(unique(GameDataFile$Batter))
ui = fluidPage(
titlePanel("Title"),
sidebarLayout(
sidebarPanel(
selectInput(
inputId="Batter1",
label="Player:",
choices = playerlist)
),
mainPanel(
plotOutput("myZone")
)))
server = function(input, output){
output$myZone <- renderPlot({
GameDataFile$PlateLocSide <- (GameDataFile$PlateLocSide * -1)
newdata <- reactive({
GameDataFile %>% filter(
Batter %in% c(input$Batter1),
Batter$PitchNo <- 1:nrow(Batter)) # Error is here
})
ggplot(data = newdata(), aes(x = PlateLocSide, y = PlateLocHeight)) +
xlim(-2.5,2.5) + ylim(0,5) + scale_fill_viridis_c() + geom_point() +
labs(x = "", y = "") + facet_wrap(~ Batter, ncol = 2) +
geom_text(aes(label = PitchNo), vjust = -0.5) + theme_bw() +
theme(strip.text = element_text(size=20, face="bold"))
}, width=400, height=500)
}
shinyApp(ui = ui, server = server)

R-shiny: Discrete value supplied to continuous scale

So I am using mpg dataset to practice my R-shiny skills, but I encountered a problem.
I want to write a app which I could choose different variables to make graph, if it involves at least one discrete variable, then I draw a geom_boxplot, else, I will just draw a geom_point.
My ui.R looks like this:
library(shiny)
shinyUI(fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput(inputId = "var1",
label = "Choose x variable",
choices =
names(mpg)
),
selectInput(inputId = "var2",
label = "Choose y variable",
choices =
names(mpg))
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
))
And
My server.R looks like this:
library(shiny)
library(tidyverse)
library(dplyr)
shinyServer(function(input, output) {
output$distPlot <- renderPlot({
if(typeof(mpg[, input$var1]) == "chr" ||
typeof(mpg[, input$var2]) == "chr")
{
ggplot(data = mpg) +
geom_boxplot(mapping =
aes(x = mpg[, input$var1],
y = mpg[, input$var2])) +
xlab(input$var1) +
ylab(input$var2) +
ggtitle(paste("Plot", input$var1, "vs", input$var2))
}
else
{
ggplot(data = mpg) +
geom_point(mapping =
aes(x = mpg[, input$var1],
y = mpg[, input$var2])) +
xlab(input$var1) +
ylab(input$var2) +
ggtitle(paste("Plot", input$var1, "vs", input$var2))
}
})
})
But it simply does not work! When I choose discrete variable, it will return me an error message says "Discrete value applied to continuous scale". However, if I both choose continuous value, it seems to be work fine.
Why it shows such an error message like that?
Please help me!
Thank you very much!
You have a couple of issues here all on the server side:
One: If you check an example of your typeof you'll see that you always get a list:
typeof(mpg[,"model"])
#[1] "list"
typeof(mpg[,"displ"])
#[1] "list"
This is because [ doesn't extract the actual element, but the list containing that element. From ?"[":
Indexing by [ is similar to atomic vectors and selects a list of the specified element(s).
Rather you should use typeof(mpg[[input$var1]]), etc because you want to extract the element of the list (not the list that contains the element).
Two:
There is actually a specific function in ggplot, aes_string which selects the columns to plot based on a string.
Incorporating these two changes should make your shiny app work. I also simplified you server a bit to get rid of the common ggplot code.
server <- function(input,output){
output$distPlot <- renderPlot({
p <- ggplot(mpg) + xlab(input$var1) +
ylab(input$var2) +
ggtitle(paste("Plot", input$var1, "vs", input$var2))
if(typeof(mpg[[input$var1]]) == "character" |
typeof(mpg[[input$var2]]) == "character")
{
p <- p + geom_boxplot(mapping =
aes_string(x = input$var1,
y = input$var2))
}
else
{
p <- p + geom_point(mapping =
aes_string(x = input$var1,
y = input$var2))
}
return(p)
})
}

ggplotly no applicable method for 'plotly_build' applied to an object of class "NULL" if statements

working on integrating plotly into shiny dashboard and ran into an error when using conditional statements. I'm trying to switch through charts using an 'if' statement with selectInput. I've used this with the circlize package and ggplot graphs without issue but when trying to use it with plotly I get the following error:
Error in UseMethod: no applicable method for 'plotly_build' applied to an object of class "NULL"
I found a posting here with similar issues, but not exactly answering my specific question:
Convert ggplot object to plotly in shiny application
Here is a sample using a similar code to the one used in the posting above but with modifications to show what I'm looking to do and the error that keeps popping up:
library(shiny)
library(ggplot2)
library(ggthemes)
library(plotly)
ui = dashboardPage(
dashboardHeader(title = 'sample'),
dashboardSidebar(), ##Body content dashboardBody(
fluidRow(
box(background = "green", selectInput(inputId = "dimension",
label = strong("Choose Metric"),
choices = c('choice' = '1', 'choice2' = '2'),
multiple = FALSE, selectize = TRUE)),
box(plotlyOutput(outputId = 'plot2')))
))
server < - function(input, output) {
output$plot2 < -renderPlotly({
print(
ggplotly(
ggplot(data = mtcars, aes(x = disp, y = cyl)) + geom_smooth(method =
lm, formula = y~x) + geom_point() + theme_gdocs()))
if (input$dimension == '2') {
print(
ggplotly(
ggplot(data = mtcars, aes(x = hp, y = cyl)) + geom_smooth(method =
lm, formula = y~x) + geom_point() + theme_gdocs()))
}
})
}
shinyApp(ui, server)
I'm still learning so I'm sure this a simple error that escapes me but I am unsure what it might be. Appreciate the help!
Shortly, the problem was that if input$dimension was '1' there was no return value. You were printing the plot out but R went then one step further and checked if the condition was met. There are few ways to code it correctly.
You can save the plot in the object, say res, and if the condition is met, overwrite it with a new plot and finally return it at the end of the function - see the example below. You could also use the else statement.
library(shiny)
library(shinydashboard)
library(ggplot2)
library(ggthemes)
library(plotly)
ui = dashboardPage(
dashboardHeader(title = 'sample') ,
dashboardSidebar(),
## Body content
dashboardBody(
fluidRow(
box(background="green", selectInput(inputId = "dimension",
label = strong("Choose Metric"),
choices = c('choice'='1','choice2'='2'),
multiple = FALSE, selectize = TRUE)),
box(plotlyOutput(outputId = 'plot2')))
))
server <- function(input, output) {
output$plot2 <- renderPlotly({
res <- ggplotly(
ggplot(data = mtcars, aes(x = disp, y = cyl)) +
geom_smooth(method = lm, formula = y ~ x) +
geom_point() +
theme_gdocs())
if (input$dimension == '2') {
res <- ggplotly(
ggplot(data = mtcars, aes(x = hp, y = cyl)) +
geom_smooth(method = lm, formula = y ~ x) +
geom_point() +
theme_gdocs())
}
res
})
}
shinyApp(ui, server)

Resources