Search code examples
rflextable

How to remove excessive whitespace between columns in flextable?


I have the following table:

enter image description here

However, there is a lot of whitespace between the second P column and the third enrichment column, that seems to be created when using autofit(). Is there a way to remove this?

Code:

library(dplyr)
library(flextable)

top_colnames = c(rep(" ", 4), "Selection Region Set", rep(" ", 4))
top_colwidths = rep(1, 9)

second_colnames = c(" ", "CKB", " ", "UKB", " ", "CKB-only", " ", "CKB+UKB", " ")
second_colwidths = rep(1, 9)


flextable(final) %>%
  set_header_labels(values = c("VIPs", rep(c("enrichment", "P"), 4))) %>%
  add_header_row(values = second_colnames, colwidths = second_colwidths, top=TRUE) %>%
  add_header_row(values = top_colnames, colwidths = top_colwidths, top=TRUE) %>%
  align(i = 1, align='left') %>%
  padding(i=2:3, j=1, padding.left=5) %>%
  padding(i=4:5, j=1, padding.left=10) %>%
  padding(i=6:7, j=1, padding.left=15) %>%
  padding(i=8, j=1, padding.left=20) %>%
  fontsize(size=10) %>%
  fontsize(size=10, part='header') %>%
  line_spacing(space = 1, j=1:9, part = "all") %>%
  width(width=0.85) %>%
  autofit()

Data:

final = structure(list(a = c("All", "DNA viruses", "RNA viruses", "non-COV", 
"COV", "non-SARS", "SARS", "under-selection"), b = c("1.13 (0.98-1.31)", 
"1.03 (0.83-1.28)", "1.19 (1.01-1.41)", "1.16 (0.99-1.38)", "1.50 (1.10-2.16)", 
"1.86 (1.00-4.33)", "1.41 (1.02-2.16)", "2.50 (1.25-10.00)"), 
    c = c("0.047*", "0.419", "0.018*", "0.038*", "0.004*", "0.021*", 
    "0.02*", "0.005*"), d = c("0.98 (0.88-1.08)", "0.92 (0.80-1.08)", 
    "1.01 (0.90-1.14)", "1.03 (0.91-1.16)", "1.04 (0.84-1.32)", 
    "1.07 (0.71-1.88)", "1.04 (0.82-1.34)", "1.50 (0.86-4.00)"
    ), e = c("0.673", "0.852", "0.429", "0.337", "0.374", "0.372", 
    "0.41", "0.083"), f = c("1.15 (0.95-1.42)", "0.93 (0.70-1.31)", 
    "1.25 (1.02-1.59)", "1.20 (0.97-1.54)", "1.89 (1.21-3.40)", 
    "2.00 (1.00-8.00)", "1.86 (1.13-3.71)", "2.00 (0.80-Inf)"
    ), g = c("0.076", "0.687", "0.016*", "0.046*", "0.001*", 
    "0.023*", "0.005*", "0.109"), h = c("1.11 (0.89-1.42)", "1.12 (0.82-1.62)", 
    "1.12 (0.87-1.48)", "1.12 (0.86-1.48)", "1.11 (0.74-2.00)", 
    "1.67 (0.62-Inf)", "1.07 (0.65-2.14)", "3.00 (1.20-Inf)"), 
    i = c("0.178", "0.242", "0.183", "0.195", "0.316", "0.208", 
    "0.424", "0.011*")), row.names = c(NA, -8L), class = c("tbl_df", 
"tbl", "data.frame"), na.action = structure(9:10, names = c("9", 
"10"), class = "omit"))

Solution

  • Here is a modified code (I'm taking the liberty of shortening the code a little to focus on this first line):

    
    final = structure(list(a = c("All", "DNA viruses", "RNA viruses", "non-COV", 
    "COV", "non-SARS", "SARS", "under-selection"), b = c("1.13 (0.98-1.31)", 
    "1.03 (0.83-1.28)", "1.19 (1.01-1.41)", "1.16 (0.99-1.38)", "1.50 (1.10-2.16)", 
    "1.86 (1.00-4.33)", "1.41 (1.02-2.16)", "2.50 (1.25-10.00)"), 
        c = c("0.047*", "0.419", "0.018*", "0.038*", "0.004*", "0.021*", 
        "0.02*", "0.005*"), d = c("0.98 (0.88-1.08)", "0.92 (0.80-1.08)", 
        "1.01 (0.90-1.14)", "1.03 (0.91-1.16)", "1.04 (0.84-1.32)", 
        "1.07 (0.71-1.88)", "1.04 (0.82-1.34)", "1.50 (0.86-4.00)"
        ), e = c("0.673", "0.852", "0.429", "0.337", "0.374", "0.372", 
        "0.41", "0.083"), f = c("1.15 (0.95-1.42)", "0.93 (0.70-1.31)", 
        "1.25 (1.02-1.59)", "1.20 (0.97-1.54)", "1.89 (1.21-3.40)", 
        "2.00 (1.00-8.00)", "1.86 (1.13-3.71)", "2.00 (0.80-Inf)"
        ), g = c("0.076", "0.687", "0.016*", "0.046*", "0.001*", 
        "0.023*", "0.005*", "0.109"), h = c("1.11 (0.89-1.42)", "1.12 (0.82-1.62)", 
        "1.12 (0.87-1.48)", "1.12 (0.86-1.48)", "1.11 (0.74-2.00)", 
        "1.67 (0.62-Inf)", "1.07 (0.65-2.14)", "3.00 (1.20-Inf)"), 
        i = c("0.178", "0.242", "0.183", "0.195", "0.316", "0.208", 
        "0.424", "0.011*")), row.names = c(NA, -8L), class = c("tbl_df", 
    "tbl", "data.frame"), na.action = structure(9:10, names = c("9", 
    "10"), class = "omit"))
    
    library(dplyr)
    library(flextable)
    
    second_colnames = c(" ", "CKB", " ", "UKB", " ", "CKB-only", " ", "CKB+UKB", " ")
    second_colwidths = rep(1, 9)
    
    set_flextable_defaults(font.size = 10, line_spacing = 1)
    flextable(final) %>%
      set_header_labels(values = c("VIPs", rep(c("enrichment", "P"), 4))) %>%
      add_header_row(values = second_colnames, colwidths = second_colwidths, top=TRUE) %>%
      add_header_lines("Selection Region Set ", top=TRUE) %>%
      align(i = 1, part = "header", align = "center") |> 
      autofit()
    
    

    enter image description here

    What happened is autofit() use only columns that are not merged. As your first line/row is not merged, its width is used to define the column width.

    Note you can also keep your code almost as it is by using:

    top_colnames = "Selection Region Set"
    top_colwidths = 9