flextable
layout can be easily managed. A set of functions will let you merge cells, add title rows, add footer notes, change the withs or heights.
Parameter col_keys
of function flextable
define the variables to display and their order.
data <- iris[c(1:3, 51:53, 101:104),]
myft <- flextable(data, col_keys = c("Species", "Sepal.Length", "Petal.Length") )
myft
Species | Sepal.Length | Petal.Length |
setosa | 5.100 | 1.400 |
setosa | 4.900 | 1.400 |
setosa | 4.700 | 1.300 |
versicolor | 7.000 | 4.700 |
versicolor | 6.400 | 4.500 |
versicolor | 6.900 | 4.900 |
virginica | 6.300 | 6.000 |
virginica | 5.800 | 5.100 |
virginica | 7.100 | 5.900 |
virginica | 6.300 | 5.600 |
If parameter col_keys
has variables that are not existing in the dataset, they will be considered as blank columns and can be used as separators (in fact they can be use as you want, there is only no mapping of data associated).
myft <- flextable(
data = data,
col_keys = c("Species", "col_1", "Sepal.Length", "Petal.Length") )
myft <- theme_vanilla(myft)
myft <- autofit(myft)
myft <- empty_blanks(myft)
myft
Species | Sepal.Length | Petal.Length | |
setosa | 5.100 | 1.400 | |
setosa | 4.900 | 1.400 | |
setosa | 4.700 | 1.300 | |
versicolor | 7.000 | 4.700 | |
versicolor | 6.400 | 4.500 | |
versicolor | 6.900 | 4.900 | |
virginica | 6.300 | 6.000 | |
virginica | 5.800 | 5.100 | |
virginica | 7.100 | 5.900 | |
virginica | 6.300 | 5.600 |
col_keys
default values are the names of the data.frame used to fill the flextable.
Use set_header_labels()
to replace labels of the bottom row of header. When the flextable is created, their values are the column names of the data.frame.
ft <- flextable( head( iris ) )
ft <- set_header_labels(ft, Sepal.Length = "Sepal length",
Sepal.Width = "Sepal width", Petal.Length = "Petal length",
Petal.Width = "Petal width" )
ft
Sepal length | Sepal width | Petal length | Petal width | Species |
5.100 | 3.500 | 1.400 | 0.200 | setosa |
4.900 | 3.000 | 1.400 | 0.200 | setosa |
4.700 | 3.200 | 1.300 | 0.200 | setosa |
4.600 | 3.100 | 1.500 | 0.200 | setosa |
5.000 | 3.600 | 1.400 | 0.200 | setosa |
5.400 | 3.900 | 1.700 | 0.400 | setosa |
New header rows can be added at the top or bottom of the header. This part in documented in part Manage headers and footers.
Under the hood, the names are in a single row data.frame associated with the header part of the flextable. You can add new rows later, they will be binded to that data.frame.
To illustrate functions, we will use a basic flextable example:
dat <- data.frame(
letters1 = c("a", "b", "b", "c"),
letters2 = c("d", "e", "b", "b"),
number = 1:4, stringsAsFactors = FALSE )
myft <- flextable(dat)
myft <- theme_box(myft)
myft
letters1 | letters2 | number |
a | d | 1 |
b | e | 2 |
b | b | 3 |
c | b | 4 |
merge_v()
will merge adjacent duplicated cells for each column of the selection.
letters1 | letters2 | number |
a | d | 1 |
b | e | 2 |
b | 3 | |
c | 4 |
merge_h()
will merge adjacent duplicated cells for each row of the selection.
letters1 | letters2 | number |
a | d | 1 |
b | e | 2 |
b | 3 | |
c | b | 4 |
Function merge_h_range
is close to the previous one but merge all colums between a range of columns.
letters1 | letters2 | number |
a | 1 | |
b | 2 | |
b | b | 3 |
c | b | 4 |
merge_at()
will merge cells for a given continuous selection of rows and cells. The result is a single cell.
letters1 | letters2 | number |
a | 1 | |
2 | ||
b | b | 3 |
c | b | 4 |
If you want to get rid of all merging (i.e. for development purposes), use merge_none()
:
letters1 | letters2 | number |
a | d | 1 |
b | e | 2 |
b | b | 3 |
c | b | 4 |
When cells are merged, the rendered borders will be those of the first cell. If a column is made of three merged cells, the bottom border that will be seen will be the bottom border of the first cell in the column. From a user point of view, this is wrong, the bottom should be the one defined for cell 3. Function fix_border_issues
is trying to fix that issue.
ft <- data.frame(a = 1:5, b = 6:10) %>%
flextable() %>% theme_box() %>%
merge_at(i = 4:5, j = 1, part = "body") %>%
hline(i = 5, part = "body",
border = fp_border(color = "orange", width = 3) )
ft
a | b |
1 | 6 |
2 | 7 |
3 | 8 |
4 | 9 |
10 |
a | b |
1 | 6 |
2 | 7 |
3 | 8 |
4 | 9 |
10 |
The default sizes of flextable columns and rows are set by default values. This will drive to inadequate rows heights and columns widths in some cases. You can use function dim
to get flextable dimensions.
mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
21.000 | 6.000 | 160.000 | 110.000 | 3.900 | 2.620 | 16.460 | 0.000 | 1.000 | 4.000 | 4.000 |
21.000 | 6.000 | 160.000 | 110.000 | 3.900 | 2.875 | 17.020 | 0.000 | 1.000 | 4.000 | 4.000 |
22.800 | 4.000 | 108.000 | 93.000 | 3.850 | 2.320 | 18.610 | 1.000 | 1.000 | 4.000 | 1.000 |
21.400 | 6.000 | 258.000 | 110.000 | 3.080 | 3.215 | 19.440 | 1.000 | 0.000 | 3.000 | 1.000 |
18.700 | 8.000 | 360.000 | 175.000 | 3.150 | 3.440 | 17.020 | 0.000 | 0.000 | 3.000 | 2.000 |
18.100 | 6.000 | 225.000 | 105.000 | 2.760 | 3.460 | 20.220 | 1.000 | 0.000 | 3.000 | 1.000 |
dim(ft_base)
#> $widths
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75
#>
#> $heights
#> [1] 0.25 0.25 0.25 0.25 0.25 0.25 0.25
Function dim_pretty()
is computing optimized widths and heights.
Function autofit()
optimises widths and heights of the flextable. This function is almost always to be called once when using flextable objects, it makes compact tables.
ft <- autofit(ft_base, add_w = 0, add_h = 0)
dim(ft)
#> $widths
#> mpg cyl disp hp drat wt qsec
#> 0.7116790 0.6090585 0.8142994 0.8142994 0.6090585 0.6090585 0.7116790
#> vs am gear carb
#> 0.6090585 0.6090585 0.6090585 0.6090585
#>
#> $heights
#> [1] 0.3220916 0.2800383 0.2800383 0.2800383 0.2800383 0.2800383 0.2800383
ft
mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
21.000 | 6.000 | 160.000 | 110.000 | 3.900 | 2.620 | 16.460 | 0.000 | 1.000 | 4.000 | 4.000 |
21.000 | 6.000 | 160.000 | 110.000 | 3.900 | 2.875 | 17.020 | 0.000 | 1.000 | 4.000 | 4.000 |
22.800 | 4.000 | 108.000 | 93.000 | 3.850 | 2.320 | 18.610 | 1.000 | 1.000 | 4.000 | 1.000 |
21.400 | 6.000 | 258.000 | 110.000 | 3.080 | 3.215 | 19.440 | 1.000 | 0.000 | 3.000 | 1.000 |
18.700 | 8.000 | 360.000 | 175.000 | 3.150 | 3.440 | 17.020 | 0.000 | 0.000 | 3.000 | 2.000 |
18.100 | 6.000 | 225.000 | 105.000 | 2.760 | 3.460 | 20.220 | 1.000 | 0.000 | 3.000 | 1.000 |
Soft returns (a line break in a paragraph) support : function
autofit
anddim_pretty
do not support soft returns and may return wrong results (will be considered as "").
Function width()
and height()
let you control dimensions of a flextable. height_all()
is an helper function to set the same height to each part of the table.
ft <- autofit(ft_base)
ft <- width(ft, j = ~ mpg + cyl + disp, width = 2)
ft <- height_all( ft, height = .4 )
ft <- height( ft, i = 3, height = 1 )
ft
mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
21.000 | 6.000 | 160.000 | 110.000 | 3.900 | 2.620 | 16.460 | 0.000 | 1.000 | 4.000 | 4.000 |
21.000 | 6.000 | 160.000 | 110.000 | 3.900 | 2.875 | 17.020 | 0.000 | 1.000 | 4.000 | 4.000 |
22.800 | 4.000 | 108.000 | 93.000 | 3.850 | 2.320 | 18.610 | 1.000 | 1.000 | 4.000 | 1.000 |
21.400 | 6.000 | 258.000 | 110.000 | 3.080 | 3.215 | 19.440 | 1.000 | 0.000 | 3.000 | 1.000 |
18.700 | 8.000 | 360.000 | 175.000 | 3.150 | 3.440 | 17.020 | 0.000 | 0.000 | 3.000 | 2.000 |
18.100 | 6.000 | 225.000 | 105.000 | 2.760 | 3.460 | 20.220 | 1.000 | 0.000 | 3.000 | 1.000 |
Package flextable does not support data transformation. A grouped dataset is then needed (and tidy). This has to be done prior to the creation of the object.
Function as_grouped_data
will modify data structure so that it will be easy to manage grouped data representation. Repeated consecutive values of group columns will be used to define the title of the groups and will be added as a row title.
Let’s have an example with aggragated data from dataset CO2
:
library(data.table)
data_CO2 <- dcast(as.data.table(CO2),
Treatment + conc ~ Type, value.var = "uptake", fun.aggregate = mean)
head(data_CO2)
#> Treatment conc Quebec Mississippi
#> 1: nonchilled 95 15.26667 11.30000
#> 2: nonchilled 175 30.03333 20.20000
#> 3: nonchilled 250 37.40000 27.53333
#> 4: nonchilled 350 40.36667 29.90000
#> 5: nonchilled 500 39.60000 30.60000
#> 6: nonchilled 675 41.50000 30.53333
as_grouped_data
will restructure the dataset:
data_CO2 <- as_grouped_data(x = data_CO2, groups = c("Treatment"))
head(data_CO2)
#> Treatment conc Quebec Mississippi
#> 1 nonchilled NA NA NA
#> 3 <NA> 95 15.26667 11.30000
#> 4 <NA> 175 30.03333 20.20000
#> 5 <NA> 250 37.40000 27.53333
#> 6 <NA> 350 40.36667 29.90000
#> 7 <NA> 500 39.60000 30.60000
The result is suitable for method as_flextable
. A call to this function and few formatting operations are producing the following result:
zz <- as_flextable( data_CO2 ) %>%
bold(j = 1, i = ~ !is.na(Treatment), bold = TRUE, part = "body" ) %>%
bold(part = "header", bold = TRUE ) %>%
width(width = 1.5)
zz
conc | Quebec | Mississippi |
Treatment: nonchilled | ||
95.000 | 15.267 | 11.300 |
175.000 | 30.033 | 20.200 |
250.000 | 37.400 | 27.533 |
350.000 | 40.367 | 29.900 |
500.000 | 39.600 | 30.600 |
675.000 | 41.500 | 30.533 |
1000.000 | 43.167 | 31.600 |
Treatment: chilled | ||
95.000 | 12.867 | 9.600 |
175.000 | 24.133 | 14.767 |
250.000 | 34.467 | 16.100 |
350.000 | 35.800 | 16.600 |
500.000 | 36.667 | 16.633 |
675.000 | 37.500 | 18.267 |
1000.000 | 40.833 | 18.733 |
It’s now easier to customize the rendering. Let’s format column conc
as an integer column:
zz <- zz %>%
compose(i = ~ !is.na(conc), j = "conc",
value = as_paragraph(
as_chunk(conc, formater = function(x) sprintf("%.0f", x))
)
)
zz
conc | Quebec | Mississippi |
Treatment: nonchilled | ||
95 | 15.267 | 11.300 |
175 | 30.033 | 20.200 |
250 | 37.400 | 27.533 |
350 | 40.367 | 29.900 |
500 | 39.600 | 30.600 |
675 | 41.500 | 30.533 |
1000 | 43.167 | 31.600 |
Treatment: chilled | ||
95 | 12.867 | 9.600 |
175 | 24.133 | 14.767 |
250 | 34.467 | 16.100 |
350 | 35.800 | 16.600 |
500 | 36.667 | 16.633 |
675 | 37.500 | 18.267 |
1000 | 40.833 | 18.733 |
Now let’s add nice bars before displaying the figures:
zz <- zz %>%
compose(i = ~ is.na(Treatment), j = "Quebec",
value = as_paragraph(
minibar(Quebec),
" ",
as_chunk(Quebec, formater = function(x) sprintf("%.01f", x))
)
) %>%
compose(i = ~ is.na(Treatment), j = "Mississippi",
value = as_paragraph( minibar(Mississippi),
" ",
as_chunk(Mississippi,
formater = function(x) sprintf("%.01f", x) )
)
) %>%
align(j = 2:3, align = "left")
zz
conc | Quebec | Mississippi |
Treatment: nonchilled | ||
95 |
|
|
175 |
|
|
250 |
|
|
350 |
|
|
500 |
|
|
675 |
|
|
1000 |
|
|
Treatment: chilled | ||
95 |
|
|
175 |
|
|
250 |
|
|
350 |
|
|
500 |
|
|
675 |
|
|
1000 |
|
|
And finally, add a footnote in the footer part:
conc | Quebec | Mississippi |
Treatment: nonchilled | ||
95 |
|
|
175 |
|
|
250 |
|
|
350 |
|
|
500 |
|
|
675 |
|
|
1000 |
|
|
Treatment: chilled | ||
95 |
|
|
175 |
|
|
250 |
|
|
350 |
|
|
500 |
|
|
675 |
|
|
1000 |
|
|
dataset CO2 has been used for this flextable |
xtable
objects can be transformed as flextable objects with function xtable_to_flextable()
.
if( require("xtable") ){
temp.ts <- ts(cumsum(1 + round(rnorm(100), 0)),
start = c(1954, 7), frequency = 12)
ft <- xtable_to_flextable(x = xtable(temp.ts, digits = 0),
NA.string = "-")
ft
}
Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec | |
1954 | 1.000 | 0.000 | 1.000 | 2.000 | 3.000 | 4.000 | ||||||
1955 | 5.000 | 6.000 | 6.000 | 8.000 | 10.000 | 11.000 | 13.000 | 14.000 | 12.000 | 12.000 | 13.000 | 16.000 |
1956 | 17.000 | 19.000 | 19.000 | 19.000 | 19.000 | 21.000 | 23.000 | 24.000 | 26.000 | 26.000 | 27.000 | 29.000 |
1957 | 29.000 | 31.000 | 32.000 | 34.000 | 34.000 | 34.000 | 36.000 | 36.000 | 36.000 | 38.000 | 39.000 | 41.000 |
1958 | 44.000 | 44.000 | 46.000 | 48.000 | 48.000 | 49.000 | 47.000 | 47.000 | 49.000 | 50.000 | 50.000 | 50.000 |
1959 | 52.000 | 53.000 | 55.000 | 55.000 | 56.000 | 58.000 | 60.000 | 59.000 | 61.000 | 64.000 | 65.000 | 66.000 |
1960 | 67.000 | 68.000 | 70.000 | 70.000 | 71.000 | 74.000 | 76.000 | 75.000 | 77.000 | 80.000 | 81.000 | 82.000 |
1961 | 81.000 | 83.000 | 85.000 | 87.000 | 89.000 | 90.000 | 91.000 | 93.000 | 94.000 | 94.000 | 97.000 | 96.000 |
1962 | 98.000 | 100.000 | 101.000 | 101.000 | 103.000 | 105.000 | 107.000 | 107.000 | 108.000 | 107.000 |