Chapter 10 Transform objects into flextable
Function as_flextable() is a generic function to cast objects into flextable.
This method exists for model objects such as lm, glm, mgcv::gam or htest
but also xtable [@R-xtable].
10.1 Groups as row titles
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 aggregated 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)# Key: <Treatment, conc>
# Treatment conc Quebec Mississippi
# <fctr> <num> <num> <num>
# 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:
# Treatment conc Quebec Mississippi
# 1 nonchilled NA NA NA
# 2 <NA> 95 15.26667 11.30000
# 3 <NA> 175 30.03333 20.20000
# 4 <NA> 250 37.40000 27.53333
# 5 <NA> 350 40.36667 29.90000
# 6 <NA> 500 39.60000 30.60000
The result is suitable for method as_flextable. A call to this function
and a few formatting operations produce the following result:
zz <- flextable::as_flextable(data_CO2) %>%
bold(j = 1, i = ~ !is.na(Treatment), bold = TRUE, part = "body") %>%
bold(part = "header", bold = TRUE) %>%
colformat_double(i = ~ is.na(Treatment), j = "conc", digits = 0, big.mark = "") %>%
autofit()
zzconc |
Quebec |
Mississippi |
|---|---|---|
Treatment: nonchilled | ||
95 |
15.26667 |
11.30000 |
175 |
30.03333 |
20.20000 |
250 |
37.40000 |
27.53333 |
350 |
40.36667 |
29.90000 |
500 |
39.60000 |
30.60000 |
675 |
41.50000 |
30.53333 |
1000 |
43.16667 |
31.60000 |
Treatment: chilled | ||
95 |
12.86667 |
9.60000 |
175 |
24.13333 |
14.76667 |
250 |
34.46667 |
16.10000 |
350 |
35.80000 |
16.60000 |
500 |
36.66667 |
16.63333 |
675 |
37.50000 |
18.26667 |
1000 |
40.83333 |
18.73333 |
Now let’s add nice bars before displaying the figures:
zz <- compose(
zz,
i = ~ is.na(Treatment), j = "Quebec",
value = as_paragraph(minibar(Quebec, height = 0.1), " ", as_chunk(Quebec))
) %>%
compose(
i = ~ is.na(Treatment), j = "Mississippi",
value = as_paragraph(minibar(Mississippi, height = 0.1), " ", as_chunk(Mississippi))
) %>%
align(j = 2:3, align = "left") %>%
width(width = c(.5, 1.5, 1.5))
zzconc |
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:
add_footer_lines(zz, "dataset CO2 has been used for this flextable") 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 | ||
10.2 Dataset summary
Create a dataset summary with functions summarizor() and as_flextable().
This function is a sugar function that can be used to create clinical
“Demographic Tables” but also can be used to present dataset summary
when there is a grouping variable.
An option is available to add an Overall category at the end
of the produced table.
library(dplyr)
library(flextable)
z <- palmerpenguins::penguins %>%
select(-contains("length")) %>%
summarizor(
by = "species",
overall_label = "Overall")
ft <- as_flextable(z, spread_first_col = TRUE) %>%
style(i = ~!is.na(variable), pr_t = fp_text_default(bold = TRUE),
pr_p = officer::fp_par(text.align = "left", padding = 5, line_spacing = 1.5)) %>%
prepend_chunks(i = ~is.na(variable), j = 1, as_chunk("\t")) %>%
autofit(add_w = .01)
ftAdelie |
Chinstrap |
Gentoo |
Overall |
|||||
|---|---|---|---|---|---|---|---|---|
island | ||||||||
Biscoe |
44 (28.9%) |
0 (0.0%) |
124 (100.0%) |
168 (48.8%) |
||||
Dream |
56 (36.8%) |
68 (100.0%) |
0 (0.0%) |
124 (36.0%) |
||||
Torgersen |
52 (34.2%) |
0 (0.0%) |
0 (0.0%) |
52 (15.1%) |
||||
bill_depth_mm | ||||||||
Mean (SD) |
18.35 (1.22) |
18.42 (1.14) |
14.98 (0.98) |
17.15 (1.97) |
||||
Median (IQR) |
18.40 (1.50) |
18.45 (1.90) |
15.00 (1.50) |
17.30 (3.10) |
||||
Range |
15.50 - 21.50 |
16.40 - 20.80 |
13.10 - 17.30 |
13.10 - 21.50 |
||||
Missing |
1 (0.7%) |
1 (0.8%) |
2 (0.6%) |
|||||
body_mass_g | ||||||||
Mean (SD) |
3 700.66 (458.57) |
3 733.09 (384.34) |
5 076.02 (504.12) |
4 201.75 (801.95) |
||||
Median (IQR) |
3 700.00 (650.00) |
3 700.00 (462.50) |
5 000.00 (800.00) |
4 050.00 (1 200.00) |
||||
Range |
2 850.00 - 4 775.00 |
2 700.00 - 4 800.00 |
3 950.00 - 6 300.00 |
2 700.00 - 6 300.00 |
||||
Missing |
1 (0.7%) |
1 (0.8%) |
2 (0.6%) |
|||||
sex | ||||||||
female |
73 (48.0%) |
34 (50.0%) |
58 (46.8%) |
165 (48.0%) |
||||
male |
73 (48.0%) |
34 (50.0%) |
61 (49.2%) |
168 (48.8%) |
||||
Missing |
6 (3.9%) |
5 (4.0%) |
11 (3.2%) |
|||||
year | ||||||||
Mean (SD) |
2 008.01 (0.82) |
2 007.97 (0.86) |
2 008.08 (0.79) |
2 008.03 (0.82) |
||||
Median (IQR) |
2 008.00 (2.00) |
2 008.00 (2.00) |
2 008.00 (2.00) |
2 008.00 (2.00) |
||||
Range |
2 007.00 - 2 009.00 |
2 007.00 - 2 009.00 |
2 007.00 - 2 009.00 |
2 007.00 - 2 009.00 |
||||
10.2.1 Compact summary
For a quick overview of a dataset rather than a statistical summary,
compact_summary() describes each column of a data.frame with a
type-specific synopsis (count of non-missing values, range for numeric and
date columns, levels for factors, etc.). The result has its own
as_flextable() method.
z <- compact_summary(iris, show_type = TRUE, show_na = TRUE)
as_flextable(z)Column |
Type |
N |
NA |
Values |
|---|---|---|---|---|
Sepal.Length |
numeric |
150 |
0 |
Min: 4.30, Max: 7.90 |
Sepal.Width |
numeric |
150 |
0 |
Min: 2.00, Max: 4.40 |
Petal.Length |
numeric |
150 |
0 |
Min: 1.00, Max: 6.90 |
Petal.Width |
numeric |
150 |
0 |
Min: 0.10, Max: 2.50 |
Species |
factor |
3 |
0 |
'setosa', 'versicolor', 'virginica' |
Use show_type and show_na to add the column type and the count of
missing values; max_levels limits how many factor/character values are
listed.
10.3 Models and tests
glm, lm, gam, htest, kmeans and pam objects can be
easily converted to flextable:
10.3.1 GLM example
clotting <- data.frame(
u = c(5,10,15,20,30,40,60,80,100),
lot1 = c(118,58,42,35,27,25,21,19,18),
lot2 = c(69,35,26,21,18,16,13,12,12))
as_flextable(glm(lot1 ~ log(u), data = clotting, family = Gamma))Estimate |
Standard Error |
z value |
Pr(>|z|) |
||
|---|---|---|---|---|---|
(Intercept) |
-0.017 |
0.001 |
-17.847 |
0.0000 |
*** |
log(u) |
0.015 |
0.000 |
36.975 |
0.0000 |
*** |
Signif. codes: 0 <= '***' < 0.001 < '**' < 0.01 < '*' < 0.05 | |||||
| |||||
(Dispersion parameter for Gamma family taken to be 0.002446059) | |||||
Null deviance: 3.513 on 8 degrees of freedom | |||||
Residual deviance: 0.01673 on 7 degrees of freedom | |||||
10.3.2 LM example
ctl <- c(4.17,5.58,5.18,6.11,4.50,4.61,5.17,4.53,5.33,5.14)
trt <- c(4.81,4.17,4.41,3.59,5.87,3.83,6.03,4.89,4.32,4.69)
group <- gl(2, 10, 20, labels = c("Ctl","Trt"))
weight <- c(ctl, trt)
lm(weight ~ group) %>% as_flextable()Estimate |
Standard Error |
t value |
Pr(>|t|) |
||
|---|---|---|---|---|---|
(Intercept) |
5.032 |
0.220 |
22.850 |
0.0000 |
*** |
groupTrt |
-0.371 |
0.311 |
-1.191 |
0.2490 |
|
Signif. codes: 0 <= '***' < 0.001 < '**' < 0.01 < '*' < 0.05 | |||||
Residual standard error: 0.6964 on 18 degrees of freedom | |||||
Multiple R-squared: 0.07308, Adjusted R-squared: 0.02158 | |||||
F-statistic: 1.419 on 18 and 1 DF, p-value: 0.2490 | |||||
10.3.3 GAM example
library(mgcv)
set.seed(2)
dat <- gamSim(1, n = 400,
dist = "normal", scale = 2, verbose = FALSE)
b <- gam(y ~ s(x0) + s(x1) + s(x2) + s(x3),
data = dat)
ft <- as_flextable(b)
ftComponent |
Term |
Estimate |
Std Error |
t-value |
p-value |
|
|---|---|---|---|---|---|---|
A. parametric coefficients |
(Intercept) |
7.833 |
0.099 |
79.303 |
0.0000 |
*** |
Component |
Term |
edf |
Ref. df |
F-value |
p-value |
|
B. smooth terms |
s(x0) |
2.500 |
3.115 |
6.921 |
0.0001 |
*** |
s(x1) |
2.401 |
2.984 |
81.858 |
0.0000 |
*** |
|
s(x2) |
7.698 |
8.564 |
88.158 |
0.0000 |
*** |
|
s(x3) |
1.000 |
1.000 |
4.343 |
0.0378 |
* |
|
Signif. codes: 0 <= '***' < 0.001 < '**' < 0.01 < '*' < 0.05 | ||||||
Adjusted R-squared: 0.715, Deviance explained 0.725 | ||||||
GCV : 4.051, Scale est: 3.903, N: 400 | ||||||
10.3.4 Hypothesis testing example
x <- rnorm(50)
y <- runif(30)
ks.test(x, y) %>% as_flextable()statistic |
p.value |
method |
alternative |
|---|---|---|---|
0.57 |
0.0000*** |
Exact two-sample Kolmogorov-Smirnov test |
two-sided |
Signif. codes: 0 <= '***' < 0.001 < '**' < 0.01 < '*' < 0.05 | |||
10.3.5 kmeans
cl <- kmeans(scale(mtcars[1:7]), 5)
ft <- as_flextable(cl)
ftvariable |
1 |
2 |
3 |
4 |
5 |
|||||
|---|---|---|---|---|---|---|---|---|---|---|
withinss |
2.15 |
5.03 |
33.38 |
4.07 |
9.58 |
|||||
size |
3 |
5 |
14 |
5 |
5 |
|||||
mpg* |
2.0147 |
0.9108 |
-0.8281 |
-0.0582 |
0.2571 |
|||||
cyl* |
-1.2249 |
-1.2249 |
1.0149 |
-0.1050 |
-0.7769 |
|||||
disp* |
-1.2551 |
-1.0170 |
0.9874 |
-0.5703 |
-0.4244 |
|||||
hp* |
-1.2498 |
-0.7626 |
0.9120 |
-0.2696 |
-0.7714 |
|||||
drat* |
1.5214 |
0.8443 |
-0.6869 |
0.4777 |
-0.3115 |
|||||
wt* |
-1.3633 |
-1.1034 |
0.7992 |
-0.1924 |
-0.1239 |
|||||
qsec* |
0.8103 |
0.0522 |
-0.6025 |
-0.3429 |
1.4915 |
|||||
(*) Centers | ||||||||||
Total sum of squares: 217.00 | ||||||||||
Total within-cluster sum of squares: 54.20 | ||||||||||
Total between-cluster sum of squares: 162.80 | ||||||||||
BSS/TSS ratio: 75.02% | ||||||||||
Number of iterations: 3 | ||||||||||
10.3.6 pam
library(cluster)
dat <- as.data.frame(scale(mtcars[1:7]))
cl <- pam(dat, 3)
ft <- as_flextable(cl)
ftvariable |
1 |
2 |
3 |
|||
|---|---|---|---|---|---|---|
size |
6 |
11 |
15 |
|||
max.diss |
2.22 |
2.76 |
3.16 |
|||
avg.diss |
1.06 |
1.42 |
1.59 |
|||
diameter |
2.81 |
3.99 |
5.06 |
|||
separation |
0.99 |
1.48 |
0.99 |
|||
avg.width |
0.41 |
0.24 |
0.29 |
|||
mpg* |
0.1509 |
1.1962 |
-0.6124 |
|||
cyl* |
-0.1050 |
-1.2249 |
1.0149 |
|||
disp* |
-0.5706 |
-1.2242 |
0.3637 |
|||
hp* |
-0.5351 |
-1.1768 |
0.4859 |
|||
drat* |
0.5675 |
0.9042 |
-0.9848 |
|||
wt* |
-0.3498 |
-1.3105 |
0.8715 |
|||
qsec* |
-0.4638 |
0.5883 |
-0.2511 |
|||
(*) Centers | ||||||
The average silhouette width is 0.2959 | ||||||
10.4 xtable
xtable objects can be transformed as flextable objects with function as_flextable().
x <- as.integer(cumsum(1 + round(rnorm(100), 0)))
temp.ts <- ts(x,
start = c(1954, 7), frequency = 12)
ft <- as_flextable(x = xtable::xtable(temp.ts, digits = 0))
ftJan |
Feb |
Mar |
Apr |
May |
Jun |
Jul |
Aug |
Sep |
Oct |
Nov |
Dec |
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
1954 |
<na> |
<na> |
<na> |
<na> |
<na> |
<na> |
1 |
1 |
2 |
3 |
5 |
5 |
1955 |
6 |
7 |
9 |
10 |
12 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
1956 |
18 |
20 |
19 |
19 |
21 |
23 |
25 |
26 |
27 |
29 |
31 |
32 |
1957 |
33 |
35 |
37 |
39 |
41 |
43 |
45 |
46 |
46 |
48 |
50 |
50 |
1958 |
53 |
54 |
54 |
55 |
55 |
54 |
56 |
59 |
61 |
62 |
64 |
63 |
1959 |
64 |
65 |
65 |
64 |
64 |
65 |
68 |
70 |
73 |
75 |
76 |
78 |
1960 |
80 |
80 |
80 |
80 |
80 |
82 |
82 |
83 |
83 |
84 |
86 |
86 |
1961 |
87 |
88 |
88 |
89 |
92 |
92 |
91 |
92 |
93 |
95 |
97 |
99 |
1962 |
100 |
103 |
105 |
104 |
105 |
106 |
109 |
112 |
113 |
112 |
<na> |
<na> |
10.5 rtables
The ‘rtables’ package shines at describing a table: its layout grammar lets you express nested groupings and the statistics to compute in a few declarative lines, then build the result against a dataset. What it deliberately leaves aside is fine typographic control over the output.
This is where the two packages complement each other: ‘rtables’ expresses
the computation, flextable takes care of the presentation. The
as_flextable() method bridges them, mapping the formatted content,
column spans, alignments, row indentation and footnotes of a TableTree
(or ElementaryTable) onto their flextable equivalents, so the table
becomes fully styleable and renders identically in HTML, Word, PowerPoint,
PDF and RTF.
# Loading required package: formatters
#
# Attaching package: 'formatters'
# The following object is masked from 'package:base':
#
# %||%
#
# Attaching package: 'rtables'
# The following object is masked from 'package:utils':
#
# str
lyt <- basic_table(title = "Demographic Summary") %>%
split_cols_by("ARM") %>%
split_rows_by("SEX") %>%
analyze("AGE", afun = mean, format = "xx.x")
tbl <- build_table(lyt, DM)
as_flextable(tbl)A: Drug X |
B: Placebo |
C: Combination |
|
|---|---|---|---|
F | |||
mean |
33.7 |
33.8 |
34.9 |
M | |||
mean |
36.5 |
32.1 |
34.3 |
U | |||
mean |
NA |
NA |
NA |
UNDIFFERENTIATED | |||
mean |
NA |
NA |
NA |
Label rows are rendered in bold in the first column and content rows
entirely in bold; row indentation is reproduced with left padding (tune it
with the indent_padding argument). When the table contains row groups,
paginate() is applied automatically so that a group is never split
across two pages in Word and RTF. For large tables, call
split_to_pages() (or split_rows() / split_columns()) on the result
to break it into several pages.