Flextable stores information about the size of merged cells in my_table$body$spans
, and you can use that information to do stuff. For example, here is a simple table followed by the contents of its $body$spans$columns
:
library(flextable)
library(dplyr)
library(officer)
set.seed(123456)
# Input data
my_mtcars <-
mtcars %>%
mutate(
vs = factor(vs, labels = c("V-shaped engine", "Straight engine")),
am = factor(am, labels = c("Automatic", "Manual")),
car = factor(rownames(mtcars))
) %>%
group_by(vs, am, gear) %>%
slice_sample(n = 2) %>%
ungroup() %>%
arrange(vs, am, gear)
# Basic table.
tbl0 <-
my_mtcars %>%
flextable(col_keys = c("vs", "am", "gear", "car", "mpg", "hp")) %>%
merge_v(j = c("vs", "am")) %>%
valign(j = 1:2, valign = "top")
tbl0
tbl0$body$spans$columns
#> [,1] [,2] [,3] [,4] [,5] [,6]
#> [1,] 6 2 1 1 1 1
#> [2,] 0 0 1 1 1 1
#> [3,] 0 4 1 1 1 1
#> [4,] 0 0 1 1 1 1
#> [5,] 0 0 1 1 1 1
#> [6,] 0 0 1 1 1 1
#> [7,] 7 4 1 1 1 1
#> [8,] 0 0 1 1 1 1
#> [9,] 0 0 1 1 1 1
#> [10,] 0 0 1 1 1 1
#> [11,] 0 3 1 1 1 1
#> [12,] 0 0 1 1 1 1
#> [13,] 0 0 1 1 1 1
.$body$spans$columns
stores how many rows each merged span consists of.
unique(cumsum(...))[,x]
, where x
is the column number you want to use to decide where the lines go, gives you the row locations of where to draw the lines.
unique(cumsum(tbl0$body$spans$columns[,1]))
#> [1] 6 13
tbl1 <-
tbl0 %>%
hline(i = unique(cumsum(.$body$spans$columns[,1])),
border = fp_border(width = 2)) %>%
fix_border_issues()
tbl1
You can draw even more lines, but the catch is that newer lines will replace older ones if they are drawn onto the same location. You therefore either need to draw the lines in reverse order (so that more prominent ones overwrite minor ones), or you need to draw the new lines only in places where there aren't existing ones.
# This is a function that returns non-duplicated values in two vectors.
remove_dupes <- function(x, y) {
c(setdiff(x, y), setdiff(y, x))
}
remove_dupes(1:4, 3:6)
#> [1] 1 2 5 6
tbl2 <-
tbl1 %>%
merge_v(j = c("am", "gear"), target = "gear", combine = TRUE) %>%
valign(j = 3, valign = "top") %>%
# The existing lines I want to keep were drawn based on column 1 values (`vs`).
# The new lines I want to draw are based on column 3 values (`gear`).
hline(i = remove_dupes(unique(cumsum(.$body$spans$columns[,1])),
unique(cumsum(.$body$spans$columns[,3]))),
border = fp_border(style = "dotted", width = 1)) %>%
fix_border_issues()
tbl2
Created on 2024-10-04 with reprex v2.1.1