Mixing purrr, flextable, and RMarkdown for table outputs

I have a solution that involves advanced knitr feature.

flextable implements a knit_print.flextable method as explained in knitr vignette. You'll learn in this documentation about that render chunk option that can allow to customise rendering in rmarkdown, and that default to knitr::knit_print. In fact, it could be any function that return a character output to be printed.

If you look at flextable:::knit_print.flextable, you'll see that for html output, it uses htmltools_value and for docx output (and pandoc > 2.0), it creates a character string with pandoc docx flags. This knit_print method is applied only if the chunk returns a flextable object.

When you map the regulartable() function, the chunk return a list. So the knit_print method is not correct. This is why if you apply knit_print inside the map you are close to the result. However the result of the chunk is still a list of element resulting for individual knit_print.flextable call.

At the end, the result of the chunk is rendered to pandoc markdown so you need the correct render function in adequation with your result.

Thus, the solution : you could use a custom rendering function that knows how to deal with a list of regulartable() output and create a character string corresponding to pandoc markdown - a concatenation of the knit_print.flextable output.

For example, a function like that:

render_custom <- function(x, ...) {
  # I use imap here because I create a title with name of the list
  # I create a character string with the result of `knit_print.flextable`
  imap_chr(x, ~ paste0("**Table: ",.y,"**\n", knit_print(.x))) %>% 
    # collapse all the results
    paste(collapse = "\n") %>% 
    # knir must use this result `asis`
    asis_output()
}

This function could then be use in the render chunk option.

Full example that renders correctly on my computer:

---
title: "PURRR and tables for word"
output: word_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(
	echo = TRUE,
	warning = FALSE,
	warnings = FALSE
)
```

```{r}
#libraries
library(knitr)
library(purrr)
library(flextable)

#the table
my_tab <- tibble::tribble(
  ~`Biogenic.Habitat/Species`, ~Limiting.Driver, ~`Max,.Min,.or.Mean`, ~Units, ~Value,
                    "Clams",  "Flow Velocity",              "Min",  "m/s",   0.04,
               "Mussel Bed",          "Depth",              "Max",    "m",     10,
               "Mussel Bed",  "Flow Velocity",              "Max",  "m/s",    1.8,
               "Mussel Bed",    "Temperature",              "Max",    "C",     32,
                  "Oysters",  "Flow Velocity",              "Max",  "m/s",    2.6,
                  "Oysters",  "Flow Velocity",              "Min",  "m/s",   1.56)

#split it to a list
my_tab_split <- my_tab %>% split(.$`Biogenic.Habitat/Species`)
```

```{r, include = FALSE}
render_custom <- function(x, ...) {
  # I use imap here because I create a title with name of the list
  # I create a character string with the result of `knit_print.flextable`
  imap_chr(x, ~ paste0("**Table: ",.y,"**\n", knit_print(.x))) %>% 
    # collapse all the results
    paste(collapse = "\n") %>% 
    # knir must use this result `asis`
    asis_output()
}
```


```{r, render=render_custom}
map(my_tab_split, ~regulartable(.x))
```

Let me know if it is clear and if it works. You can adapt render_custom to your desire.

5 Likes