I'm really new to using RShiny and I'm struggling to add a Checkbox. There are a few stages to the task I've been given but I'm currently stuck on this step. My aim currently is to create a webapp which displays a boxplot graph with the x-axis (categorical) and y-axis (numerical) variables being dependent on a selectInput. So far I've gotten this to work but now I need to add a checkbox such that when it is clicked on, the boxplots split up into separate plots by categorical level. I've tried a few variations of code but I'm not really getting anywhere and the best I've come up with I think is the following:
library(shiny)
library(palmerpenguins)
library(dplyr)
library(ggplot2)
data <- penguins[,-2]
colnames(data) <- c("Species",
"Bill Length (mm)",
"Bill Depth (mm)",
"Flipper Length (mm)",
"Body Mass (g)",
"Sex",
"Year")
data.numeric <- data[, c(2:5, 7)]
data.categorical <- data[, c(1,6)]
ui <- fluidPage(
headerPanel("Penguin boxplots"),
selectInput("ycol",
"Numeric Variable",
names(data.numeric),
selected = names(data.numeric)[3]),
selectInput("xcol",
"Categorical Variable",
names(data.categorical),
selected = names(data.categorical)[2]),
checkboxInput("split",
"Split Levels",
value = FALSE),
mainPanel(
plotOutput("plot1")
))
server <- function(input, output){
reactive({ if (input$split){
output$plot1 <- renderPlot({
selectedData <- reactive({
data[, c(input$xcol, input$ycol)]
})
plot(selectedData())
})
} else {
output$plot1 <- renderPlot({
par(mar = c(20, 4.1, 0, 1))
selectedData <- reactive({
data[, c(input$xcol, input$ycol)]
})
plot(selectedData())
})
}
})
}
shinyApp(ui = ui, server = server)
as you'll see, the code here isn't intended to actually split the graphs up, it's just there for me to be able to test the code until I can get the if and else statements working - then i'll facet the plots appropriately. So really, my issue is getting the if and else statement to work.
Any help would be really appreciated! Thanks folks
You can try something like this for server.
I would recommend avoiding nesting output and reactive expressions inside other reactive expressions, and duplicating output$plot1.
Instead, you can simplify, and include an if/else inside your renderPlot to show different visualizations depending on checkbox. I added a bit to demonstrate differences in plotting with different checkbox states.
Certainly, additional reactive expressions could be helpful, depending on what else you may want to include in your app.
Edit: Replaced plot with ggplot geom_boxplot and uses facet_wrap.
server <- function(input, output){
output$plot1 <- renderPlot({
selectedData <- data[, c(input$xcol, input$ycol)]
if (input$split) {
ggplot(data = selectedData, aes(y = .data[[input$ycol]])) +
geom_boxplot() +
facet_wrap(~.data[[input$xcol]])
} else {
ggplot(data = selectedData, aes(x = .data[[input$xcol]], y = .data[[input$ycol]])) +
geom_boxplot()
}
})
}
Related
I have a scatterplot in a shiny dashbaord and would like to generate two different tables by selecting/highlighting different areas of the scatterplot. I am currently able to generate a single table by selecting/highlighting an area, however am not sure how to make this work for two tables/selections (or if that is even possible).
Any help or advice would be greatly appreciated. Thankyou
Sample code to generate a shiny dashboard with a scatterplot and highlight/generate a single table is provided below (and was taken from here)
Some more detail : Ideally this process would be achieved by manually selecting/dragging an area over some points, generating the first table and then manually selecting/dragging an area over a different subset of points and generating the second table. After this, if another area is selected, it resets the first selection and table and then the next selection would reset the second selection and table.
ui <- fluidPage(
plotOutput("plot", brush = "plot_brush"),
tableOutput("data")
)
server <- function(input, output, session) {
output$plot <- renderPlot({
ggplot(mtcars, aes(wt, mpg)) + geom_point()
}, res = 96)
output$data <- renderTable({
brushedPoints(mtcars, input$plot_brush)
})
}
shinyApp(ui=ui, server=server)
Maybe this might be helpful. You can track which table (1 or 2) in reactiveValues as well as the data for each table. Let me know if this is what you had in mind. If you wanted to maintain the previous selection in the plot, I would think you may need to manually place a rectangle. A github issue allowing for multiple selections of brushed points is an open issue (enhancement). Alternatively, you could tag points for each table based on this approach.
library(shiny)
ui <- fluidPage(
plotOutput("plot", brush = "plot_brush"),
h2("Table 1"),
tableOutput("data1"),
h2("Table 2"),
tableOutput("data2")
)
server <- function(input, output, session) {
rv <- reactiveValues(table = 1,
data1 = NULL,
data2 = NULL)
output$plot <- renderPlot({
ggplot(mtcars, aes(wt, mpg)) + geom_point()
}, res = 96)
my_data <- eventReactive(input$plot_brush, {
if (rv$table == 1) {
rv$table <- 2
rv$data1 <- input$plot_brush
} else {
rv$table <- 1
rv$data2 <- input$plot_brush
}
return(rv)
})
output$data1 <- renderTable({
brushedPoints(mtcars, my_data()$data1)
})
output$data2 <- renderTable({
brushedPoints(mtcars, my_data()$data2)
})
}
shinyApp(ui=ui, server=server)
I wasted hours to find out why my plot is automatically updating itself when I change inputs while it was supposed to wait for the Run button but it simply ignored that step and I ended up finally finding ggplot as the trouble maker!!! This is my minimal code:
library(ggplot2)
library(tidyverse)
varnames <- names(cars)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(
column(
width = 12,
# Variables Inputs:
varSelectInput("variables", "Select Input Variables", cars, multiple = TRUE),
selectizeInput("outvar", "Select Output Variable", choices = varnames, "speed", multiple = F),
# Run Button
actionButton(inputId = "run", label = "Run")
)
)
),
# Main panel for displaying outputs ----
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output, session) {
df <- reactive({
cars %>% dplyr::select(!!!input$variables, input$outvar)
})
plt <- eventReactive(input$run, {
#Just creating lm formula
current_formula <- paste0(input$outvar, " ~ ", paste0(input$variables, collapse = " + "))
current_formula <- as.formula(current_formula)
#Fitting lm
fit <- lm(current_formula, data = df())
pred <- predict(fit, newdata = df())
#Plotting
ggplot(df(), aes(df()[, input$outvar], pred)) +
labs(x = "Observed", y = "Predicted") +
geom_point() +
theme_bw()
#plot(df()[, input$outvar], pred) #This one works fine!!!!
})
output$plot <- renderPlot({
plt()
})
}
# Run the application
shinyApp(ui = ui, server = server)
If you run this, you'll notice that ggplot doesn't care anymore about the Run button after the 1st run and it keeps updating as you change the inputs!! However, if you use the simple base plot function (which I put in a comment in the code) there wouldn't be any problems and that works just fine! Sadly I need ggplot in my app because base plot is ugly. I am seeing suggestion for using isolate() to solve this issue but I have no clue where isolate() should be put to fix my problem also it doesn't make sense to use isolate() when base plot function works fine without it and it's the ggplot that makes the problem. Any explanation would be appreciated.
The issue is that ggplot aesthetics are lazy evaluated. You really want to put symbols into the aes() rather that reactive data values. Change your plotting code to
ggplot(df(), aes(.data[[input$outvar]], pred)) +
labs(x = "Observed", y = "Predicted") +
geom_point() +
theme_bw()
With ggplot you use the .data pronoun to access the current data source rather than trigger the reactive df() object again.
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'm just trying to create a basic shiny app that allows the user to view simple plots for a selected variable and apply filters as required.
However, I am having difficulty getting the plot to work with the filters applied. I can get the plot to work if I don't apply the filters, but the current code gives the error: "Error: no applicable method for 'filter_' applied to an object of class "character"".
I realise the problem is to do with what is trying to be plotted and probably something to do with the filter applied to "input$Variable", but I cannot work it out! Any help appreciated!
Here is my code:
library(shiny)
data <- read.csv("df1Final.csv", stringsAsFactors = T)
ui <- fluidPage(
titlePanel("PhD Data"),
sidebarLayout(
sidebarPanel(
uiOutput("Variable"),
sliderInput("Age.Yr", "Age:", 8, 12, c(8,12), step = 0.25),
sliderInput("Train.Hr", "Training (Hours):", 9, 11, c(9,11), step = 0.1),
sliderInput("Grade", "Grade:", 3, 5, c(3,5), step = 1)
),
mainPanel(plotOutput("coolplot"),
br(), br(),
verbatimTextOutput("results")
)
)
)
server <- function(input, output) {
output$Variable <- renderUI({
selectInput("Variable", "Data:", choices = colnames(data)[-1])
})
filtered <- reactive({
if (is.null(input$Variable)) {
return(NULL)
}
input$Variable %>%
filter(Age.Yr >= input$Age.Yr[1],
Age.Yr <= input$Age.Yr[2],
Train.Hr >= input$Train.Hr[1],
Train.Hr <= input$Train.Hr[2],
Grade >= input$Grade[1],
Grade <= input$Grade[2]
)
})
output$coolplot <- renderPlot({
if (is.null(filtered())) {
return()
}
ggplot(filtered(), aes(x=filtered(), color=Group)) +
geom_density()
})
output$results <- renderPrint({
describeBy(data[,input$Variable], data$Group, mat = F)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I think the problem is you are filtering on the actual value that Shiny passes you and not on your data. Input$Variable is not a set of data, it is just the title of that column of data.
Try printing out input$Variable before acting on it.
So I have been able to get this to work by adjusting the filtering to be:
data[input$Variable] %>% ...
I then had to also adjust the aes in ggplot to be:
aes_string(x=input$Variable)
Still trying to work out how to add the color = Group to the ggplot at the moment as this now doesn't work.
For a Shiny program I'm writing, I have input variables that contain a dash, commas and brackets. Spaces I can substitute out but the rest are needed since they are refering to chemical compounds and don't make sense without them. As expected, these characters make the Shiny app unable to find the desired variable; whilst variables with none of these characters work fine.
EDITED: The code below is a test Shiny app. With Chemical-X(a,b) the app returns "could not find function X". With Chemical.B the app returns "object Chemical.B not found" which is the desired result since the app sees the chemical as an object and not some function that doesn't exist.
library (shiny)
library (ggplot2)
dat <- as.data.frame(c("Chemical-X(a,b)", "Chemical.B"))
dat[,2] <- (c(6,3))
colnames(dat) <- c("Chemical", "Count")
ui <- fluidPage(
titlePanel("SE Test"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "varX",
label = "Chemical",
choices = dat[,1],
width = "200px"),
selectInput(inputId = "varY1",
label = "Count",
choices = dat[,2],
width = "200px")
),
mainPanel(
plotOutput("chemPlot")
)
)
)
server <- function(input, output){
output$chemPlot <- renderPlot({
plot.data <- ggplot(data = dat)
point <- plot.data + geom_point(
aes_string(x = input$varX, y = input$varY1))
plot(point)
})
}
shinyApp(ui = ui, server = server)
Is there a known way of doing this or will I need to come up with some viable work around? I have tried using backticks as suggested here but this hasn't worked.
Thanks, Matt
I have found that backticks and aes_string usually works for me.
library("ggplot2")
my_dodgy_var <- "name with~special character"
mtcars[[my_dodgy_var]] <- mtcars$cyl
ggplot(mtcars, aes_string(x=paste0("`", my_dodgy_var, "`"), y="mpg")) +
geom_point()
I often use a helper function paste_aes to do this, eg:
paste_aes <- function(x) paste0("`", x, "`")
I've fixed it now by calling as.name the Shiny input$ variable. For the example above it would look like this.
server <- function(input, output){
output$chemPlot <- renderPlot({
plot.data <- ggplot(data = dat)
point <- plot.data + geom_point(
aes_string(x = as.name(input$varX), y = as.name(input$varY1)))
plot(point)
This appears to work now as intended. Thank you aocall for your efforts.