I'm trying to create an reactive plot where you can select an ethnicity in a selectInput() and see the population of that ethnicity in the midwest.
This is my ui:
ethnicity_sidebar_content <- sidebarPanel(
selectInput(
inputId = "ethnicity",
label = "Select Ethnicity",
choices = list(
"Total" = "total",
"White" = "white",
"Black" = "black",
"American Indian" = "amerindian",
"Asian" = "asian",
"Other" = "other"
)
)
)
ethnicity_main_content <- mainPanel(
plotOutput("ethnicity_plot")
)
ethnicity_panel <- tabPanel(
"Midwest by Ethnicity",
sidebarLayout(
ethnicity_sidebar_content,
ethnicity_main_content
)
)
This is my server:
midwest_poverty <- midwest %>%
mutate(popbelowpoverty = floor(percbelowpoverty / 100 * poppovertyknown)) %>%
group_by(state) %>%
summarise(
poppovertyknown = sum(poppovertyknown),
popbelowpoverty = sum(popbelowpoverty)
) %>%
mutate(popabovepoverty = poppovertyknown - popbelowpoverty)
server <- function(input, output) {
output$ethnicity_plot <- renderPlot({
p <- ggplot(data = midwest_ethnicity) +
geom_bar(
mapping = aes(x = state, y = input$ethnicity),
stat = "identity"
)
p
})
}
When I run shinyApp, I keep getting a bar plot that graphs the column name rather than the data in the column.
Edit: I think this was a simple mistake where I was using aes instead of aes_string
When you write aes(x = state, y = input$ethnicity) in the ggplot call, it will look for variable state in the dataset midwest_ethnicity for x-axis. Same for y, it will look for a variable named White for instance if this is the value in input$ethnicity.
I don't think there is a variable with such a name in your dataset.
If it is the case (White is a variable of your dataset), it could not work if ggplot don't consider input$ethnicity as a string, and not as a value. You can test y = get(input$ethnicity).
Another option as proposed in comments, is to use aes_string() instead of aes().
Related
I'm using flexdashboard and shiny to choose which variable to plot:
varSelectInput("button_var_fir"
, "Select first num variable"
, data = df_scat,
multiple = FALSE
)
ggplot(df_scat, aes(x = !!input$button_var_fir, y = Gen_type, fill = stat(x))) +
geom_point(size= 3, alpha = .075)
it works fine, so far. My problem is, that I would like to subset the data e.g via
df$variable > 0
ggplot(df_scat, aes(x = df$!!input$button_var_fir > 0, y = Gen_type, fill = stat(x))) +
geom_point(size= 3, alpha = .075)
but this doesn't work due to the $!!. How can I solve this?
In {ggplot2}, which uses tidy evaluation, you can use the .data pronoun to dynamically select variables. It's nicely explained outside the {shiny} context in this answer too. This doesn't apply in {plotly} so you can either select with x = data[[input$column]] or x = get(input$column). This is also explained in this question.
Here's a small example to demonstrate how to do this for each plotting function.
library(plotly)
library(tidyverse)
library(shiny)
nbins <- 10
ui <- fluidPage(titlePanel("Dynamic Variable Selection"),
sidebarLayout(sidebarPanel(
selectInput(
inputId = "y1",
label = "Select variable",
choices = names(mtcars))),
mainPanel(plotOutput(outputId = "ggplot"),
plotlyOutput(outputId = "plotly"))))
server <- function(input, output) {
# dynamically pull variable in ggplot
output$ggplot <- renderPlot({
mtcars %>%
ggplot(aes(x = .data[[input$y1]])) +
geom_histogram(bins = nbins) +
ggtitle("ggplot")})
# dynamically pull variable in plotly
output$plotly <- renderPlotly({
mtcars %>%
plot_ly(x = .[[input$y1]], type = "histogram", nbinsx = nbins) %>%
layout(title = list(text = "Plotly"),
xaxis = list(title = input$y1))
})
}
shinyApp(ui = ui, server = server)
Maybe what you want is
df[[input$button_var_fir]] > 0
instead of df$!!input$button_var_fir > 0.
Addition:
You want to subset the data that goes into the plot, right? What I would actually do is subsetting the dataframe itself before it goes into the plot function. When you use the tidyverse this could be what you want:
df_scat %>%
filter(!!input$button_var_fir > 0) %>%
ggplot(aes(x = !!input$button_var_fir, y = Gen_type, fill = stat(x))) +
geom_point(size= 3, alpha = .075)
I'm prototyping a dashboard (R Shiny) to create density plots that can be customized to graph BMI of Hernia patients by different categories, namely Gender, Race, Ethnicity, and Smoker. The categories Gender, Ethnicity, and Smoker all seem to be working perfectly fine, however attempting to plot BMI by Race fails when I run the app and use the drop down menus to select the category 'Race'. Instead, I receive the message, "Error: 'x' and 'units' must have length > 0", which is mystifying. Other posts with the similar error message on Stack Overflow suggest coercing the racial categories to lower case, which I attempted (i.e., df$Race = tolower(df$Race), but that didn't seem to help.
Categories of Race include:
White or Caucasian
Native Hawaiian or Other Pacific Islander
Other
Black or African American
Unknown
Asian
Patient Refused
American Indian or Alaska Native
Of these, it appears that our data includes only 1 patient who identifies as 'American Indian or Alaska Native'.
Below you will find the code I've written for 'app.R', and the code I use to plot the data.
First, app.R:
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
library(DBI)
library(dplyr)
library(DT)
library(tibble)
source("connect.R")
source("density.R")
con = connect()
myquery = as_tibble(dbGetQuery(con,
"SELECT
pat.lngPatientID PatID,
pat.lngMRN MRN,
pat.strFirstName FirstName,
pat.strLastName LastName,
pat.dteBirth DOB,
pat.lngHeight Height,
pat.lngWeight Weight,
pat.lngBMI BMI,
tpg.strValue Gender,
tpr.strValue Race,
eth.strValue Ethnicity,
tss.strValue Smoker
FROM tblPatient pat
LEFT JOIN tlkpGender tpg
ON pat.lngGender = tpg.lngValue
LEFT JOIN tlkpRace tpr
ON pat.lngRace = tpr.lngValue
LEFT JOIN tlkpEthnicity eth
ON pat.lngEthnicity = eth.lngValue
LEFT JOIN tlkpSmokingStatus tss
ON pat.strSmokingStatus = tss.lngValue "
)
)
df = na.omit(myquery)
# Define UI
ui <- fluidPage(
titlePanel("BMI of Hernia Patients"),
sidebarLayout(
sidebarPanel(
helpText("Create BMI density plots from the HHC Hernia Database."),
selectInput("variable",
label = "Choose a variable to display",
choices = list("BMI"),
selected = "BMI"),
selectInput("category",
label = "Choose a category to graph BMI by",
choices = list("Gender",
"Race",
"Ethnicity",
"Smoker"),
selected = "None"),
sliderInput("range",
label = "Display Range:",
min = 0, max = 100, value = c(0, 65))
),
mainPanel(
# DT::dataTableOutput("mytable"),
plotOutput("dense_plot")
)
)
)
# Define server logic
server <- function(input, output) {
#output$mytable = DT::renderDataTable({myquery})
output$dense_plot = renderPlot({
var = switch(input$variable,
"BMI" = df$BMI)
cat = switch(input$category,
"Gender" = df$Gender,
"Race" = df$Race,
"Ethnicity" = df$Ethnicity,
"Smoker" = df$Smoker)
density_plots(dataset = df,
variable = var,
category = cat,
x_label = "BMI",
title_card = "Distribution of BMI",
lower = input$range[1],
upper = input$range[2])
})
}
# Run the app
shinyApp(ui = ui, server = server)
Next, we've density.R, which contains two functions density_plot() which creates a single density plot for BMI of all patients, and density_plots() which creates a density plot of BMI by a specific category. This second function is what I'm calling in app.R
library(ggplot2)
density_plot <- function(dataset, variable, rm_na = TRUE, border_color = "darkgoldenrod4", fill_color = "dodgerblue4", transparency = 0.25, lower = 0, upper = 65,
title_card = "", x_label = "") {
# plots a single density plot. Defaults are set to appropriate values for Hernia BMI.
ggplot(data = dataset) +
geom_density(mapping = aes(x = variable), na.rm = rm_na, color = border_color, fill = fill_color, alpha = transparency) +
scale_x_continuous(limits = c(lower, upper)) +
coord_cartesian(xlim = c(lower, upper)) +
labs(x = x_label, title = title_card)
}
density_plots <- function(dataset, variable, category, rm_na = TRUE, transparency = 0.25, lower = 0, upper = 65, title_card = "", x_label = "") {
ggplot(data = dataset) +
geom_density(mapping = aes(x = variable, color = category, fill = category), na.rm = rm_na, alpha = transparency) +
scale_x_continuous(limits = c(lower, upper)) +
coord_cartesian(xlim = c(lower, upper)) +
labs(x = x_label, title = title_card)
}
Its hard to debug this since its not reproducible but you can try this:
# Define server logic
server <- function(input, output) {
#output$mytable = DT::renderDataTable({myquery})
output$dense_plot = renderPlot({
density_plots(dataset = df,
variable = input$variable,
category = input$category,
x_label = "BMI",
title_card = "Distribution of BMI",
lower = input$range[1],
upper = input$range[2])
})
}
density_plots <- function(dataset,
variable,
category,
rm_na = TRUE,
transparency = 0.25,
lower = 0, upper = 65,
title_card = "", x_label = "") {
ggplot(data = dataset) +
geom_density(mapping = aes_string(x = variable, color = category, fill = category),
na.rm = rm_na, alpha = transparency) +
scale_x_continuous(limits = c(lower, upper)) +
coord_cartesian(xlim = c(lower, upper)) +
labs(x = x_label, title = title_card)
}
at the moment I try to create an interactive heatmap in R with apexcharter. This works fine at manual chart creation but fails on interactive use within shiny.
library(shiny)
library(tidyverse)
library(apexcharter)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Test Heatmap"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "heatmap_filter",
label = "heatmap filter",
choices = c(1999, 2008),
selected = 2008
)
),
mainPanel(
apexchartOutput("heatmap")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$heatmap <- renderApexchart({
df <- mpg %>% filter(year == input$heatmap_filter) %>% mutate_if(is.character, as.factor) %>% group_by(manufacturer, class) %>% summarise(cnt = n()) %>% tidyr::complete(class, fill = list(cnt = 0))
q20 <- round(as.numeric(quantile(df %>% filter(cnt>0) %>% pull(cnt), probs = seq(0,1,0.2), na.rm = TRUE))[2],0)
q40 <- round(as.numeric(quantile(df %>% filter(cnt>0) %>% pull(cnt), probs = seq(0,1,0.2), na.rm = TRUE))[3],0)
q60 <- round(as.numeric(quantile(df %>% filter(cnt>0) %>% pull(cnt), probs = seq(0,1,0.2), na.rm = TRUE))[4],0)
q80 <- round(as.numeric(quantile(df %>% filter(cnt>0) %>% pull(cnt), probs = seq(0,1,0.2), na.rm = TRUE))[5],0)
apex(
data = df,
type = "heatmap",
mapping = aes(x = manufacturer, y = class, fill = cnt)
) %>%
ax_dataLabels(enabled = TRUE) %>%
ax_plotOptions(
heatmap = heatmap_opts(
enableShades = FALSE,
colorScale = list(
ranges = list(
list(from = 0, to = q20, color = "#106e45"), #grün
list(from = q20, to = q40, color = "#90dbba"), #leichtes grün
list(from = q40, to = q60, color = "#fff33b"), #gelb
list(from = q60, to = q80, color = "#f3903f"), # orange
list(from = q80, to = 20, color = "#e93e3a") #rot
)
)
)
) %>%
ax_title(
text = paste("Test interactive heatmap",
input$heatmap_filter
), align = "center"
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
With the manual approach everthing works as expected. But when I change the input select only the values changes but not the heatmap quantil ranges and not the title input. Its seems like the input value is not pushing the changes to already calculated variables. I already tried to use an reactive df or reactive variables but so far nothing works.
I added a minimal example where you could change the year input and this should change the title and the color ranges.
Can you help me?
Thanks in advance.
Try setting auto_update to FALSE in the call to apex
apex(
data = df,
type = "heatmap",
auto_update = FALSE,
...
I have a simple shiny app like below and you can run it. The plots are created by ggvis and user can choose student name from inputSelect. In the plots, I want to change the color of background in specific score range. For example, in each plot, the color of plot background for the score higher than 80 or lower than 50 are highlighted with blue(See picture attached). I was trying to add layers and draw rectangles onto plot using layer_rects(), but the problem is the values of x-axis are changed if different students are chosen.Anyone did this before or any ideas? And is it possible if I want only the points in that score range pop up? Thanks a lot!
library(shiny)
library(ggvis)
df <- data.frame(Student = c("a","a","a","a","a","b","b","b","b","b","c","c","c","c"),
year = c(seq(2001,2005,1),seq(2010,2014,1),seq(2012,2015,1)),
score = runif(14,min = 50,max = 100), stringsAsFactors=F)
ui = (fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("stu","Choose Student",
choice = unique(df$Student))
),
mainPanel(ggvisOutput("plot"))
)
)
)
server = function(input,output,session){
dataInput = reactive({
gg = df[which(df$Student == input$stu),]
})
vis = reactive({
data = dataInput()
data %>%
ggvis(x = ~year, y = ~score) %>%
scale_numeric("y",domain = c(40,120))%>%
layer_lines()
})
vis %>% bind_shiny("plot")
}
runApp(list(ui = ui, server = server))
To have the width of the rectangles to change with the x-axis variable, you can use x = ~min(year) and x2 = ~max(year). I'm not sure how to make the variables dependent on the current scale limits, which seems like it would be a nicer solution. But, this should work. For example, the lower rectangle would be
layer_rects(x = ~min(year), x2 =~max(year),
y = 40-3.5, y2 = 50, opacity := 0.05, fill := "blue")
It isn't vectorized for different limits (at least it didn't look to be), so you can write a function to simplify having multiple rectangles. The whole server would look like
shinyServer(function(input,output,session){
dataInput = reactive({
gg = df[which(df$Student == input$stu),]
})
buffer <- 3.5 # set to make the rectangle reach the scale boundaries
rectLims <- list(lower=c(40-buffer, 80), upper=c(50, 120+buffer))
make_rect <- function(vis, lims, buffer=buffer) {
for (i in seq_along(lims$lower))
vis <- layer_rects(vis, x = ~min(year), x2 =~max(year),
y = rectLims$lower[i], y2 = rectLims$upper[i],
opacity := 0.05, fill := "blue")
vis
}
vis = reactive({
data = dataInput()
data %>%
ggvis(x = ~year, y = ~score) %>%
scale_numeric("y",domain = c(40,120)) %>%
layer_points()%>%
layer_lines() %>%
make_rect(lims=rectLims)
})
vis %>% bind_shiny("plot")
})
For your second question, if you only want points to show up in that range, you can make a subset of the data to use for the layer_points or a logical vector (converted to numeric with +) and use that as the opacity argument,
vis = reactive({
data = dataInput()
## Option 1: and use layer_points(data=inrange)
## inrange <- with(dataInput(), dataInput()[score >=80 | score <= 50,])
## Options 2, with opacity=~inrange
inrange = +with(data, score >=80 | score <= 50)
data %>%
ggvis(x = ~year, y = ~score) %>%
scale_numeric("y",domain = c(40,120)) %>%
layer_points(opacity = ~inrange) %>%
layer_lines() %>%
make_rect(lims=rectLims)
})
I have the following code on my Server. R
data_agg_plot1<- reactive({
brush1 <- linked_brush(keys = data_agg()$id, "navy" )
data_agg <- data_agg()
plot1<-data_agg%>%
ggvis(x = ~dates_all) %>%
group_by(factor(dates_all.1)) %>%
layer_points(y = ~ value, fill =~dates_all.1, shape =~dates_all.1) %>%
layer_paths(y = ~ value, stroke = ~dates_all.1 , strokeOpacity := 0.5) %>%
scale_ordinal("fill", range = c("green", "red", "blue"))%>%
scale_ordinal("shape", range = c("triangle-up","triangle-down","circle")) %>%
scale_ordinal("stroke",range=c("green","red","blue")) %>%
brush1$input() %>%
hide_legend(c('stroke','fill'))%>%
add_legend(c('shape','fill'),
title = "Symbol", orient = "left",
values = c("New hires", "Attrition" , "Net Growth"),
properties = legend_props(
title = list(fontSize = 16))) %>%
add_axis("x",properties= axis_props(labels = list(angle=60,align = "left")),
tick_padding =0,
title = "") %>%
add_axis("y", title = "Total Count") %>%
set_options(width = "auto",height = 400) %>%
scale_numeric('y',clamp = TRUE)
return(list(plot1,brush1))
})
so this is a reactive function that returns me a list of 2 functions, a plot and my brush object.
the purpose of doing so is so that I can make my keys reactive - this is so that I can make an additional plot based on my user's selection. think of it as the second plot depends on what the first user highlights in the first plot.
this is my following code:
plot1_data<-reactive({
data_agg_plot1()[[1]]
})
plot1_data%>%bind_shiny("plot1")
selected_plot1 <- reactive({
data_agg_plot1()[[2]]
})
output$test <- renderPrint({
temp <- selected_plot1()$selected()
print(temp)
})
however, when I print out the selection, it is all false,
please refer to the image below:
can anybody explain to me how to overcome this?
I highly suspect I have to re-write my linkedbrush function,
I have tried both solutions from:
linked_brush in ggvis cannot work in Shiny when data change
but it does not work.