rknitrr-flextable

Set a new header to the table dynamically from the labels of banner


I have a function which is creating table one by one for the list of banner. but now want to show them as binding as cbind.

I am able to bind the table by columns but not able to make banners as headers for table. also I want to remove names like n.x, n.y . just wanted to keep them N and i needed a flextable output

currently table is coming like below enter image description here

library(knitr)
library(tidyverse)
library(flextable)

banner <- c("All","Other")
t1 <- structure(list(` ` = c("CA", "USA","UK", "GER", "Italy","France", "China"),
                                               Local = c("38%", "58%","71%", "78%", "91%", "91%", "84%"),
                                               Outsider = c("43%", "28%", "29%"," 9%", " 7%", " 4%", " 5%"), 
                                               Mixed = c("19%","13%", " 0%", "13%", " 1%", " 5%", "11%"),
                                               N = c("9999", "9999","9999", "9999", "9999", "9999", "9999")), row.names = c(NA, -7L), class = "data.frame")

t2 <- structure(list(` ` = c("CA", "USA","UK", "GER", "Italy","France", "China"),
                     Local = c("71%", "93%","96%", "96%", "96%", "96%", NA),
                     Outsider = c(" 7%", " 4%", " 4%"," 0%", " 0%", " 0%", NA),
                     Mixed = c("21%"," 4%", " 0%", " 4%", " 4%", " 4%", NA),
                     N = c("2800", "2800","2800", "2800", "2800", "2800", NA)), row.names = c(NA, -7L), class = "data.frame")
tl <- list(t1,t2)


fun1 <- function(tl,banner){
  t_list1<-list()
  for (i in 1:length(banner)) 
  {
    t_list <- list()
    t_list1[[i]] <- tl[[i]]
  }
  t1<-Reduce(function(...)merge(...,all=TRUE,by=" "),t_list1)
  
  t1 <- t1 %>% flextable()
  t1
}

fun1(tl=tl,banner = banner)

the output should be look llike below

enter image description here


Solution

  • One option to fix both your issues would be to first rename the column names of your sublists and add the banner as a prefix. Additionally I convert your lists to dataframe at this step. After doing so you column names are unique and no .x or .y will be added when using merge. And as a second benefit we can now utilize separate_header() to split the column names to achieve your desired final result:

    library(flextable)
    
    tl <- list(t1, t2)
    names(tl) <- banner
    
    tl <- Map(
      function(x, y) {
        names(x)[-1] <- paste(y, names(x)[-1], sep = "_")
        data.frame(x, check.names = FALSE)
      },
      tl, names(tl)
    )
    
    fun1 <- function(tl) {
      t1 <- Reduce(function(...) merge(..., all = TRUE, by = " "), tl)
    
      t1 |> 
        flextable() |> 
        separate_header()
    }
    
    fun1(tl = tl)
    

    EDIT Of course could you move Map inside your function to do the renaming.

    library(flextable)
    
    tl <- list(t1, t2)
    
    fun1 <- function(tl, banner) {
      tl <- Map(
        function(x, y) {
          names(x)[-1] <- paste(y, names(x)[-1], sep = "_")
          data.frame(x, check.names = FALSE)
        },
        tl, banner
      )
      
      t1 <- Reduce(function(...) merge(..., all = TRUE, by = " "), tl)
    
      t1 |> 
        flextable() |> 
        separate_header()
    }
    
    fun1(tl = tl, banner = banner)
    

    enter image description here