rreactablereactablefmtr

How to efficiently use pill_buttons() in reactablefmtr for multiple variables?


I have a data frame that contains ~15 variables of interest, and I'm aiming to conditionally fill the values based on some criteria using the pill_buttons() function in the reactablefmtr package.

The dummy data below attempts to simulate my real data, but for the purpose of this post, I've just gone with 3 variables instead of 15 or so. var1_z, var2_z etc. are z-scores that correspond to their namesake column. I then create additional columns that reference the colour I'd like the respective values to be coloured in the table.

library(tidyverse)
library(reactable)
library(reactablefmtr)

cell_bg <- function(x){
  case_when(
    x > 1.5 ~ "red",
    x < -1.5 ~ "lightblue",
    .default = "transparent"
    )
  }

set.seed(1)
ex <- data.frame(
  date = seq(Sys.Date(), Sys.Date() - 9, -1),
  var1 = round(rnorm(10, 8000, 500)),
  var2 = round(rnorm(10, 500, 50)),
  var3 = round(rnorm(10, 100, 20)),
  var1_z = round(runif(10, -3, 3), 2),
  var2_z = round(runif(10, -3, 3), 2),
  var3_z = round(runif(10, -3, 3), 2)
  ) %>%
  mutate(
    across(
      .cols = var1_z:var3_z,
      .fns = cell_bg,
      .names = "{.col}_pal"
      )
    )

I'm familiar with how to use the pill_buttons() function, as displayed below on my dummy data set. However, its repetitive and tedious to complete each function for each variable as I have below, as well as coding colDef(show = FALSE) for every column I wish not to display in the final output. As I'm sure you can understand, having to repeat these steps for many more columns seems overkill.

reactable(ex,
          highlight = TRUE,
          pagination = FALSE,
          defaultColDef = colDef(
            align = "center",
            headerVAlign = "center",
            html = TRUE
            ),
          columns = list(
            date = colDef(sticky = "left"),
            var1 = colDef(cell = pill_buttons(ex, color_ref = "var1_z_pal")),
            var2 = colDef(cell = pill_buttons(ex, color_ref = "var2_z_pal")),
            var3 = colDef(cell = pill_buttons(ex, color_ref = "var3_z_pal")),
            var1_z = colDef(show = FALSE),
            var2_z = colDef(show = FALSE),
            var3_z = colDef(show = FALSE),
            var1_z_pal = colDef(show = FALSE),
            var2_z_pal = colDef(show = FALSE),
            var3_z_pal = colDef(show = FALSE)
            )
          )

I know I can use other table packages, such as kableExtra that may streamline my workflow, but I'm set on using reactablefmtr and the pill_buttons() function as I like the aesthetic. I'm hoping someone may have a solution where I can make my code to create the desired output more efficient without having to write many, many lines.

enter image description here


Solution

  • Here is an example where every colDef(show = FALSE) and every colDef(cell = pill_buttons(...)) is generated dynamically without writing it down for every relevant column. This can be extended to an arbitrary number of columns.

    We use lapply here in order to generate a list containing the different colDef and additionally name these list entries such that they match the relevant column names.

    library(tidyverse)
    library(reactable)
    library(reactablefmtr)
    
    # columns with coldef(show = FALSE)
    colsNotShow <- ex |> select(contains("z")) |> colnames()
    
    # generate coldef for these ones
    coldefs <- lapply(1:length(colsNotShow), \(x) colDef(show = FALSE))
    names(coldefs) <- colsNotShow
    
    # columns with pill_buttons function
    colsShowPillButtons <- ex |> select(starts_with("var") & !contains("z")) |> colnames()
    
    coldefsShowPillButtons <- lapply(colsShowPillButtons,
                                \(x) colDef(
                                  cell = pill_buttons(ex,
                                                      color_ref = paste0(
                                                        x,"_z_pal"))))
    
    names(coldefsShowPillButtons) <- colsShowPillButtons
    
    # combine them and add the date colDef
    coldefs <- c(coldefs, coldefsShowPillButtons, list(date = colDef(sticky = "left")))
    
    reactable(ex,
              highlight = TRUE,
              pagination = FALSE,
              defaultColDef = colDef(
                align = "center",
                headerVAlign = "center",
                html = TRUE
              ),
              columns = coldefs)
    

    Result:

    enter image description here


    Data:

    cell_bg <- function(x){
      case_when(
        x > 1.5 ~ "red",
        x < -1.5 ~ "lightblue",
        .default = "transparent"
      )
    }
    
    set.seed(1)
    ex <- data.frame(
      date = seq(Sys.Date(), Sys.Date() - 9, -1),
      var1 = round(rnorm(10, 8000, 500)),
      var2 = round(rnorm(10, 500, 50)),
      var3 = round(rnorm(10, 100, 20)),
      var1_z = round(runif(10, -3, 3), 2),
      var2_z = round(runif(10, -3, 3), 2),
      var3_z = round(runif(10, -3, 3), 2)
    ) %>%
      mutate(
        across(
          .cols = var1_z:var3_z,
          .fns = cell_bg,
          .names = "{.col}_pal"
        )
      )