I have an R Shiny app which plots horsepower from the mtcars data set against a user selected x variable. I would like the user to be able to select a transformation to perform on the x-variable ahead of plotting. In the simplified example below, these transformations are the ability to square it or obtain its reciprocal. I am doing this using a switch function.
While this switching function works in non-shiny contexts, I can't get it to execute inside a working shiny app. I know that I could perform the transformation on a reactive copy of the data frame and plot from that, but if possible, I would like to perform the transformation in the ggplot call itself. Does anyone have any suggestions on how to do so?
library(shiny)
library(tidyverse)
tran_func <- function(pred, trans) {
switch(trans,
"None" = pred,
"Reciprocal" = 1/pred,
"Squared" = pred^2,
)
}
ui <- fluidPage(
selectInput("xvar",
"Select X Variable",
choices = names(mtcars),
selected = "disp"),
selectInput("transform",
"Select Transformation",
choices = c("None", "Reciprocal", "Squared"),
selected = "None"),
plotOutput("scatter_good"),
plotOutput("scatter_bad")
)
server <- function(input, output, session) {
output$scatter_good <- renderPlot({
mtcars %>%
ggplot(aes_string(x = "hp", y = input$xvar)) +
geom_point()
})
output$scatter_bad <- renderPlot({
mtcars %>%
ggplot(aes_string(x = "hp", y = tran_func(input$xvar, "Squared"))) +
geom_point()
})
}
shinyApp(ui, server)
The issue would be the evaluation of the string passed from input$xvar to modify the column. An option is to pass the 'data' also as argument in the function, and use [[ to subset the column without converting to symbol or evaluate
library(shiny)
library(ggplot2)
library(dplyr)
tran_func <- function(data, pred, trans) {
switch(trans,
"None" = data[[pred]],
"Reciprocal" = 1/data[[pred]],
"Squared" = data[[pred]]^2,
)
}
ui <- fluidPage(
selectInput("xvar",
"Select X Variable",
choices = names(mtcars),
selected = "disp"),
selectInput("transform",
"Select Transformation",
choices = c("None", "Reciprocal", "Squared"),
selected = "None"),
plotOutput("scatter_good"),
plotOutput("scatter_bad")
)
server <- function(input, output, session) {
output$scatter_good <- renderPlot({
mtcars %>%
mutate(y_col = tran_func(cur_data(), input$xvar, input$transform)) %>%
ggplot(aes(x = hp, y = y_col)) +
geom_point()
})
output$scatter_bad <- renderPlot({
mtcars %>%
mutate(y_col = tran_func(cur_data(), input$xvar, input$transform)) %>%
ggplot(aes(x = hp, y =y_col)) +
geom_point()
})
}
shinyApp(ui, server)
-output
Related
To avoid multiple lines with basically the same code, i want to map over multiple (two in this case) vectors to render multiple (two in this case) plots with my custom plot function.
What am i missing with my code?
library(shiny)
library(ggplot2)
if (interactive()) {
options(device.ask.default = FALSE)
ui = fluidPage(
plotOutput(outputId = "plotOne"),
plotOutput(outputId = "plotTwo"))
server = function(input, output, session){
###define dataset filter vector
vars = c("virginica", "setosa")
###define outputId vector
outputIds = c("plotOne", "plotTwo")
###define plotting function
plot_function = function(vars, outputIds){
output$outputIds = renderPlot({
iris %>%
filter(Species == vars) %>%
ggplot(aes(x = Sepal.Length)) +
geom_histogram()
})
}
map2(.x = vars, .y = outputIds, .f = plot_function)
}
shinyApp(ui, server)
}
Instead of creating multiple ggplot's why not use facets here?
library(shiny)
library(ggplot2)
ui = fluidPage(
plotOutput(outputId = "plot"),
)
server = function(input, output, session){
###define dataset filter vector
vars = c("virginica", "setosa")
output$plot = renderPlot({
iris %>%
filter(Species %in% vars) %>%
ggplot(aes(x = Sepal.Length)) +
geom_histogram() +
facet_wrap(~Species)
})
}
shinyApp(ui, server)
I am building a shiny application with several tabs, each tab takes a user input (unique(data_in$cat), and generates some type of graph. The problem occurs in the second tab--for some reason, it does not generate the graph that is specified by data2. The first graph on the first tab is being displayed correctly.I see no error when I run this code, so I don't know where to start debugging!
library(shiny)
library(openxlsx)
library(ggplot2)
data_in <- read.xlsx("www/dem_data_clean.xlsx")
ui <- navbarPage(title = "Data",
tabPanel(title = "Over-all trends",
plotOutput("Histall"),
selectInput("Indall","Demographic Variable of Interest",choices = unique(data_in$cat))
),
tabPanel(title = "2017-2018"),
plotOutput("Hist17"),
selectInput("Ind17","Demographic Variable of Interest",choices = unique(data_in$cat))
)
server <- function(input, output, session) {
data1 <- reactive({
a <- subset(data_in,cat==input$Indall)
return(a)
})
data2 <- reactive({
a <- subset(data_in,cat==input$Ind17)
return(a)
})
output$Histall <- renderPlot({
ggplot(data1(), aes(x=Year,y=value, group =name, color=name)) + geom_line(stat = "identity") +
ylab("Percent of Population")
})
output$Hist17 <- renderPlot({
data2() %>%
filter(Year=="2017-18") %>%
ggplot(aes(name, value)) + geom_bar(stat = "identity")
})
}
shinyApp(ui, server)
Any suggestions to what I am doing wrong? I've tried playing around with different things for a few hours now to no avail!
The UI code is not correct, second plotOutput and selectInput are not within second tabPanel. It works if you fix it :
ui <- navbarPage(title = "Data",
tabPanel(title = "Over-all trends",
plotOutput("Histall"),
selectInput("Indall",
"Demographic Variable of Interest",
choices = unique(data_in$cat))
),
tabPanel(title = "2017-2018",
plotOutput("Hist17"),
selectInput("Ind17",
"Demographic Variable of Interest",
choices = unique(data_in$cat)))
)
The ggplot just shows a vertical line of values that doesn't change when I try changing the x axis selection. The only thing on the x axis is the word "column" when I try to change the x axis, instead of the values of df$column according to what's selected.
df_variable <- df
df_colnames <- colnames(df)
xaxis_input <- selectInput(
inputId = "xaxis",
label = "Feature of Interest",
choices = df_colnames,
selected = df_colnames['default']
)
ui <- fluidPage(
titlePanel("DF"),
xaxis_input,
plotOutput(
outputId = "df_plot",
)
)
server <- function(input, output) {
output$df_plot <- renderPlot({
plot <- ggplot(data = df) +
geom_point(aes(x = input$xaxis, y = some_other_col))
return(plot)
})
}
input$xaxis is a string, so you cannot use it directly inside aes().
Try using aes_string() instead.
Note that some_other_col should also be a string.
server <- function(input, output) {
output$df_plot <- renderPlot({
plot <- ggplot(data = df) +
geom_point(aes_string(x = input$xaxis, y = "some_other_col"))
return(plot)
})
A full working example:
library(shiny)
library(ggplot2)
df <- iris
df_colnames <- colnames(df)
xaxis_input <- selectInput(
inputId = "xaxis",
label = "Feature of Interest",
choices = df_colnames
)
ui <- fluidPage(
titlePanel("DF"),
xaxis_input,
plotOutput(
outputId = "df_plot",
)
)
server <- function(input, output) {
output$df_plot <- renderPlot({
plot <- ggplot(data = df) +
geom_point(aes_string(x = input$xaxis, y = "Sepal.Width"))
return(plot)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I'm new to R. I have a large dataset that I want the user to be able to select the x values plotted on a graph. To make it easier, I've done the same thing using the mpg dataset:
library(shiny)
ui <- fluidPage(
selectInput(
inputId= "manuf",
label= "Manufacturer",
choices= mpg$manufacturer,
multiple= TRUE
),
plotOutput("graph1")
)
server <- function(input, output) {
output$graph1 <- renderPlot({
ggplot() +
geom_point (
mapping = aes (
x= input$manuf,
y= ???
)
)
})
}
shinyApp(ui = ui, server = server)
I can't for the life of me figure out what the correct syntax is for the 'y' input. I have been googling my heart out and can't figure it out, and I'm sure it's relatively simple. I want it to only output the data for whatever you've selected in the drop down.
putting in y= mpg$hwy shows ALL hwy datapoints when one manufacturer is selected and throws an error ("Aesthetics must be either length 1 or the same as the data") with more. I think the errors are self-explanatory, but that doesn't help me figure out the correct code for 'y'. Ideas? Thanks in advance.
The aesthetic mappings for ggplot (like aes(x = ...)) should be column names, but you aren't giving the user a choice of column names, you give the user the choice of manufacturer values---which correspond to rows. If you want the user to select certain rows to plot based on the manufacturer, you should subset/filter the data that you give to ggplot, perhaps like this:
library(shiny)
library(ggplot2)
ui <- fluidPage(
selectInput(
inputId = "manuf",
label = "Manufacturer",
choices = mpg$manufacturer,
multiple = TRUE
),
plotOutput("graph1")
)
server <- function(input, output) {
output$graph1 <- renderPlot({
ggplot(data = mpg[mpg$manufacturer %in% input$manuf, ]) +
geom_point (
mapping = aes (
x = manufacturer,
y = hwy
)
)
})
}
shinyApp(ui = ui, server = server)
Let's forget about Shiny for a moment and focus on how you would filter a dataset for plotting with ggplot(). The tidyverse approach is to use dplyr::filter:
library(dplyr)
library(ggplot2)
mpg %>%
filter(manufacturer == "audi") %>%
ggplot(aes(manufacturer, hwy)) +
geom_point()
So your server function would look something like this (untested):
server <- function(input, output) {
output$graph1 <- renderPlot({
mpg %>%
filter(manufacturer == input$manuf) %>%
ggplot(aes(manufacturer, hwy)) +
geom_point()
)}
}
I am challenged trying to implement a feature into my Shiny app. The problem is two-fold:
Is it possible to have 2 inputs from the same variable? I have one variable that is a list of indicators. I want the user to be able to select 2 indicators with selectInput, and then draw a scatter plot. There has to be 2 selectInputs because other parts of the app will rely on only the first selectInput. My data is long. I don't think it will work if I make it wide because my data includes latitude and longitude information so it wouldn't make sense to create a selectInput with names(data), for example.
If I can have 2 selectInputs from the same variable, how would I call the values in my plot, since the value is called 'value' for both the inputs?
EDIT: Following Gregor's suggestion to reference the inputs with aes_string, I would expect the following example of mtcars gathered into long format to work, but I instead get an aesthetics or object not found error. I think I probably need to filter the data, but I don't understand how I can do that since my variable indicators now refers to both 'indicators' and 'indicators2'. I.e., I can't have
filtered <-
cars %>%
filter(indicators == input$indicators,
indicators == input$indicators2)
Maybe I need to create a reactive expression that creates a new data frame instead? This is my non-working reproducible code with long-form mtcars:
library(ggplot2)
library(shiny)
cars <- mtcars %>%
gather(indicators, value, mpg:carb)
ui <- fluidPage(
# Application title
titlePanel("mtcars"),
sidebarLayout(
sidebarPanel(
selectInput("indicators",
label = "select indicator:",
choices = c("mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb")
),
selectInput("indicators2",
label = "select indicator:",
choices = c("mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb")
)
),
mainPanel(
plotOutput("carsPlot")
)
)
)
server <- function(input, output) {
output$carsPlot <- renderPlot({
ggplot(cars, aes_string(x = input$indicators, y = input$indicators2)) +
geom_point(shape = 1)
})
}
# Run the application
shinyApp(ui = ui, server = server)
You can use aes_string to pass input$indicators and input$indicators2 to ggplot like this. There is no need to cast your data into wide format since ggplot can actually handle long data better.
library(ggplot2)
library(shiny)
ui <- fluidPage(
# Application title
titlePanel("mtcars"),
sidebarLayout(
sidebarPanel(
selectInput("indicators",
label = "select indicator:",
choices = names(mtcars)),
selectInput("indicators2",
label = "select indicator:",
choices = names(mtcars))
),
mainPanel(
plotOutput("carsPlot")
)
)
)
server <- function(input, output) {
output$carsPlot <- renderPlot({
ggplot(mtcars, aes_string(x = input$indicators, y = input$indicators2)) +
geom_point(shape = 1)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Here another solution with a selectInput in multiple mode.
library(dplyr)
library(tidyr)
library(ggplot2)
library(shiny)
cars <- mtcars %>%
gather(indicators, value, mpg:carb)
ui <- fluidPage(
titlePanel("mtcars"),
sidebarLayout(
sidebarPanel(
uiOutput("ui_indicators")
),
mainPanel(
plotOutput("carsPlot")
)
)
)
server <- function(input, output) {
output$ui_indicators <- renderUI({
choices <- unique(cars$indicators)
selectInput("indicators",
label = "select indicators :",
choices = choices,
multiple = TRUE)
})
output$carsPlot <- renderPlot({
filtered <- cars %>% filter(indicators %in% input$indicators)
ggplot(filtered, aes(x = indicators, y = value)) +
geom_point(shape=1)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Building on the tip from Gregor on aes_string, I managed to fix this. I ended up using the wide data and adding a proper reactive statement that creates a new data frame out of the selected indicators.
My server function now looks like this:
server <- function(input, output) {
selectedVars <- reactive({
cars[, c(input$indicators, input$indicators2)]
})
output$carsPlot <- renderPlot({
ggplot(selectedVars(), aes_string(x = input$indicators, y = input$indicators2)) +
geom_point(shape = 1)
})
}
All works beautifully, and I am beginning to learn more about the utility of reactive functions in Shiny :)
Gregor de Cillia provided the answer I was looking for.
The two inputs (which are not a problem) can be referenced using aes_string.