How to select a column from a dynamic input variable? - r

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)

Related

r shiny and ggplot2::facet_wrap how can I add categories to facet_wrap without having the original plot resize?

I have a shiny app that lets the user add categories to the facet_wrap. When I start with one category the plot fills the entire box but when I add a second category, the initial plot adjusts to half the initial size. Is there any way I can set the size, such that the first facet fits half the box and doesn't adjust in size when I add a second category?
Here's what I happens when I choose a second facet category:
Current behavior
Here's what I want to happen:
desired behavior
Here is a simple reprex--when you add a second feature from select feature, it adjusts the size of the first plot.
I found a decent solution and added it to this example using the ggh4x::facet_manual. However, this solution does not work with ggplotly and in a bs4dash box, it starts to look crammed in my app where there are upwards of 40 plots. Ideally, I'd like the box to be scrollable. Thanks in advance for any suggestions!
library(shiny)
library(tidyverse)
library(glue)
library(ggh4x)
library(plotly)
library(janitor)
library(bs4Dash)
iris_df <- iris %>%
clean_names() %>%
mutate(extra_feature1 = sepal_length,
extra_feature2 = sepal_width,
extra_feature3 = petal_length,
extra_feature4 = petal_width,
extra_feature5 = sepal_length,
extra_feature6 = sepal_width,
extra_feature7 = petal_length,
extra_feature8 = petal_width) %>%
select(species, everything()) %>%
pivot_longer(-species) %>%
mutate(feature = glue("{name}_{species}"))
iris_species <- iris_df %>%
clean_names() %>%
distinct(species) %>%
pull()
iris_features <- iris_df %>%
clean_names() %>%
distinct(feature) %>%
pull()
# Define UI for application that draws a histogram
ui <- dashboardPage(dark = FALSE,
# Application title
dashboardHeader("Reprex"),
# Sidebar with a slider input for number of bins
dashboardSidebar(skin = "light",
selectInput("species",
"Select species:",
choices = iris_species,
selectize = FALSE,
multiple = TRUE,
selected = iris_species[1]
),
selectInput("features",
"Select feature:",
choices = iris_features,
selectize = TRUE,
multiple = TRUE,
selected = iris_features[1]
),
radioButtons("facets", label = "View all features:",
choices = list("On" = "facet_wrap", "Off" = ""),
selected = "", inline = FALSE)),
# Show a plot of the generated distribution
dashboardBody(
fluidRow(box(
plotOutput("densityPlot"),
width=12,
headerBorder = FALSE,
collapsible = FALSE))
))
# Define server logic required to draw a histogram
server <- function(input, output, session) {
observeEvent(input$species,
{updateSelectInput(session,
"features",
choices = unique(iris_df$feature[iris_df$species == input$species]),
selected = iris_df$feature[1])
})
design <- matrix(c(1:12), 2, 6, byrow = FALSE)
output$densityPlot <- renderPlot({
if (input$facets == '') {
p1 <- iris_df %>%
filter(species %in% input$species) %>%
filter(feature %in% input$features) %>%
ggplot(aes(value, fill = species)) +
geom_density(alpha = .5) +
theme_light() +
facet_manual(~name, scales = "free", design = t(design), respect = FALSE)
#facet_wrap(~name, scales = "free")
p1
}
else {
iris_df %>%
filter(species %in% input$species) %>%
ggplot(aes(value, fill = species)) +
geom_density(alpha = .5) +
theme_light() +
facet_wrap(~name, ncol = 2, scales = "free")
}
})
}
# Run the application
shinyApp(ui = ui, server = server)

Interactive heatmap in R using apexcharter fails at reactivity

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,
...

Reactive plot is mapping variable name?

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().

plot_ly plot from dplyr breaks down at input from Shiny SelectInput

I'm just learning Shiny.
Here's the code that doesn't work (along with some sample data built-in):
library(tidyverse)
library(shiny)
library(plotly)
library(shinyjs)
analysis_df<- data.frame(
report_month = c("jan","jan","jan","jan","jan","jan"),
payee_id = c("59","59","59","59","59","59"),
Payee = sample(LETTERS[1:5],6,replace = TRUE),
Attrib_1 = sample(LETTERS[6:10],6,replace = TRUE),
Attrib_2 = sample(LETTERS[11:15],6,replace = TRUE),
country_of_sale_iso2 = c("AU","AU","AU","NZ","AU","AU"),
currency = c("USD","USD","USD","USD","USD","USD"),
Attrib_3 = c("Pandora-AU","Pandora-AU","Pandora-AU","Pandora-AU","Pandora-AU","Pandora-AU"),
month_paid = c("jun","jun","jun","jun","jun","jun"),
Attrib_4 = sample(LETTERS[16:20],6,replace = TRUE),
Attrib_5 = sample(LETTERS[21:25],6,replace = TRUE),
units = c("2","8","6","2","10","4"),
gross = c("0.003254785","0.013019141","0.009764356","0.003254785","0.016273926","0.00650957"),
reserves_wh = c("0","0","0","0","0","0"),
rsrv_liq = c("0","0","0","0","0","0"),
Attrib_7 = c("0.002753548","0.011014193","0.008260645","0.002753548","0.013767741","0.005507097"),
Attrib_8 = c("3.25E-04","0.001301914","9.76E-04","3.25E-04","0.001627393","6.51E-04"),
Attrib_9 = c("1.76E-04","7.03E-04","5.27E-04","1.76E-04","8.79E-04","3.52E-04"),
Attrib_10 = c("0.03","0.03","0.03","0.03","0.03","0.03"),
Attrib_11 = c("1","1","1","1","1","1"),
Attrib_12 = c("0.003254785","0.013019141","0.009764356","0.003254785","0.016273926","0.00650957")
)
attribs <- c("Attrib_1","Attrib_2","Attrib_3","Attrib_4")
payees <- analysis_df %>% distinct(Payee) %>% as.vector()
ui <- fluidPage(
headerPanel("Product Explorer"),
sidebarPanel(
selectInput('slice_by', 'Color the Bars By:', choices = attribs, selected = "Attrib_1"),
sliderInput('plotHeight', 'Adjust Chart Size',
min = 100, max = 2000, value = 425)
),
mainPanel(
plotlyOutput('Plot', height = "900px")
)
)
server <- function(input, output) {
output$Plot <- renderPlotly({
col_cht <- analysis_df %>%
filter(payee_id == 59) %>%
plot_ly(x = ~report_month,
y = ~gross) %>%
add_bars(color = input$slice_by) %>%
layout(barmode = "stack",
height = input$plotHeight)
})
}
shinyApp(ui, server)
I want the SelectInput to work, and it doesn't.
However, if I replace
add_bars(color = input$slice_by) %>%
with
add_bars(color = ~Attrib_1) %>%
i.e., hard-code it, the plot looks the way it should.
When you are piping with
> analysis_df %>%
the analysis_df dataframe is passed to the functions. So when using ~Attrib_1 you are passing the values in the Attrib_1 column, which are
# > analysis_df$Attrib_1
# [1] H J J H H G
So the plot gets different colors for the levels in analysis_df$Attrib_1.
When you are using input$slice_by that returns only one value, the value selected in Select. So you are getting just one color in the plot.
To get it to work use
color = analysis_df[, input$slice_by]
If you don't want to use analysis_df inside pipe, search about Non-standard Evaluation in R. With lazyeval you can do this,
color = interp(~x, x = as.name(input$slice_by))

R: How to change plot background color for a specific range in ggvis shiny app

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)
})

Resources