Search code examples
rplotly

Add a line to a bar chart in Plotly based on a selected variable from a dropdown menu in R


I am using Plotly to generate multiple bar charts based on a dropdown menu in R. My dataset contains hundreds of variables, and I am able to create the dropdown menu automatically thanks to this function I found here :


create_buttons <- function(df, y_axis_var_names) {
  lapply(
    y_axis_var_names,
    FUN = function(var_name, df) {
      button <- list(
        method = 'restyle',
        args = list('y', list(df[, var_name])),
        label = sprintf(var_name) #'Show %s', 
      )
    },
    df
  )
  
}

However, I am struggling to automatically add a line for each bar chart based on the variable selected in the dropdown menu. The dropdown menu itself is generated successfully, but I can't seem to find a way to add a line for each selected variable.

Here's an example dataset I am using:

structure(list(YW = structure(1:58, .Label = c("47-2021", "48-2021", 
"49-2021", "50-2021", "51-2021", "52-2021", "1-2022", "2-2022", 
"3-2022", "4-2022", "5-2022", "6-2022", "7-2022", "8-2022", "9-2022", 
"10-2022", "11-2022", "12-2022", "13-2022", "14-2022", "15-2022", 
"16-2022", "17-2022", "18-2022", "19-2022", "20-2022", "21-2022", 
"22-2022", "23-2022", "24-2022", "25-2022", "26-2022", "27-2022", 
"28-2022", "29-2022", "30-2022", "31-2022", "32-2022", "33-2022", 
"34-2022", "35-2022", "36-2022", "37-2022", "38-2022", "39-2022", 
"40-2022", "41-2022", "42-2022", "43-2022", "44-2022", "45-2022", 
"46-2022", "47-2022", "48-2022", "49-2022", "50-2022", "51-2022", 
"52-2022"), class = "factor"), X1 = c(0, 0, 0, 2, 0, 2, 0, 6, 
3, 2, 2, 4, 0, 0, 0, 0, 2, 2, 2, 5, 2, 3, 2, 2, 2, 2, 0, 2, 0, 
0, 2, 4, 4, 2, 2, 0, 0, 0, 2, 0, 6, 4, 4, 6, 6, 13, 2, 2, 0, 
4, 2, 0, 3, 6, 4, 4, 0, 2), X2 = c(0, 0, 0, 2, 0, 2, 0, 6, 3, 
2, 2, 4, 0, 0, 0, 4, 10, 2, 2, 5, 2, 3, 2, 2, 2, 2, 0, 2, 0, 
0, 2, 4, 4, 2, 2, 0, 0, 2, 2, 0, 6, 6, 4, 6, 6, 13, 2, 2, 0, 
4, 2, 0, 3, 6, 4, 4, 0, 2), X3 = c(0, 0, 0, 2, 0, 2, 0, 6, 2, 
2, 2, 4, 2, 0, 0, 0, 2, 2, 2, 5, 2, 3, 2, 2, 2, 2, 0, 2, 0, 0, 
2, 4, 4, 4, 2, 0, 0, 0, 2, 0, 6, 4, 4, 6, 6, 13, 2, 2, 0, 4, 
4, 0, 3, 6, 4, 4, 0, 2), X4 = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 
0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0)), class = "data.frame", row.names = c(NA, -58L
))


p = plot_ly(data = test, x=~YW, y= ~X1 ,type ="bar", width = 1500, height = 500 )%>%
  config(locale = 'fr')%>%
  layout(autosize=T,
         title = "",
         xaxis = list(title = "Weeks"),
         yaxis = list(title = "Frequency"),
         updatemenus = list(
           list(y = 0.7,
                buttons = create_buttons(test, names(test[,!names(test) %in% "YW"])))))

enter image description here

I also have a separate dataframe that contains the values I want to use for the lines, where each column corresponds to a different variable. For example:

lines_x = data.frame(X1 = 5, X2 = 3, X3 = 4, X4 = 1)

My goal is to have a graph that looks like the one below, where the dropdown menu allows the user to select different variables and the lines update accordingly:

plot_ly(data = test, x=~YW, y= ~X1 ,type ="bar", width = 1500, height = 500 )%>%
  add_lines(y= lines_x$X1)

enter image description here

How can I modify my code to achieve this? Thanks for the help !


Solution

  • I've modified a few things.

    1. The function used to create your buttons: Instead of modifying one trace's y-axis, you need to modify 2. (More on that in a bit.)

    2. The plot: added a second trace for the horizontal lines.

    3. The layout: changed the function call to create button; added 3rd argument.

    The New Function

    There are now three arguments: data frame for the 1st trace, data frame for the 2nd trace (the horizontal lines), and the variable names needed for the y-axis.

    In the args, instead of "y", list(), you'll see y = list(). Inside this list are 2 lists, the data for the first trace df[, vn] (the bar chart) and the data for the second trace df2[, vn] (the lines).

    Plotly wasn't happy with a single value for y when restyling, so I've called the rep to repeat the value for the length of the data on the x-axis.

    creat2 <- function(df, df2, var_n) { # 1st y df, 2nd y df, names
      lapply(
        var_n, 
        function(vn) {
          button <- list(
            method = 'restyle', label = paste0(vn),
            args = list( # a list of 'y's (a list for each trace)
              list(y = list(df[, vn], rep(df2[, vn], nrow(df)))))
          )
        }
      )
    }
    

    The Modified Plot and Layout

    I've added parentheses around the plot and the assignment operator; this allows you to update an object and print the object at the same time.

    Plot specifically: add_lines was included; arbitrarily I chose to plot X1. I opted to set the legend to false, since it won't be meaningful, as well.

    Layout specifically: In the buttons assignment, the function name has changed to creat2 and the parameters now include 2 data frames and names.

    (p = plot_ly(data = test, x = ~YW, y = ~X1, type ="bar", width = 1500, height = 500 ) %>% 
      config(locale = 'fr') %>% 
      add_lines(y = lines_x$X1, showlegend = F) %>% # hide legend w/ multi traces
      layout(autosize = T,
             title = "",
             xaxis = list(title = "Weeks"),
             yaxis = list(title = "Frequency"),
             updatemenus = list(list(
               y = 0.7,
               buttons = creat2(test, lines_x,
                                names(test[,!names(test) %in% "YW"]))))))
    

    In these images, I've commented out the plot size designation. It makes it a bit easier to use my viewer pane.

    enter image description here

    enter image description here

    enter image description here