knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
library(reactablefmtr)
library(tidyverse)
library(gapminder)

With {reactablefmtr}, we can easily apply color scales to columns within a table by using color_scales() within style of reactable::colDef().

By default a normalized orange-white-blue color scale is applied to the column.

data <- MASS::Cars93 %>% 
  filter(Type %in% c("Compact", "Sporty", "Van")) %>% 
  select(c("Make", "Type", "MPG.city", "MPG.highway")) %>% 
  tail(10)

reactable(
  data,
  defaultSorted = "MPG.city",
  defaultSortOrder = "desc",
  columns = list(
    MPG.city = colDef(
      style = color_scales(data)
    )
  )
)

Custom color palettes

If we want to show a different color palette than the default, we can call them within the colors argument like so:

my_color_pal = c("#e5f5e0", "#a1d99b", "#31a354")

reactable(
  data,
  defaultSorted = "MPG.city",
  defaultSortOrder = "desc",
  columns = list(
    MPG.city = colDef(
      style = color_scales(data, colors = my_color_pal)
      ),
    MPG.highway = colDef(
      style = color_scales(data, colors = my_color_pal)
      )
  )
)

The opacity of the colors can be controlled by assigning a value between 0 (fully transparent) and 1 (fully opaque).

reactable(data,
  defaultSorted = "MPG.city",
  defaultSortOrder = "desc",
  columns = list(
    MPG.city = colDef(
      style = color_scales(data, colors = my_color_pal, opacity = 0.5)
      ),
    MPG.highway = colDef(
      style = color_scales(data, colors = my_color_pal, opacity = 0.5)
      )
  )
)

The order of the color palette matters. In the example above, the color purple is assigned to the lowest values, lightgrey is assigned to middle values, and green is assigned to highest values.

We may also use color palettes from other packages, such as the "Mako" color palette from the {viridis} package:

library(viridis)

reactable(
  data,
  defaultSorted = "MPG.city",
  columns = list(
    MPG.city = colDef(
      style = color_scales(data, colors = viridis::mako(5))
      ),
    MPG.highway = colDef(
      style = color_scales(data, colors = viridis::mako(5))
      )
  )
)

Adding a legend

If you would like to add a legend for the color palette used within color_scales(), you can do so by including add_legend() below the table and listing the color palette used within color_scales(). If no color palette is defined by the user within add_legend(), it will show the default blue-to-orange color palette used in color_scales().

reactable(
  data,
  defaultSorted = "MPG.city",
  columns = list(
    MPG.city = colDef(
      style = color_scales(data, colors = viridis::mako(5))
      ),
    MPG.highway = colDef(
      style = color_scales(data, colors = viridis::mako(5))
      )
  )
) %>% 
  add_legend(colors = viridis::mako(5))


By default, the labels next to the legend show as "Low" and "High". You can hide these labels by setting show_labels to FALSE or provide your own custom labels by providing one text label for the low values and one text label for the high values within labels as shown below:

reactable(
  data,
  defaultSorted = "MPG.city",
  columns = list(
    MPG.city = colDef(
      style = color_scales(data, colors = viridis::mako(5))
      ),
    MPG.highway = colDef(
      style = color_scales(data, colors = viridis::mako(5))
      )
  )
) %>% 
  add_legend(colors = viridis::mako(5), labels = c("Low MPG", "High MPG"))


The position of the legend can be either "above" or "below" the table as specified with position, and the horizontal alignment can be either "left", "right", or "center" with align.

The margin around the legend can also be adjusted using margin and specifying the top, right, bottom, and left margin sizes in that order.

reactable(
  data,
  defaultSorted = "MPG.city",
  columns = list(
    MPG.city = colDef(
      style = color_scales(data, colors = viridis::mako(5))
      ),
    MPG.highway = colDef(
      style = color_scales(data, colors = viridis::mako(5))
      )
  )
) %>% 
  add_legend(colors = viridis::mako(5), labels = c("Low MPG", "High MPG"), position = "above", align = "left", margin = margin(10, 0, 10, 0))

Formatting numbers

To format the numbers within columns containing color_scales(), you can use any of the formats within reactable's colFormat() argument:

reactable(
  data,
  defaultSorted = "MPG.city",
  defaultSortOrder = "desc",
  columns = list(
    MPG.city = colDef(
      format = colFormat(suffix = " mpg"),
      style = color_scales(data)
    )
  )
)

Conditional color assignments

Colors can be conditionally assigned to values based on another column by using color_ref.

In the example below, we assigned a blue color to Compact cars, a red color to Sporty cars, and a gold color to Vans using dplyr::case_when().

Then within color_scales(), we reference the name of the conditional column we just created to apply the colors to the values in MPG.city and MPG.highway.

car_data <- data %>% 
  mutate(car_colors = dplyr::case_when(
  Type == "Compact" ~ "dodgerblue",
  Type == "Sporty" ~ "tomato",
  Type == "Van" ~ "gold",
  TRUE ~ "other"
  ))

reactable(
  car_data,
  defaultSorted = "Type",
  columns = list(
    MPG.city = colDef(style = color_scales(car_data, color_ref = "car_colors")),
    MPG.highway = colDef(style = color_scales(car_data, color_ref = "car_colors")),
    car_colors = colDef(show = FALSE))
)

We can further apply the conditional colors to the entire dataset by setting the style within defaultColDef:

reactable(
  car_data,
  defaultSorted = "Type",
  defaultColDef = colDef(
    style = color_scales(car_data, color_ref = "car_colors")
    ),
  columns = list(car_colors = colDef(show = FALSE))
)

The same conditional coloring can be applied based on numeric conditions as well. For example, if we wanted to highlight which cars have an MPG.city value of 23 or greater, we could use the same method as above but apply the conditions based on the MPG.city column instead of the Type column.

car_data <- car_data %>% 
  mutate(mpg_colors = dplyr::case_when(
  MPG.city >= 23 ~ "darkgreen",
  TRUE ~ "grey"
  ))

reactable(
  car_data,
  defaultSorted = "MPG.city",
  defaultSortOrder = "desc",
  defaultColDef = colDef(
    style = color_scales(car_data, color_ref = "mpg_colors")
    ),
  columns = list(
    car_colors = colDef(show = FALSE),
    mpg_colors = colDef(show = FALSE))
)

Row-wise styling

By default, color_scales() conditionally assigns colors to values based on their relation to other values within that particular column. However, if the table you're showing is row-wise data, such as average temperatures by month for each year, then it will be difficult to compare how temperatures compare in each month:

By including span = TRUE within our color_scales() formatter, we can conditionally assign colors to the values based on their relation to other values within the entire dataset, instead of within each column. Now our table displaying temperatures is much easier to read when comparing temperatures across months:

Note: the dataset for this example is sourced from the reactable demo cookbook

dimnames <- list(start(nottem)[1]:end(nottem)[1], month.abb)
temps <- matrix(nottem, ncol = 12, byrow = TRUE, dimnames = dimnames)
temps <- as_tibble(temps, rownames = "Year")
temppal <- c("#36a1d6", "#76b8de", "#a0bfd9", "#ffffff", "#d88359", "#d65440", "#c62c34")

reactable(
  temps,
  defaultColDef = colDef(
    style = color_scales(temps, span = TRUE, colors = temppal),
    minWidth = 50
    )
)

If we only wanted to apply color scales to some of the months, we can do so by referencing either the numeric positions of the columns or the column names within span:

reactable(
  temps,
  defaultColDef = colDef(
    style = color_scales(temps, span = 4:7, colors = temppal),
    minWidth = 50))

Lastly, if you wanted to completely hide the text, you could do this by setting show_text = FALSE, which displays the table as a heatmap as shown below:

Note: by including cell = tooltip(), the values are still visible on hover and the format of the numbers can be specified with number_fmt.

population_data <- gapminder %>% 
  filter(continent == "Americas") %>%
  mutate(country = as.character(country),
         year = paste0("'", str_sub(year, 3, 4))) %>% 
  select(country, year, lifeExp) %>%
  pivot_wider(names_from = year, values_from = lifeExp) 

reactable(
  population_data,
  compact = TRUE,
  pagination = FALSE,
  showSortIcon = FALSE,
  defaultSorted = "'52",
  defaultSortOrder = "desc",
  defaultColDef = colDef(
    maxWidth = 50,
    align = "center",
    cell = tooltip(number_fmt = scales::comma),
    style = color_scales(population_data, show_text = FALSE, span = TRUE)
  ),
  columns = list(
    country = colDef(
      maxWidth = 175,
      align = "left"
    )
  )
) %>% 
  add_title("Average Life Expectancy") %>% 
  add_source("Data sourced from the {gapminder} package") 


kcuilla/reactablefmtr documentation built on Jan. 13, 2023, 11:36 p.m.