Formattable data frames are data frames to be rendered as HTML table with formatter functions applied, which resembles conditional formatting in Microsoft Excel.

Simple examples

Suppose we have the following data frame:

scores <- data.frame(id = 1:5,
  prev_score = c(10, 8, 6, 8, 8),
  cur_score = c(8, 9, 7, 8, 9),
  change = c(-2, 1, 1, 0, 1))

In the console, it is printed as plain texts:

scores
##   id prev_score cur_score change
## 1  1         10         8     -2
## 2  2          8         9      1
## 3  3          6         7      1
## 4  4          8         8      0
## 5  5          8         9      1

Using knitr::kable() or formattable(), the data frame can be rendered as HTML table which looks more friendly.

id prev_score cur_score change
1 10 8 -2
2 8 9 1
3 6 7 1
4 8 8 0
5 8 9 1

In fact, formattable() calls knitr::kable() internally to translate data frame to HTML code. In addition, formattable() supports formatter functions to customize the transformation between values in the data frame to HTML code to generate.

plain_formatter <- formatter("span")
plain_formatter(c(1, 2, 3))
## [1] "<span>1</span>" "<span>2</span>" "<span>3</span>"
width_formatter <- formatter("span",
  style = x ~ style(width = suffix(x, "px")))
width_formatter(c(10, 11, 12))
## [1] "<span style=\"width: 10px\">10</span>"
## [2] "<span style=\"width: 11px\">11</span>"
## [3] "<span style=\"width: 12px\">12</span>"

The values of change can be positive, negative or zero. We can make positives green, negatives red, and zeros black by creating a formatter function that performs conditional transformation from value to HTML code.

sign_formatter <- formatter("span", 
  style = x ~ style(color = ifelse(x > 0, "green", 
    ifelse(x < 0, "red", "black"))))
sign_formatter(c(-1, 0, 1))
## [1] "<span style=\"color: red\">-1</span>" 
## [2] "<span style=\"color: black\">0</span>"
## [3] "<span style=\"color: green\">1</span>"

Note that we don’t have to write HTML but use helper functions like style() and ifelse() to make it easier to specify conditions. Then we call formattable() on the data frame with a list of formatter functions so as to apply conditional formatting.

formattable(scores, list(change = sign_formatter))
id prev_score cur_score change
1 10 8 -2
2 8 9 1
3 6 7 1
4 8 8 0
5 8 9 1

We can also create another formatter function that makes above-average values bold while leaving others unchanged.

above_avg_bold <- formatter("span", 
  style = x ~ style("font-weight" = ifelse(x > mean(x), "bold", NA)))
formattable(scores, list(
  prev_score = above_avg_bold,
  cur_score = above_avg_bold,
  change = sign_formatter))
id prev_score cur_score change
1 10 8 -2
2 8 9 1
3 6 7 1
4 8 8 0
5 8 9 1

Cross formatting

Sometimes, we need to format one column based on the values of another column. This can be easily done with one-sided formula in formatter(). When using formatter("span", style = ~ expr), expr is evaluated in the data frame so that all columns are available for use.

formattable(scores, list(
  cur_score = formatter("span", 
    style = ~ style(color = ifelse(change >= 0, "green", "red")))))
id prev_score cur_score change
1 10 8 -2
2 8 9 1
3 6 7 1
4 8 8 0
5 8 9 1

Hiding columns

To hide columns, use FALSE formatter.

formattable(scores, list(prev_score = FALSE))
id cur_score change
1 8 -2
2 9 1
3 7 1
4 8 0
5 9 1

Using built-in formatters

To making formatting easier, formattable package provides a group of built-in formatter functions. Suppose we have the following data on a number of products. Some columns are already formattable vectors.

products <- data.frame(id = 1:5, 
  price = c(10, 15, 12, 8, 9),
  rating = c(5, 4, 4, 3, 4),
  market_share = percent(c(0.1, 0.12, 0.05, 0.03, 0.14)),
  revenue = accounting(c(55000, 36400, 12000, -25000, 98100)),
  profit = accounting(c(25300, 11500, -8200, -46000, 65000)))
products
##   id price rating market_share     revenue      profit
## 1  1    10      5       10.00%   55,000.00   25,300.00
## 2  2    15      4       12.00%   36,400.00   11,500.00
## 3  3    12      4        5.00%   12,000.00  (8,200.00)
## 4  4     8      3        3.00% (25,000.00) (46,000.00)
## 5  5     9      4       14.00%   98,100.00   65,000.00

Without any formatter functions applied, the formattable data frame is directly rendered as an HTML table.

formattable(products)
id price rating market_share revenue profit
1 10 5 10.00% 55,000.00 25,300.00
2 15 4 12.00% 36,400.00 11,500.00
3 12 4 5.00% 12,000.00 (8,200.00)
4 8 3 3.00% (25,000.00) (46,000.00)
5 9 4 14.00% 98,100.00 65,000.00

We can supply a list of formatter functions to make it look more colorful. For example, we apply sign_formatter to profit column so that values of different signs are displayed in different colors.

formattable(products, list(profit = sign_formatter))
id price rating market_share revenue profit
1 10 5 10.00% 55,000.00 25,300.00
2 15 4 12.00% 36,400.00 11,500.00
3 12 4 5.00% 12,000.00 (8,200.00)
4 8 3 3.00% (25,000.00) (46,000.00)
5 9 4 14.00% 98,100.00 65,000.00

Using built-in functions like color_tile() and color_bar() makes it easier to compare the magnitute of values of specified columns.

formattable(products, list(
  price = color_tile("transparent", "lightpink"),
  rating = color_bar("lightgreen"),
  market_share = color_bar("lightblue"),
  revenue = sign_formatter,
  profit = sign_formatter))
id price rating market_share revenue profit
1 10 5 10.00% 55,000.00 25,300.00
2 15 4 12.00% 36,400.00 11,500.00
3 12 4 5.00% 12,000.00 (8,200.00)
4 8 3 3.00% (25,000.00) (46,000.00)
5 9 4 14.00% 98,100.00 65,000.00

Area formatting

Sometimes, it is useful to apply a formatter function to an area so that all cells in the area share one benchmark. Area formatting is supported through the syntax of area(row, col) ~ formatter in the formatter list.

The following example renders the three columns altogether so that they share the same benchmark, not independently.

set.seed(123)
df <- data.frame(id = 1:10, 
  a = rnorm(10), b = rnorm(10), c = rnorm(10))
formattable(df, list(area(col = a:c) ~ color_tile("transparent", "pink")))
id a b c
1 -0.56047565 1.2240818 -1.0678237
2 -0.23017749 0.3598138 -0.2179749
3 1.55870831 0.4007715 -1.0260044
4 0.07050839 0.1106827 -0.7288912
5 0.12928774 -0.5558411 -0.6250393
6 1.71506499 1.7869131 -1.6866933
7 0.46091621 0.4978505 0.8377870
8 -1.26506123 -1.9666172 0.1533731
9 -0.68685285 0.7013559 -1.1381369
10 -0.44566197 -0.4727914 1.2538149

If a one-sided formula is supplied, the function will be applied to all cells.

formattable(df[, -1], list(~ percent))
a b c
-56.05% 122.41% -106.78%
-23.02% 35.98% -21.80%
155.87% 40.08% -102.60%
7.05% 11.07% -72.89%
12.93% -55.58% -62.50%
171.51% 178.69% -168.67%
46.09% 49.79% 83.78%
-126.51% -196.66% 15.34%
-68.69% 70.14% -113.81%
-44.57% -47.28% 125.38%

Dynamically generating formatters

Since formattable() accepts a list of formatter functions, the list can be dynamically generated. For example, the following code applies row-wise formatting, that is, each row is colored independently.

df <- cbind(data.frame(id = 1:10), 
  do.call(cbind, lapply(1:8, function(x) rnorm(10))))
formattable(df, lapply(1:nrow(df), function(row) {
  area(row, col = -1) ~ color_tile("lightpink", "lightblue")
}))
id 1 2 3 4 5 6 7 8
1 0.42646422 -0.69470698 0.25331851 0.37963948 -0.4910312 0.005764186 0.9935039 -0.71040656
2 -0.29507148 -0.20791728 -0.02854676 -0.50232345 -2.3091689 0.385280401 0.5483970 0.25688371
3 0.89512566 -1.26539635 -0.04287046 -0.33320738 1.0057385 -0.370660032 0.2387317 -0.24669188
4 0.87813349 2.16895597 1.36860228 -1.01857538 -0.7092008 0.644376549 -0.6279061 -0.34754260
5 0.82158108 1.20796200 -0.22577099 -1.07179123 -0.6880086 -0.220486562 1.3606524 -0.95161857
6 0.68864025 -1.12310858 1.51647060 0.30352864 1.0255714 0.331781964 -0.6002596 -0.04502772
7 0.55391765 -0.40288484 -1.54875280 0.44820978 -0.2847730 1.096839013 2.1873330 -0.78490447
8 -0.06191171 -0.46665535 0.58461375 0.05300423 -1.2207177 0.435181491 1.5326106 -1.66794194
9 -0.30596266 0.77996512 0.12385424 0.92226747 0.1813035 -0.325931586 -0.2357004 -0.38022652
10 -0.38047100 -0.08336907 0.21594157 2.05008469 -0.1388914 1.148807618 -1.0264209 0.91899661

Converting to DT::datatables

as.datatable() is designed to convert a formattable data frame to DT::datatables.

Some formatters can be preserved well after the conversion.

as.datatable(formattable(products, list(
  price = color_tile("transparent", "lightpink"),
  revenue = sign_formatter,
  profit = sign_formatter)))