1

I apologize in case this questions sounds too basic, but I have been trying already for a while and it still doesn't work. (I saw there was a similar question already here but after trying to implement what was suggested there I still received an error). So I would be very grateful if somebody could explain to me in more detail what I do wrong with an example of code.

I have the following data set:

year <- c(2000, 2002, 2006, 2000, 2004, 2010, 2010, 2011, 2020, 2006)
prices <- c(100, 200, 300, 120, 240, 400, 430, 490, 700, 650)
colors1 <- c("red", "red", "blue", "green","blue", "red", "blue", "green", "green", "red")
size <- c("s", "m", "l", "xl", "l", "s", "m", "xl", "l","m")
city <- c("NY","LA", "DC","NY","LA", "DC","NY","LA", "DC", "NY")
delivery <- c(3, 7, 3, 10, 20, 5, 10, 2, 12,4)
df <- data.frame(year, prices, colors1,size,city, delivery, stringsAsFactors = FALSE)

df$vatprice<- df$prices * 1.2

This is the structure of my data set. It looks basically like this.

   year prices colors1 size city delivery vatprices
1  2000    100     red    s   NY        3       120
2  2002    200     red    m   LA        7       240
3  2006    300    blue    l   DC        3       360
4  2000    120   green   xl   NY       10       144
5  2004    240    blue    l   LA       20       288
6  2010    400     red    s   DC        5       480
7  2010    430    blue    m   NY       10       516
8  2011    490   green   xl   LA        2       588
9  2020    700   green    l   DC       12       840
10 2006    650     red    m   NY        4       780

Now what I am trying to do I want to create a small shiny App which would show the following:

on the x-axis a range of years (as you can see my years are descrete but I hope I can still set them as a range)

on y axis: either price or vatprice

and all this data should be categorized either by color, size or city.

ui <- basicPage(
  sidebarPanel(
    sliderInput("range", "Year:",min = 1990, max = 2040, value = c(2000,2020)),textOutput("SliderText"),
    selectInput("yaxis", "Y Variable", names(df[,c(2,7)]), selected = names(df)[[2]]),
    radioButtons(inputId = "cat", label = "Categorized by:", names(df[,c(3:5)]))
  ), # closing of the sidebar panel
  plotOutput("scatter")

)


# server 

server <- shinyServer(function(input, output){

  my_range <- reactive({
    cbind(input$range[1],input$range[2])
  })
  output$SliderText <- renderText({my_range()})

  output$scatter <- renderPlot({
    ggplot(df, aes(df$year, df$prices)) + 
   geom_point(aes(color = factor(df$size))) + labs(title = "Overview",
                  color = "Legend") + xlab("Year") + ylab("Price") + theme_minimal()

  })})


shinyApp(ui = ui, server = server)

So when I run the programm I get basically already my entire UI and the graph itself. But I have to connect somehow my df data set with the server in order to make my shiny app interactive.

So my question is: Do I have to create a reactive variable for each of those columns? Or should I just create one reactive data set? Can somebody show me the logic behind, cause I still feel lost.

I tried to do it like here: How do I connect my inputs to my graph outputs but still got an error - so I removed it and kept only my basic UI and server function, since they don't show any errors.

Grr La
  • 46
  • 6
Newbie
  • 91
  • 1
  • 8

2 Answers2

0

First, we need to get a final df in terms of year range and column names based on what the user has entered. Then we need to pass these variables to ggplot using !!sym since they will be in string form i.e prices will be "prices" not prices.

library(dplyr)
library(ggplot2)
server <- shinyServer(function(input, output){

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

  df_final <- reactive({
    filter(df, between(year,input$range[1],input$range[2])) %>% 
    select(year,input$yaxis,input$cat) 
  })

  output$SliderText <- renderText({my_range()})

  #Check if filter and select work correctly and as we wanted
  observe(print(str(df_final()))) 

  output$scatter <- renderPlot({
    ggplot(df_final(), aes(year, !!sym(input$yaxis))) + 
      geom_point(aes(color = factor(!!sym(input$cat)))) + labs(title = "Overview",
                                                      color = "Legend") + xlab("Year") + ylab("Price") + theme_minimal()

  })})


shinyApp(ui = ui, server = server)
A. Suliman
  • 12,923
  • 5
  • 24
  • 37
  • Amazing this works ! Thank you so much ! I have a question regarding your code. Why are you using !!sym Because I was doing before df[,input$cat] & df[,input$yaxis] and had the same result as you - however, my x-axis did not function but with your solution it does. What are the differences between my and your way ? Why cant I also just do x= df[ ,input=range] ? – Newbie May 11 '19 at 13:50
  • @Newbie you're most welcome. Both `df[,input$cat]` and `!!sym(input$yaxis)` work in this case but if you comment/removed `ylab("Price")` and `color = "Legend"` you will see the difference, see [here](https://www.tidyverse.org/articles/2018/07/ggplot2-3-0-0/) and [here](https://dplyr.tidyverse.org/articles/programming.html) for more details about `sym` and `quo`.You may use `x= df[ ,input=range]` or any subset method taking into account the user inputs but I'm some what a fan of tidyverse ;) – A. Suliman May 11 '19 at 18:11
0

I propose another answer, I made several change, first I inspired myself from the basic tutorial :

https://shiny.rstudio.com/tutorial/written-tutorial/lesson4/

Secondly I use aes_string as in :

Best way to convert character strings in to function arguments in R/ggplot2?

It seems to me more natural for what I did. Secondly I made the selection inside the server I don't know if it is efficient but I think that it is clear. Secondly your cbind inside data.frame call is a bad idea beacause you force every variable to be the same type and it is not what you want.

year <- c(2000, 2002, 2006, 2000, 2004, 2010, 2010, 2011, 2020, 2006)
Price <- c(100, 200, 300, 120, 240, 400, 430, 490, 700, 650)
colors1 <- c("red", "red", "blue", "green","blue", "red", "blue", "green", "green", "red")
size <- c("s", "m", "l", "xl", "l", "s", "m", "xl", "l","m")
city <- c("NY","LA", "DC","NY","LA", "DC","NY","LA", "DC", "NY")
delivery <- c(3, 7, 3, 10, 20, 5, 10, 2, 12,4)
df <- data.frame(year,
                 Price,
                 colors1,
                 size, city,
                 delivery)

df$Vatprice <- df$Price * 1.2

str(df)

library(shiny)
library(ggplot2)

ui <- fluidPage(
  sidebarPanel(
    sliderInput("range",
                "Year:", min = 1990,
                max = 2040, value = c(2000, 2020)),
    textOutput("SliderText"),
    selectInput("yaxis",
                "Y Variable",
                names(df[,c(2,7)]),
                selected = names(df)[[2]]),
    radioButtons(inputId = "cat",
                 label = "Categorized by:",
                 names(df[,3:5]))
  ),
  mainPanel(
    plotOutput("scatter")
  )
)

server <- function(input, output){

  output$scatter <- renderPlot({

    select_row_year <- df$year %in% input$range[1]:input$range[2]
    y_indices <- which(colnames(df) == input$yaxis)
    color <- which(colnames(df) == input$cat)

    ggplot(df[select_row_year, ], aes_string(x = "year",
                                             y = colnames(df)[y_indices],
                                             color = colnames(df)[color])) + 
      geom_point() +
      labs(title = "Overview", color = "Legend") +
      xlab("Year") + ylab(input$yaxis) + theme_minimal()
  })
  }

I hope it will help you. I made your legend y axis interactive.

Edit 1 :

Is it something like this that you are looking for :

ui <- fluidPage(
  sidebarPanel(
    sliderInput("range",
                "Year:", min = 1990,
                max = 2040, value = c(2000, 2020)),
    textOutput("SliderText"),
    selectInput("yaxis",
                "Y Variable",
                names(df[,c(2,7)]),
                selected = names(df)[[2]]),
    radioButtons(inputId = "cat",
                 label = "Categorized by:",
                 names(df[,3:5])),
    checkboxGroupInput(inputId = "city",
                       label = "No city choice",
                       choices = NULL,
                       selected = NULL)
  ),
  mainPanel(
    plotOutput("scatter")
  )
)

server <- function(input, output, session){
  observe({
    if(input$cat  == "city"){
     updateCheckboxGroupInput(session=session, inputId = "city",
                             label = "Select city",
                             choices = levels(df$city),
                             selected = NULL)
    }
  })

  output$scatter <- renderPlot({
    if(length(input$city) != 0){
      select_row <- df$year %in% input$range[1]:input$range[2] & df$city %in% input$city
    }else{
      select_row <- df$year %in% input$range[1]:input$range[2]
    }
    y_indices <- which(colnames(df) == input$yaxis)
    color <- which(colnames(df) == input$cat)

    ggplot(df[select_row, ], aes_string(x = "year",
                                             y = colnames(df)[y_indices],
                                             color = colnames(df)[color])) + 
      geom_point() +
      labs(title = "Overview", color = "Legend") +
      xlab("Year") + ylab(input$yaxis) + theme_minimal()
  })
  }

shinyApp(ui = ui, server = server)
Rémi Coulaud
  • 1,684
  • 1
  • 8
  • 19
  • Thank you very much ! This works also very good! Maybe one additional question here, since you already wrote the code, what would I have to change to make a sort of a filter. So that I can chose for instance only DC or DC and NY or all 3 cities ?Do i have to use checkbox instead of radioButton? Or do I have to build some sort of a filter ? In case it does not take you to much time could you show me ont he code you provided what you would change in order to make a filter ? – Newbie May 11 '19 at 14:28