Arie
Arie

Reputation: 55

How do I set header labels in a flextable with multiple header rows in R?

I am using the flextable-function in R to create a nice looking flat contingency table. My flat contingency table has column headers in two rows. I tried to change these using the set_header_labels-function in the flextable-package but failed.

The steps below represent the process that I went through. The first five steps went fine. I failed to arrive at my desired result in the final step where I want to change column header names from "A" to "aa" and from "B" to "bb". Please be advised that changing "A" to "aa" etc. at the very start is not solving my problem. I really need to make the changes I want, at the very end, not at the beginning!

  1. Create a tibble.
t <- tibble(ID = 1:10, 
header1 = c("A", "A", "A", "A", "A", "B", "B", "B", "B", "B"),
header2 = c("No", "No", "No", "Yes", "Yes", "Yes", "Yes", "No", "Yes", "No"),
q1 = c(1, 3, 2, 2, 3, 1, 2, 1, 1, 3))
  1. Create a flat contingency table.

ft <- (t %>% ftable(row.vars = c("q1"), col.vars = c("header1", "header2")))

  1. Load the function to convert a flat contingency table to a flextable. Source: Is there an (easy) way to convert flat contingency tables (ftable) to flextable
ftable_to_flextable <- function( x ){

  row.vars = attr( x, "row.vars" )
  col.vars = attr( x, "col.vars" )
  rows <- rev(expand.grid( rev(row.vars), stringsAsFactors = FALSE ))
  cols <- rev(expand.grid( rev(col.vars), stringsAsFactors = FALSE ))

  xmat <- as.matrix(x)
  cols$col_keys = dimnames(xmat)[[2]]
  xdata <- cbind(
    data.frame(rows, stringsAsFactors = FALSE),
    data.frame(xmat, stringsAsFactors = FALSE)
  )
  names(xdata) <- c(names(row.vars), cols$col_keys)

  ft <- regulartable(xdata)
  ft <- set_header_df(ft, cols)
  ft <- theme_booktabs(ft)
  ft <- merge_v(ft, j = names(row.vars))
  ft
}
  1. Create a flextable.

flext <- ftable_to_flextable(ft)

  1. Merge two header titles.
flext <- merge_at(flext, i = 1, j = 2:3, part = "header")
flext <- merge_at(flext, i = 1, j = 4:5, part = "header")

Running flext now returns:

enter image description here

  1. Now, I want the change the two labels in the first header row of the flat contingency table.

set_header_labels(flext, A = "aa", B = "bb")

I get the following error message:

Error in `[<-`(`*tmp*`, i, j, value = value) : subscript out of bounds

Upvotes: 2

Views: 5264

Answers (1)

David Gohel
David Gohel

Reputation: 10675

this should do the job with your code

# flext <- flextable::compose(flext, i = 1, j = 2, part = "header", value = as_paragraph("aa"))
# flext <- flextable::compose(flext, i = 1, j = 4, part = "header", value = as_paragraph("bb"))

If that helps, this is how I would implement it using generic as_flextable:

library(flextable)

dat <- data.frame(
  ID = 1:10,
  header1 = c("A", "A", "A", "A", "A", "B", "B", "B", "B", "B"),
  header2 = c("No", "No", "No", "Yes", "Yes", "Yes", "Yes", "No", "Yes", "No"),
  q1 = c(1, 3, 2, 2, 3, 1, 2, 1, 1, 3), 
  stringsAsFactors = FALSE
)

dat$header1 <- factor(dat$header1, levels = c("A", "B"), labels = c("aa", "bb"))
dat$header2 <- factor(dat$header2, levels = c("No", "Yes"), labels = c("nope", "ok"))
ft <- ftable(dat,
  row.vars = c("q1"),
  col.vars = c("header1", "header2")
)


as_flextable.ftable <- function(x, ...) {
  row.vars <- attr(x, "row.vars")
  col.vars <- attr(x, "col.vars")
  rows <- rev(expand.grid(rev(row.vars), stringsAsFactors = FALSE))
  cols <- rev(expand.grid(rev(col.vars), stringsAsFactors = FALSE))

  xmat <- as.matrix(x)
  cols$col_keys <- dimnames(xmat)[[2]]
  xdata <- cbind(
    data.frame(rows, stringsAsFactors = FALSE),
    data.frame(xmat, stringsAsFactors = FALSE)
  )
  names(xdata) <- c(names(row.vars), cols$col_keys)

  ft <- flextable(xdata)
  ft <- set_header_df(ft, cols)
  ft <- theme_booktabs(ft)
  ft <- merge_h(ft, i = seq_len(ncol(rows)), part = "header")
  ft
}

as_flextable(ft)

flextable

This may need additional work but not that much

Upvotes: 2

Related Questions