Chapter 12 Plotting flextable

wrap_flextable() wraps a flextable as a patchwork-compliant patch. It allows flextable objects to be combined with ggplot2 plots using the +, |, and / operators from patchwork.

To illustrate, we build a dumbbell chart of Bundesliga team statistics paired with a matching flextable (adapted from the R Graph Gallery).

library(ggplot2)
library(patchwork)

dataset <- data.frame(
  team = c(
    "FC Bayern Munchen", "SV Werder Bremen", "Borussia Dortmund",
    "VfB Stuttgart", "Borussia M'gladbach", "Hamburger SV",
    "Eintracht Frankfurt", "FC Schalke 04", "1. FC Koln",
    "Bayer 04 Leverkusen"
  ),
  matches = c(2000, 1992, 1924, 1924, 1898, 1866, 1856, 1832, 1754, 1524),
  won     = c(1206,  818,  881,  782,  763,  746,  683,  700,  674,  669),
  lost    = c( 363,  676,  563,  673,  636,  625,  693,  669,  628,  447)
)
dataset$win_pct  <- dataset$won  / dataset$matches * 100
dataset$loss_pct <- dataset$lost / dataset$matches * 100
dataset$team <- factor(dataset$team, levels = rev(dataset$team))

The dumbbell chart:

pal <- c(lost = "#EFAC00", won = "#28A87D")
df_long <- reshape(dataset, direction = "long",
  varying = list(c("loss_pct", "win_pct")),
  v.names = "pct", timevar = "type",
  times = c("lost", "won"), idvar = "team"
)

p <- ggplot(df_long, aes(x = pct / 100, y = team)) +
  stat_summary(
    geom = "linerange", fun.min = "min", fun.max = "max",
    linewidth = .7, color = "grey60"
  ) +
  geom_point(aes(fill = type), size = 4, shape = 21,
    stroke = .8, color = "white"
  ) +
  scale_x_continuous(
    labels = scales::percent,
    expand = expansion(add = c(.02, .02))
  ) +
  scale_y_discrete(name = NULL, guide = "none") +
  scale_fill_manual(
    values = pal,
    labels = c(lost = "Lost", won = "Won")
  ) +
  labs(x = NULL, fill = NULL) +
  theme_minimal(base_size = 10) +
  theme(
    legend.position = "top",
    legend.justification = "left",
    panel.grid.minor = element_blank(),
    panel.grid.major.y = element_blank()
  )
p

And the flextable:

ft_dat <- dataset[, c("matches", "win_pct", "loss_pct", "team")]
ft_dat$team <- as.character(ft_dat$team)

ft <- flextable(ft_dat) |>
  border_remove() |>
  bold(part = "header") |>
  colformat_double(j = c("win_pct", "loss_pct"),
    digits = 1, suffix = "%") |>
  set_header_labels(
    team = "Team", matches = "GP",
    win_pct = "Won", loss_pct = "Lost") |>
  color(color = "#28A87D", j = "win_pct") |>
  color(color = "#EFAC00", j = "loss_pct") |>
  bold(j = c("win_pct", "loss_pct")) |>
  italic(j = "team") |>
  align(align = "right", part = "all") |>
  autofit()
ft

GP

Won

Lost

Team

2 000

60.3%

18.1%

FC Bayern Munchen

1 992

41.1%

33.9%

SV Werder Bremen

1 924

45.8%

29.3%

Borussia Dortmund

1 924

40.6%

35.0%

VfB Stuttgart

1 898

40.2%

33.5%

Borussia M'gladbach

1 866

40.0%

33.5%

Hamburger SV

1 856

36.8%

37.3%

Eintracht Frankfurt

1 832

38.2%

36.5%

FC Schalke 04

1 754

38.4%

35.8%

1. FC Koln

1 524

43.9%

29.3%

Bayer 04 Leverkusen

12.1 Basic usage

Use wrap_flextable() to place a flextable alongside ggplot2 plots.

12.2 Aligning rows with flex_body

When flex_body = TRUE, body rows stretch to match the height of the adjacent plot panel. Each table row aligns with the corresponding category on the y axis. Header and footer keep their fixed size.

wrap_flextable(ft, flex_body = TRUE, just = "right") +
  p +
  plot_layout(widths = c(1.1, 2))

12.3 Aligning columns with flex_cols

When flex_cols = TRUE, data columns stretch to fill the panel width determined by the adjacent plot. Each column aligns with the corresponding category on the x axis. Use n_row_headers to exclude leading columns from stretching.

cyl_mpg <- aggregate(mpg ~ cyl, mtcars, mean)

gg_bar <- ggplot(cyl_mpg, aes(factor(cyl), mpg)) +
  geom_col(fill = "#28A87D", width = 0.7) +
  labs(x = "Cylinders", y = "Mean MPG") +
  theme_minimal(base_size = 10)

wide <- data.frame(t(round(cyl_mpg$mpg, 1)))
names(wide) <- cyl_mpg$cyl

ft_cyl <- flextable(wide) |>
  bold(part = "header") |>
  align(align = "center", part = "all") |>
  autofit()
wrap_flextable(ft_cyl, flex_cols = TRUE) /
  gg_bar +
  plot_layout(heights = c(1, 4))

12.4 Horizontal alignment with just

The just argument controls horizontal alignment of the table within its patchwork panel: "left" (default), "right", or "center".

wrap_flextable(ft, just = "left") | p

wrap_flextable(ft, just = "right") | p

12.5 Panel alignment

The panel argument controls what portion of the table aligns with the plot panel region:

  • "body" (default): header and footer fall outside the panel region.
  • "full": the whole table is placed inside the panel region.
wrap_flextable(ft, panel = "body", flex_body = TRUE, just = "right") +
  p +
  plot_layout(widths = c(1.1, 2))

wrap_flextable(ft, panel = "full", flex_body = TRUE, just = "right") +
  p +
  plot_layout(widths = c(1.1, 2))

12.6 Saving as image

Use save_as_image() to export a flextable as a PNG or SVG file:

save_as_image(ft, path = "table.png")
save_as_image(ft, path = "table.svg")