0

I am trying to combine two things into 1 violin plot. In my Shiny app user can choose variable he want to have in violin plot (X variable). As a Y variable I have age which can have range chosen by user:

There is no problem if I just pick a variable and stay with full range of income. However, when I want to have a different range I get an error.

To show the problem more visible: - this works ok, I can change the X variable and it is fine with the full range of income works fine with full range

  • if I change the income range I get error

error

I think that the problem appears because the X variable has different range. I don't know how and where to change it to make it work.

If I drop the X interactivity it works:

renderPlot({
  data %>%
    filter(
      between(Age, input$income[1], input$income[2])) %>%
  ggplot(aes(x=Sex, y=Age)) + 
  geom_violin(aes(fill=Sex), trim=FALSE) +
  geom_boxplot(width=0.3)+
  stat_summary(fun=mean, geom="point", shape=20, size=5)+
  ggtitle(input$var)})

enter image description here

But I want it to stay interactive. Do you have any solution for that? Here is a code with titanic dataset showing the same problem which you can copy-paste:

---
title: "dataset"
output: 
  flexdashboard::flex_dashboard:
    orientation: rows
    vertical_layout: fill
runtime: shiny
---

```{r global, include=FALSE}
#dataset, libraries and other global things

library(flexdashboard)
library(ggplot2)
library(tidyverse)
library(dplyr)
library(titanic)
data("titanic_train")
data<-na.omit(titanic_train)


```


Dashboard {data-orientation=rows}
=====================================     

Inputs {.sidebar}
-------------------------------------

**Age by:**

```{r plot-option}
selectInput("var", label = "Please choose variable:",
            choices = names(subset(data, select=c(Sex ,Pclass, SibSp))))

sliderInput("income", HTML("Income interval:"),
                  min = min(data$Age), max = max(data$Age), value = c(min(data$Age), max(data$Age)), step =1)
```



Row
-------------------------------------

### Age by: {data-width=450}

```{r}
selected <- reactive({  data[, c(input$var)] })

renderPlot({
  data %>%
    filter(
      between(Age, input$income[1], input$income[2])) %>%
  ggplot(aes(x=selected(), y=Age)) + 
  geom_violin(aes(fill=input$var), trim=FALSE) +
  geom_boxplot(width=0.3)+
  stat_summary(fun=mean, geom="point", shape=20, size=5)+
  ggtitle(input$var)})

```
Elise
  • 37
  • 6
  • Hi, it will be easier to help you if you provide a [minimal, reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) – bretauv May 16 '20 at 20:45
  • Hi, sorry for that. I edited this question. Is this problem now more clear? Thank you. – Elise May 16 '20 at 21:26
  • No, the goal of a reproducible example is to provide some code that just needs to be copied and pasted in R to reproduce your situation. Here, you only provide some chunks of code that cannot run if we copy-paste them in R. You need to modify your post again (see the first link I provided and [this one](https://stackoverflow.com/help/minimal-reproducible-example) for more details) – bretauv May 16 '20 at 21:59
  • Agree with @bretauv, this would be helpful to have a minimal working example with sample data. But based on your error, my guess is that you have different lengths of data in your `ggplot` statement. Your `x = selected()` will use all rows of `data` (with a specific column selected). This is different than `y = MonthlyIncome` which has less rows because it was filtered above it. One way to approach is this is to do the filtering in your `selected` reactive expression, and use the `selected()` results instead of `data` in your `renderPlot` – Ben May 16 '20 at 22:08
  • Ok. Sorry for that. I added the whole code which you can paste in R. The code is based on titanic data set to avoid importing csv file. The problem is the same. – Elise May 17 '20 at 11:53

1 Answers1

1

As @Ben says; there is a difference in the number of rows selected. I'm somewhat new with Shiny, but why do you need to subset the data in the reactive part? It's not needed (you filter it later on) and it's less efficient since you're effectively subsetting your data every time you change the selected variable (granted, not a big problem in a small dataset, but still..)

So I suggest:

renderPlot({
  data %>%
    filter(
      between(MonthlyIncome, input$income[1], input$income[2])) %>%
  ggplot(aes_string(x= input$var, y=data$MonthlyIncome)) + 
  geom_violin(aes(fill=input$var), trim=FALSE) +
  geom_boxplot(width=0.3)+
  stat_summary(fun=mean, geom="point", shape=20, size=5)+
  ggtitle(input$var)})

Define UI for application

ui <- fluidPage(

# Application title
titlePanel("Iris"),

# Sidebar with a slider input for number of bins 
sidebarLayout(
    sidebarPanel(
        selectInput("Variables","Choose a Variable",
                    choices = c(colnames(iris)),
                    selected = "Species"),
        sliderInput("PetalLength", "Length Interval", 
                    min = 0.1, max = 10, value = c(0.5, 7.5), step = 0.1)
    )    
    ,

    # Show a plot of the generated distribution
    mainPanel(
        plotOutput("irisPlot")
    )
)

)

Define server logic

server <- function(input, output) { data(iris)

output$irisPlot <- renderPlot({
    iris_data <- filter(iris, between(Petal.Length, input$PetalLength[1], input$PetalLength[2]))
        ggplot(iris_data, aes_string(x= input$Variables, y=iris_data$Petal.Length)) + 
        geom_violin(aes(fill=input$Variables), trim=FALSE) +
        geom_boxplot(width=0.3)+
        stat_summary(fun=mean, geom="point", shape=20, size=5)+
        ggtitle(input$Variables)})

}

Run the application

shinyApp(ui = ui, server = server)

  • Thank you for advice. It would work, but I would lose the Male and Female on the violin plot. It would be just one violin plot - Gender. :( – Elise May 17 '20 at 11:55
  • It's because of the way ggplot uses the output. Try using aes_string instead, and using the input$var directly in the renderPlot instead. You don't need the double reactivity. See the example using the iris dataset above. – RegressionSquirrel May 17 '20 at 13:11
  • Thank you for example. I ran this iris example. Unfortunately, it seems that as soon as I change the range in the displayed app I get the same error. That only works for defined earlier range: 0.5 - 7.5. – Elise May 17 '20 at 13:57
  • Ok, I think I have it. The problem is that the filtering and piping is only passed on to the x argument of the aes call. I've amended the iris example in my answer - it now works with the inputSlider as well. It's.. not an elegant solution I guess, but it works. – RegressionSquirrel May 17 '20 at 15:16
  • Yes, it works. Thank you! I just have aesthetic question - is it possible to have fill by for example here species? Right now there is one color - red and legend saying Species. It'd be ideally to have colors depending on the column and the legend saying setosa, virginica, versicolor. Not just "Species". – Elise May 17 '20 at 21:49
  • Yes, that's also possible - change the aes call to aes_string for geom_violin as well. In general, if you want to use sliderInput variables in ggplot, you will need to use aes_string. – RegressionSquirrel May 18 '20 at 05:51