Appendix J — Table Formatting
Flextable is a great package for formatting tables.
J.1 Data
First, let’s get some data. I’ve included a subset of data for the first nine pokemon from Dave Parr’s pokedex package. We have some character and numeric columns, plus some missing data (not all pokemon have a secondary type).
# devtools::install_github("DaveParr/pokedex")
# data from pokedex::pokemon
pokemon <- data.frame(
id = 1:9,
name = c("Bulbasaur", "Ivysaur", "Venusaur",
"Charmander", "Charmeleon", "Charizard",
"Squirtle", "Wartortle", "Blastoise"),
height = c(0.7, 1, 2, 0.6, 1.1, 1.7, 0.5, 1, 1.6),
weight = c(6.9, 13, 100, 8.5, 19, 90.5, 9, 22.5, 85.5),
type_1 = c("grass", "grass", "grass",
"fire", "fire", "fire",
"water", "water", "water"),
type_2 = c("poison", "poison", "poison",
NA, NA, "flying",
NA, NA, NA)
)
J.2 Basic table
Look at how the table displays by default. This will display in one way in RStudio, but a different way when you render the document, so also render it and look at the HTML version.
id | name | height | weight | type_1 | type_2 |
---|---|---|---|---|---|
1 | Bulbasaur | 0.7 | 6.9 | grass | poison |
2 | Ivysaur | 1.0 | 13.0 | grass | poison |
3 | Venusaur | 2.0 | 100.0 | grass | poison |
4 | Charmander | 0.6 | 8.5 | fire | NA |
5 | Charmeleon | 1.1 | 19.0 | fire | NA |
6 | Charizard | 1.7 | 90.5 | fire | flying |
7 | Squirtle | 0.5 | 9.0 | water | NA |
8 | Wartortle | 1.0 | 22.5 | water | NA |
9 | Blastoise | 1.6 | 85.5 | water | NA |
Now see how it looks if you use flextable()
.
id |
name |
height |
weight |
type_1 |
type_2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
J.3 Cusomising tables
J.3.1 Subset/reorder columns
You can use the col_keys
argument to select a subset of columns, or change their order. You can also do this with data wrangling before you send a data frame to flextable, but this can be easier in some instances.
J.3.2 Header
Use set_header_labels()
to customise the header. You can change the name of one or more column.
flextable(pokemon) |>
set_header_labels(id = "ID",
name = "Name",
height = "Height (m)",
weight = "Weight (kg)",
type_1 = "Type 1",
type_2 = "Type 2")
set_header_labels()
ID |
Name |
Height (m) |
Weight (kg) |
Type 1 |
Type 2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
If you have headers that have a nested structure, and their names reflect this (like “category_instance1” and “category_instance2”), you can use separate_header()
to separate them.
flextable(pokemon) |>
separate_header() |>
set_header_labels(type_1 = "primary",
type_2 = "secondary")
separate_header()
id |
name |
height |
weight |
type |
|
---|---|---|---|---|---|
primary |
secondary |
||||
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
Alternatively, you can manually add extra header rows.
flextable(pokemon) |>
add_header_row(values = c("", "Measurements", "Types"),
colwidths = c(2, 2, 2)) |>
set_header_labels(type_1 = "primary",
type_2 = "secondary")
add_header_row()
Measurements |
Types |
||||
---|---|---|---|---|---|
id |
name |
height |
weight |
primary |
secondary |
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
J.3.3 Alignment
The defaults are usually correct, left-aligning text and right-aligning numbers, but you can also customise this using the align()
function.
The code below centre aligns the first row of the header to “center”, and then right aligns the body cells in the “name” column.
flextable(pokemon) |>
add_header_row(values = c("", "Measurements", "Types"),
colwidths = c(2, 2, 2)) |>
set_header_labels(type_1 = "primary",
type_2 = "secondary") |>
align(align = "center", i = 1, part = "header") |>
align(align = "right", j = "name", part = "body")
Measurements |
Types |
||||
---|---|---|---|---|---|
id |
name |
height |
weight |
primary |
secondary |
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
See if you can figure out what i
and j
mean, using trial-and-error first, and the help function if you get stuck.
J.4 Themes
There are some built-in themes you can use.
J.4.1 Alafoli
id |
name |
height |
weight |
type_1 |
type_2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
J.4.2 APA
id |
name |
height |
weight |
type_1 |
type_2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.70 |
6.90 |
grass |
poison |
2 |
Ivysaur |
1.00 |
13.00 |
grass |
poison |
3 |
Venusaur |
2.00 |
100.00 |
grass |
poison |
4 |
Charmander |
0.60 |
8.50 |
fire |
|
5 |
Charmeleon |
1.10 |
19.00 |
fire |
|
6 |
Charizard |
1.70 |
90.50 |
fire |
flying |
7 |
Squirtle |
0.50 |
9.00 |
water |
|
8 |
Wartortle |
1.00 |
22.50 |
water |
|
9 |
Blastoise |
1.60 |
85.50 |
water |
J.4.3 Booktabs
id |
name |
height |
weight |
type_1 |
type_2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
J.4.4 Box
id |
name |
height |
weight |
type_1 |
type_2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
J.4.5 Tron
id |
name |
height |
weight |
type_1 |
type_2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
J.4.6 Tron Legacy
id |
name |
height |
weight |
type_1 |
type_2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
J.4.7 Vader
id |
name |
height |
weight |
type_1 |
type_2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
J.4.8 Vanilla
id |
name |
height |
weight |
type_1 |
type_2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.7 |
6.9 |
grass |
poison |
2 |
Ivysaur |
1.0 |
13.0 |
grass |
poison |
3 |
Venusaur |
2.0 |
100.0 |
grass |
poison |
4 |
Charmander |
0.6 |
8.5 |
fire |
|
5 |
Charmeleon |
1.1 |
19.0 |
fire |
|
6 |
Charizard |
1.7 |
90.5 |
fire |
flying |
7 |
Squirtle |
0.5 |
9.0 |
water |
|
8 |
Wartortle |
1.0 |
22.5 |
water |
|
9 |
Blastoise |
1.6 |
85.5 |
water |
J.5 Defaults
You can set a ton of defaults using set_flextable_defaults()
. Do this at the top of your script and all of the tables in your document will have consistent styles.
id |
name |
height |
weight |
type_1 |
type_2 |
---|---|---|---|---|---|
1 |
Bulbasaur |
0.70 |
6.90 |
grass |
poison |
2 |
Ivysaur |
1.00 |
13.00 |
grass |
poison |
3 |
Venusaur |
2.00 |
100.00 |
grass |
poison |
4 |
Charmander |
0.60 |
8.50 |
fire |
(none) |
5 |
Charmeleon |
1.10 |
19.00 |
fire |
(none) |
6 |
Charizard |
1.70 |
90.50 |
fire |
flying |
7 |
Squirtle |
0.50 |
9.00 |
water |
(none) |
8 |
Wartortle |
1.00 |
22.50 |
water |
(none) |
9 |
Blastoise |
1.60 |
85.50 |
water |
(none) |
J.6 Next Steps
You can learn a lot more about how to customisie tables from the flextable book.
- As a group, make a list of things you might want to do to tables, like highlight a row, add footnotes, include images, or set border styles.
- Divide up the list and each create a short tutorial for how to accomplish the thing, using just the
pokemon
table. - Share your tutorial with the group!