2

I recently started experementing with Shiny and as you can imagine many issues arise. I get the concept of inputs and outputs and that we have to connect them, however, I am still strugelling to create Filters which base on some hirarchy. (Or in general how to connect multiple filters) I checked already similar articles such as this one R Shiny - How to filter by checkboxGroupInput however I have hard time getting the idea if I do not see the data and thats why I was hoping somebody could help me with my example.

So my goal was to create a still very simple App which is based on multiple conditions. I have a data set about a city which shows the migration of people into different districts based on their nationalities (for the past 10 years)

so it looks sort of like that: (my data set is called Eu1)


Year Districts  Country  Amount
2018 District_1    UK     70
2017 District_1    UK     63
2016 District_1    UK     48
2015 District_1    UK     55
2018 District_1    Fr     35
2017 District_1    Fr     41
2016 District_1    Fr     39
2015 District_1    Fr     30
2018 District_1    Ger   2526
2017 District_1    Ger   2459
2016 District_1    Ger   2326
2015 District_1    Ger   2225
2018 District_2    Fr      8
2017 District_2    Fr      6
2016 District_2    Fr      7
2015 District_2    Fr     14
2018 District_2    UK     23
2017 District_2    UK     25
2016 District_2    UK     28
2015 District_2    UK     29
2018 District_2    Ger    734
2017 District_2    Ger    713
2016 District_2    Ger    696
2015 District_2    Ger    698

This is a simplified version of my data set ( I have of course many more variables)

UI

ui <- fluidPage(
  setBackgroundColor(color = c("#66e0ff", "#00a3cc", "#003d4d")),
  titlePanel("Migration"),
  sidebarLayout(
    sidebarPanel(tags$style(".well {background-color:#e6f9ff;}"),
      sliderInput(inputId = "range",
                             label = "Chose the year range:",
                             min = 2002, max = 2020, value = c(2002,2020)),
      selectInput(inputId = "dis",
                  label = "Chose the district",
                  choices = unique(Eu1$District)),
      checkboxGroupInput(inputId = "con",
                  label = "Chose the place of birth",
                  choices = unique(Eu1$country))

    ),#sidebar panel
    mainPanel(plotOutput("graph1")) # closing main panel
  )#sidelayout panel
)#fluid page

The ui works pretty well

I have also started building the server but I am stuck at the moment.

server <- function(input, output){


  # reactive range 
  df_range <- reactive({
    cbind(input$range[1],input$range[2])
  })

  # first filter based on distric
  op1 <- reactive({
    Eu1 %>% filter(District == input$dis)
  })

  # second filter for countries
  op2 <- reactive({
    op1() %>% filter(country== input$con)
  })

  # filtering first graph
  df_range <- reactive({
    filter(Eu1, between(Year,input$range[1],input$range[2])) %>% 
      select(Year, input$dis,input$con) 
  })

  # Ensures that our filter works properly
  observe(print(str(df_range())))


  # create a graph 
  output$graph1 <- renderPlot({
  ggplot(df_range(), aes(x=input$range, y=Eu1$Amount)) +
    geom_line(aes(colour=input$con)) +
    geom_point()
  })

}

As you may see, at some point I got fully lost there.

My desired outcome is the following: 1. We have a range of years (this should be always reactive its like the first filter) 2. We have the selectInput (the district) so the user can chose the desire district of the city (in the range of years which he specified) 3. Ones the district is choosen the user can chose the desired nationalities which are supposed to be displayed in the line graph.

So the outcome is basically a line chart which shows the migration of different nationalities towards different districts and the user should have the ability to compare the desired nationalities in one graph (so f.i. to see how many people from Ger and FR moved to Dis1) and if they want to see only Ger then they click only Ger in the Groupclickbox.

I apologize in case this is something very simple but I still do not fully get how to connect this reactivity of different filters with each other, and therefore, I wanted to see it on my data because then it would be less abstract and I would understand better how everything is connected. So an elaborative explanation is highly welcome!

Thank you very much in advance!

Newbie
  • 91
  • 1
  • 8
  • Can you use dput(yourdata) to make the example reproducible? – Ollie Perkins May 17 '19 at 22:00
  • Hi @OlliePerkins, I just added an actual part of my data set ( I filtered in a way so the structure is more visible) I have in general around 15 district and 30 nationalities. But I hope based on this sample the idea of my data structure is clear now. – Newbie May 18 '19 at 09:49

1 Answers1

4

I think this should do it:

library(shiny)
library(shinyWidgets)
library(ggplot2)
library(dplyr)

Eu1 <- data.frame(stringsAsFactors=FALSE,
                  Year = c(2018, 2017, 2016, 2015, 2018, 2017, 2016, 2015, 2018, 2017,
                           2016, 2015, 2018, 2017, 2016, 2015, 2018, 2017, 2016, 2015,
                           2018, 2017, 2016, 2015),
                  Districts = c("District_1", "District_1", "District_1", "District_1",
                                "District_1", "District_1", "District_1", "District_1",
                                "District_1", "District_1", "District_1", "District_1", "District_2",
                                "District_2", "District_2", "District_2", "District_2",
                                "District_2", "District_2", "District_2", "District_2", "District_2",
                                "District_2", "District_2"),
                  Country = c("UK", "UK", "UK", "UK", "Fr", "Fr", "Fr", "Fr", "Ger", "Ger",
                              "Ger", "Ger", "Fr", "Fr", "Fr", "Fr", "UK", "UK", "UK", "UK",
                              "Ger", "Ger", "Ger", "Ger"),
                  Amount = c(70, 63, 48, 55, 35, 41, 39, 30, 2526, 2459, 2326, 2225, 8, 6,
                             7, 14, 23, 25, 28, 29, 734, 713, 696, 698)
)


ui <- fluidPage(
  setBackgroundColor(color = c("#66e0ff", "#00a3cc", "#003d4d")),
  titlePanel("Migration"),
  sidebarLayout(
    sidebarPanel(tags$style(".well {background-color:#e6f9ff;}"),
                 sliderInput(inputId = "range",
                             label = "Chose the year range:",
                             min = 2002, max = 2020, value = c(2002,2020)),
                 selectInput(inputId = "dis",
                             label = "Chose the district",
                             choices = unique(Eu1$District)),
                 checkboxGroupInput(inputId = "con",
                                    label = "Chose the place of birth",
                                    choices = unique(Eu1$Country),
                                    selected = unique(Eu1$Country)[1])

    ),#sidebar panel
    mainPanel(plotOutput("graph1")) # closing main panel
  )#sidelayout panel
)#fluid page

server <- function(input, output){

  df_dat <- reactive({

    # make sure inputs are not NULL
    req(input$con, input$dis, input$range) 

    # filter data according to inputs made by the user
    df_dat <- filter(Eu1, between(Year, input$range[1], input$range[2]), Districts == input$dis, Country %in% input$con)

    return(df_dat)
  })

  # Ensures that our filter works properly
  observe(print(str(df_dat())))

  # create a graph 
  output$graph1 <- renderPlot({

    # use to debug:
    # browser()

    # make sure filtered data is not NULL
    req(df_dat())

    # plot filtered data
    ggplot(df_dat(), aes(x = Year, y = Amount)) +
      geom_line(aes(colour = Country)) +
      geom_point()
  })

}

shinyApp(ui = ui, server = server)

There were several issues - e.g.:

  • be strict about upper and lower case of your column names
  • tried using select but filter was required
  • reactiveVal df_range was defined twice
ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
  • Thank you so much ! It works! I just have few small questions regardings the code: 1. You havent really used the op1 and op2 filters in your code. Does this mean I do not really need it ? Or does shiny read the filter already as input$dis & input$con inside the df_dat. Can you explain this a bit more? 2. Why do you use req(). What is the purpose here? How does shiny interprete this function? – Newbie May 19 '19 at 09:20
  • Or in case it is not to much to ask for - could you maybe write a comment below or above each operation you did ? (Just a bit more elaborative) – Newbie May 19 '19 at 09:27
  • Just updated my answer: for a scenario like this you don't need intermediate filters. One filter statement calling all the inputs is suffiecient (I just left op1&2 in the code because I didn't know if you need them somewhere else in your actual code). – ismirsehregal May 19 '19 at 09:31
  • Ahh! Alright, thank you very much ! It helped a lot ! For what scenario would I need intermediate filters ? If my outcome depends on the inputs I have choosen ? – Newbie May 19 '19 at 12:05
  • For example if the choices of one of your selectInputs depend on a previous selection. – ismirsehregal May 19 '19 at 12:10