Chapter 3 Table design
3.1 Table parts
A flextable object is composed of 3 parts:
- header: by default, there is only one header row containing the names of the data.frame.
- body: the body part is containing data from the data.frame.
- footer: the footer part is not implemented by default but can contains footnotes or any content.
Ozone |
Solar.R |
Wind |
Temp |
Month |
Day |
---|---|---|---|---|---|
41 |
190 |
7.4 |
67 |
5 |
1 |
36 |
118 |
8.0 |
72 |
5 |
2 |
12 |
149 |
12.6 |
74 |
5 |
3 |
18 |
313 |
11.5 |
62 |
5 |
4 |
<na> |
<na> |
14.3 |
56 |
5 |
5 |
28 |
<na> |
14.9 |
66 |
5 |
6 |
The parts can be augmented with new lines.
ft <- add_header_row(ft, values = c("air quality", "time"), colwidths = c(4, 2))
ft <- add_footer_lines(ft, "hello note")
ft
air quality |
time |
||||
---|---|---|---|---|---|
Ozone |
Solar.R |
Wind |
Temp |
Month |
Day |
41 |
190 |
7.4 |
67 |
5 |
1 |
36 |
118 |
8.0 |
72 |
5 |
2 |
12 |
149 |
12.6 |
74 |
5 |
3 |
18 |
313 |
11.5 |
62 |
5 |
4 |
<na> |
<na> |
14.3 |
56 |
5 |
5 |
28 |
<na> |
14.9 |
66 |
5 |
6 |
hello note |
3.2 Visible columns
A flextable is an object that will produce a reporting table from a data.frame
object.
Parameter col_keys
of function flextable
define the variables to show as
columns and their order. Visible columns are by default all columns of the
data.frame.
myft <- flextable(
data = head(airquality),
col_keys = c("Ozone", "Solar.R", "Wind", "Temp",
"Month", "Day"))
myft
Ozone |
Solar.R |
Wind |
Temp |
Month |
Day |
---|---|---|---|---|---|
41 |
190 |
7.4 |
67 |
5 |
1 |
36 |
118 |
8.0 |
72 |
5 |
2 |
12 |
149 |
12.6 |
74 |
5 |
3 |
18 |
313 |
11.5 |
62 |
5 |
4 |
<na> |
<na> |
14.3 |
56 |
5 |
5 |
28 |
<na> |
14.9 |
66 |
5 |
6 |
They can be choosen from a subset of existing columns and also non-existent
columns. Non-existent columns contain blanks by default (""
).
When parameter col_keys
has names that are not existing in the dataset, they
will be considered as blank columns and no mapping exists with the data.
These blank columns can be used as separators.
myft <- flextable(
data = head(airquality),
col_keys = c("Ozone", "Solar.R", "col1", "Month", "Day")) |>
width(j = "col1", width = .2) |>
empty_blanks()
myft
Ozone |
Solar.R |
Month |
Day |
|
---|---|---|---|---|
41 |
190 |
5 |
1 |
|
36 |
118 |
5 |
2 |
|
12 |
149 |
5 |
3 |
|
18 |
313 |
5 |
4 |
|
<na> |
<na> |
5 |
5 |
|
28 |
<na> |
5 |
6 |
Their content can also be defined with functions such as mk_par()
or append_chunks()
.
In the following example we show how to highlight some rows according to a condition; if the condition is true, a colored text is added in the blank column.
library(grid)
myft |>
append_chunks(
i = ~ Ozone < 30, j = "col1",
as_chunk(";)", props = fp_text_default(color = "#ec11c2")))
Ozone |
Solar.R |
Month |
Day |
|
---|---|---|---|---|
41 |
190 |
5 |
1 |
|
36 |
118 |
5 |
2 |
|
12 |
149 |
;) |
5 |
3 |
18 |
313 |
;) |
5 |
4 |
<na> |
<na> |
5 |
5 |
|
28 |
<na> |
;) |
5 |
6 |
By default, the cells are filled with the content of the data.frame cells, the
values are formatted with the function format
. The displayed content of the
cells can be updated, i.e. you can define the decimal separator, the text
representing missing values, a text to be used as a suffix or prefix.
With flextable, a cell is made of one single paragraph of text. Paragraphs can contain several chunks of text with different formatting but also images and equations. It is possible to create composite content, i.e. composed of several pieces of text, images and even ggplot graphics.
# data prep ----
z <- as.data.table(ggplot2::diamonds)
z <- z[, list(
price = mean(price, na.rm = TRUE),
list_col = list(.SD$x)
), by = "cut"]
# flextable ----
ft <- flextable(data = z) %>%
mk_par(j = "list_col", value = as_paragraph(
plot_chunk(value = list_col, type = "dens", col = "#ec11c2",
width = 1.5, height = .4, free_scale = TRUE)
)) %>%
colformat_double(big.mark = " ", suffix = " $") %>%
set_header_labels(box = "composite content", density = "density") %>%
autofit()
ft
cut |
price |
list_col |
---|---|---|
Ideal |
3 457.54 $ |
|
Premium |
4 584.26 $ |
|
Good |
3 928.86 $ |
|
Very Good |
3 981.76 $ |
|
Fair |
4 358.76 $ |
3.3 Selectors
Selectors can be used to specify the rows and columns where an operation should happened.
Many flextable
functions have selectors i
and j
: bg()
, bold()
,
hline()
, color()
, padding()
, fontsize()
, italic()
, align()
, compose()
.
It makes conditional formatting easy and multiple
operations can be seamlessly piped (with magrittr::%>%
or |>
).
dat <- head(ggplot2::diamonds, n = 10)
qflextable(dat) %>%
color(~ price < 330, color = "orange", ~ price + x + y + z ) %>%
color(~ carat > .24, ~ cut, color = "red")
carat |
cut |
color |
clarity |
depth |
table |
price |
x |
y |
z |
---|---|---|---|---|---|---|---|---|---|
0.23 |
Ideal |
E |
SI2 |
61.5 |
55 |
326 |
3.95 |
3.98 |
2.43 |
0.21 |
Premium |
E |
SI1 |
59.8 |
61 |
326 |
3.89 |
3.84 |
2.31 |
0.23 |
Good |
E |
VS1 |
56.9 |
65 |
327 |
4.05 |
4.07 |
2.31 |
0.29 |
Premium |
I |
VS2 |
62.4 |
58 |
334 |
4.20 |
4.23 |
2.63 |
0.31 |
Good |
J |
SI2 |
63.3 |
58 |
335 |
4.34 |
4.35 |
2.75 |
0.24 |
Very Good |
J |
VVS2 |
62.8 |
57 |
336 |
3.94 |
3.96 |
2.48 |
0.24 |
Very Good |
I |
VVS1 |
62.3 |
57 |
336 |
3.95 |
3.98 |
2.47 |
0.26 |
Very Good |
H |
SI1 |
61.9 |
55 |
337 |
4.07 |
4.11 |
2.53 |
0.22 |
Fair |
E |
VS2 |
65.1 |
61 |
337 |
3.87 |
3.78 |
2.49 |
0.23 |
Very Good |
H |
VS1 |
59.4 |
61 |
338 |
4.00 |
4.05 |
2.39 |
Default values for i
and j
are NULL
, NULL
value is interpreted as
all rows or all columns.
3.3.1 Usage
i
for rows selection and j
for columns selection can be expressed in different ways:
3.3.1.1 as a formula
Use i = ~ col %in% "xxx"
to select all rows where
values from column ‘col’ are “xxx”.
The argument expression is ~
and then an R expression. There is no need to quote
variable, when the formula will be evaluated, values from the corresponding dataset
(to the part) will be used.
To express multiple conditions, use operator &
or |
:
i = ~ col1 < 5 & col2 %in% "zzz"
The columns can be selected with a formula also. Use operator +
for multiple columns.
To select columns col1
and col2
: j = ~ col1 + col2
ft <- qflextable(dat)
color(
ft, i = ~ cut %in% "Premium",
j = ~ x + y, color = "red")
carat |
cut |
color |
clarity |
depth |
table |
price |
x |
y |
z |
---|---|---|---|---|---|---|---|---|---|
0.23 |
Ideal |
E |
SI2 |
61.5 |
55 |
326 |
3.95 |
3.98 |
2.43 |
0.21 |
Premium |
E |
SI1 |
59.8 |
61 |
326 |
3.89 |
3.84 |
2.31 |
0.23 |
Good |
E |
VS1 |
56.9 |
65 |
327 |
4.05 |
4.07 |
2.31 |
0.29 |
Premium |
I |
VS2 |
62.4 |
58 |
334 |
4.20 |
4.23 |
2.63 |
0.31 |
Good |
J |
SI2 |
63.3 |
58 |
335 |
4.34 |
4.35 |
2.75 |
0.24 |
Very Good |
J |
VVS2 |
62.8 |
57 |
336 |
3.94 |
3.96 |
2.48 |
0.24 |
Very Good |
I |
VVS1 |
62.3 |
57 |
336 |
3.95 |
3.98 |
2.47 |
0.26 |
Very Good |
H |
SI1 |
61.9 |
55 |
337 |
4.07 |
4.11 |
2.53 |
0.22 |
Fair |
E |
VS2 |
65.1 |
61 |
337 |
3.87 |
3.78 |
2.49 |
0.23 |
Very Good |
H |
VS1 |
59.4 |
61 |
338 |
4.00 |
4.05 |
2.39 |
3.3.1.2 as a character vector
Argument j
supports simple character vector containing the col_key
names.
ft %>%
color(j = c("x", "y"), color = "orange", part = "all") %>%
bold(j = c("price", "x"), bold = TRUE)
carat |
cut |
color |
clarity |
depth |
table |
price |
x |
y |
z |
---|---|---|---|---|---|---|---|---|---|
0.23 |
Ideal |
E |
SI2 |
61.5 |
55 |
326 |
3.95 |
3.98 |
2.43 |
0.21 |
Premium |
E |
SI1 |
59.8 |
61 |
326 |
3.89 |
3.84 |
2.31 |
0.23 |
Good |
E |
VS1 |
56.9 |
65 |
327 |
4.05 |
4.07 |
2.31 |
0.29 |
Premium |
I |
VS2 |
62.4 |
58 |
334 |
4.20 |
4.23 |
2.63 |
0.31 |
Good |
J |
SI2 |
63.3 |
58 |
335 |
4.34 |
4.35 |
2.75 |
0.24 |
Very Good |
J |
VVS2 |
62.8 |
57 |
336 |
3.94 |
3.96 |
2.48 |
0.24 |
Very Good |
I |
VVS1 |
62.3 |
57 |
336 |
3.95 |
3.98 |
2.47 |
0.26 |
Very Good |
H |
SI1 |
61.9 |
55 |
337 |
4.07 |
4.11 |
2.53 |
0.22 |
Fair |
E |
VS2 |
65.1 |
61 |
337 |
3.87 |
3.78 |
2.49 |
0.23 |
Very Good |
H |
VS1 |
59.4 |
61 |
338 |
4.00 |
4.05 |
2.39 |
3.3.1.3 as a integer vector
Each element is the row number or col_key
number:
color(ft, i = 1:3, j = 1:3, color = "orange")
carat |
cut |
color |
clarity |
depth |
table |
price |
x |
y |
z |
---|---|---|---|---|---|---|---|---|---|
0.23 |
Ideal |
E |
SI2 |
61.5 |
55 |
326 |
3.95 |
3.98 |
2.43 |
0.21 |
Premium |
E |
SI1 |
59.8 |
61 |
326 |
3.89 |
3.84 |
2.31 |
0.23 |
Good |
E |
VS1 |
56.9 |
65 |
327 |
4.05 |
4.07 |
2.31 |
0.29 |
Premium |
I |
VS2 |
62.4 |
58 |
334 |
4.20 |
4.23 |
2.63 |
0.31 |
Good |
J |
SI2 |
63.3 |
58 |
335 |
4.34 |
4.35 |
2.75 |
0.24 |
Very Good |
J |
VVS2 |
62.8 |
57 |
336 |
3.94 |
3.96 |
2.48 |
0.24 |
Very Good |
I |
VVS1 |
62.3 |
57 |
336 |
3.95 |
3.98 |
2.47 |
0.26 |
Very Good |
H |
SI1 |
61.9 |
55 |
337 |
4.07 |
4.11 |
2.53 |
0.22 |
Fair |
E |
VS2 |
65.1 |
61 |
337 |
3.87 |
3.78 |
2.49 |
0.23 |
Very Good |
H |
VS1 |
59.4 |
61 |
338 |
4.00 |
4.05 |
2.39 |
3.3.1.4 as a logical vector
carat |
cut |
color |
clarity |
depth |
table |
price |
x |
y |
z |
---|---|---|---|---|---|---|---|---|---|
0.23 |
Ideal |
E |
SI2 |
61.5 |
55 |
326 |
3.95 |
3.98 |
2.43 |
0.21 |
Premium |
E |
SI1 |
59.8 |
61 |
326 |
3.89 |
3.84 |
2.31 |
0.23 |
Good |
E |
VS1 |
56.9 |
65 |
327 |
4.05 |
4.07 |
2.31 |
0.29 |
Premium |
I |
VS2 |
62.4 |
58 |
334 |
4.20 |
4.23 |
2.63 |
0.31 |
Good |
J |
SI2 |
63.3 |
58 |
335 |
4.34 |
4.35 |
2.75 |
0.24 |
Very Good |
J |
VVS2 |
62.8 |
57 |
336 |
3.94 |
3.96 |
2.48 |
0.24 |
Very Good |
I |
VVS1 |
62.3 |
57 |
336 |
3.95 |
3.98 |
2.47 |
0.26 |
Very Good |
H |
SI1 |
61.9 |
55 |
337 |
4.07 |
4.11 |
2.53 |
0.22 |
Fair |
E |
VS2 |
65.1 |
61 |
337 |
3.87 |
3.78 |
2.49 |
0.23 |
Very Good |
H |
VS1 |
59.4 |
61 |
338 |
4.00 |
4.05 |
2.39 |
3.3.2 Selectors and flextable parts
Several operations (bold, color, padding, compose) accept part="all"
. In this
case all mean to apply to each part of the flextable (header, body and footer if any).
That’s useful for many cases:
- add a vertical line (
vline
) to one or several column in header and body part, usepart="all", j = c('col1', 'col2')
as selector.
border <- fp_border_default()
vline(ft, j = c('clarity', 'price'), border = border, part = "all")
carat |
cut |
color |
clarity |
depth |
table |
price |
x |
y |
z |
---|---|---|---|---|---|---|---|---|---|
0.23 |
Ideal |
E |
SI2 |
61.5 |
55 |
326 |
3.95 |
3.98 |
2.43 |
0.21 |
Premium |
E |
SI1 |
59.8 |
61 |
326 |
3.89 |
3.84 |
2.31 |
0.23 |
Good |
E |
VS1 |
56.9 |
65 |
327 |
4.05 |
4.07 |
2.31 |
0.29 |
Premium |
I |
VS2 |
62.4 |
58 |
334 |
4.20 |
4.23 |
2.63 |
0.31 |
Good |
J |
SI2 |
63.3 |
58 |
335 |
4.34 |
4.35 |
2.75 |
0.24 |
Very Good |
J |
VVS2 |
62.8 |
57 |
336 |
3.94 |
3.96 |
2.48 |
0.24 |
Very Good |
I |
VVS1 |
62.3 |
57 |
336 |
3.95 |
3.98 |
2.47 |
0.26 |
Very Good |
H |
SI1 |
61.9 |
55 |
337 |
4.07 |
4.11 |
2.53 |
0.22 |
Fair |
E |
VS2 |
65.1 |
61 |
337 |
3.87 |
3.78 |
2.49 |
0.23 |
Very Good |
H |
VS1 |
59.4 |
61 |
338 |
4.00 |
4.05 |
2.39 |
- change color only on the first row of the header part, use
part="header", i = 1
as selector.
color(ft, i = 1, color = "red", part = "header")
carat |
cut |
color |
clarity |
depth |
table |
price |
x |
y |
z |
---|---|---|---|---|---|---|---|---|---|
0.23 |
Ideal |
E |
SI2 |
61.5 |
55 |
326 |
3.95 |
3.98 |
2.43 |
0.21 |
Premium |
E |
SI1 |
59.8 |
61 |
326 |
3.89 |
3.84 |
2.31 |
0.23 |
Good |
E |
VS1 |
56.9 |
65 |
327 |
4.05 |
4.07 |
2.31 |
0.29 |
Premium |
I |
VS2 |
62.4 |
58 |
334 |
4.20 |
4.23 |
2.63 |
0.31 |
Good |
J |
SI2 |
63.3 |
58 |
335 |
4.34 |
4.35 |
2.75 |
0.24 |
Very Good |
J |
VVS2 |
62.8 |
57 |
336 |
3.94 |
3.96 |
2.48 |
0.24 |
Very Good |
I |
VVS1 |
62.3 |
57 |
336 |
3.95 |
3.98 |
2.47 |
0.26 |
Very Good |
H |
SI1 |
61.9 |
55 |
337 |
4.07 |
4.11 |
2.53 |
0.22 |
Fair |
E |
VS2 |
65.1 |
61 |
337 |
3.87 |
3.78 |
2.49 |
0.23 |
Very Good |
H |
VS1 |
59.4 |
61 |
338 |
4.00 |
4.05 |
2.39 |
- change color in column ‘col1’ and ‘col3’ in body part when values of ‘col2’ are negative, use
part="body", i = ~ col2 < 0, j = c('col1', 'col3)
as selector.
carat |
cut |
color |
clarity |
depth |
table |
price |
x |
y |
z |
---|---|---|---|---|---|---|---|---|---|
0.23 |
Ideal |
E |
SI2 |
61.5 |
55 |
326 |
3.95 |
3.98 |
2.43 |
0.21 |
Premium |
E |
SI1 |
59.8 |
61 |
326 |
3.89 |
3.84 |
2.31 |
0.23 |
Good |
E |
VS1 |
56.9 |
65 |
327 |
4.05 |
4.07 |
2.31 |
0.29 |
Premium |
I |
VS2 |
62.4 |
58 |
334 |
4.20 |
4.23 |
2.63 |
0.31 |
Good |
J |
SI2 |
63.3 |
58 |
335 |
4.34 |
4.35 |
2.75 |
0.24 |
Very Good |
J |
VVS2 |
62.8 |
57 |
336 |
3.94 |
3.96 |
2.48 |
0.24 |
Very Good |
I |
VVS1 |
62.3 |
57 |
336 |
3.95 |
3.98 |
2.47 |
0.26 |
Very Good |
H |
SI1 |
61.9 |
55 |
337 |
4.07 |
4.11 |
2.53 |
0.22 |
Fair |
E |
VS2 |
65.1 |
61 |
337 |
3.87 |
3.78 |
2.49 |
0.23 |
Very Good |
H |
VS1 |
59.4 |
61 |
338 |
4.00 |
4.05 |
2.39 |
The most efficient selector for rows is the formula expression. The most efficient selector for columns is the character vector.
Selectors expressed as formula connot be used everywhere:
- Rows selector expressed as formula cannot be used with
header
part. The header part contains only character values. - A common value for argument
part
is “all”. In this case, a
row selector expressed as a formula is not supported (it can not as header dataset is made of only character columns and body dataset is the original dataset).