Scientific formats, subscripts and superscripts in RMarkdown table (docx output)

Viewed 454

Let's say I have the following rmd:

---
title: "Table won't work"
author: "Exhausted student"
date: "2022/01/28"
output: 
  bookdown::word_document2
---

```{r table, echo=F, warning=F, message=F}
library(tidyverse)
a <- tibble(
  constants = c("c", "NA", "h", "e", "H2O"),
  values = c(2.998e8, 6.022e23, 6.626e-34, -1.602e-19, 18.02)
)

knitr::kable(a, digits = 35)
```

which produces this table in Word.

The Problem

I need the scientific format to use superscripts and multiply sign (i.e. 2.998 × 108), and some cells requires subscript (e.g. NA and H2O).

The final table should look like this. How can I do that?

What I've tried/would never try

  1. huxtable package and its markdown() function: I managed to format some contents as H~2~O, then enable markdown across table by huxtable(a) %>% `markdown<-`(TRUE). Which did not recognize the syntax, and apparently would not work in forseeable future according to the author.
  2. flextable and as_sub(): Produces right format. I pass the lables to flextable::compose(), where the labels were something like as_paragraph(list_values = list("H", as_sub("2"), "O"). The code is obviously too lengthy; plus i have to manipulate cells one-by-one. Technically still doable, but I do have tables with 100+ cells needed formatting.
  3. Output first, format in Word later: Again, needed manipulation one-by-one. Would be an option if it would work everything out automatically.
  4. Convincing the school bureaucrats to accept html/pdf/latex as output: this is forever an impossible option.
  5. Format outside word then export formatted table as image: strictly forbidden in the reports.

Edit: the table works now! Great thanks to Maël's answer, but please check my own findings to see my final result:

well-formatted table

4 Answers

You can use tildes (~) to put in subscript and carets (^) for superscripts; and use sprintf to get the expected digit format:

---
title: "Table won't work"
author: "Exhausted student"
date: "2022/01/28"
output: 
  bookdown::word_document2
---

```{r table, echo=F, warning=F, message=F}
library(tidyverse)

expSup <- function(x, digits=3) {
  sprintf(paste0("%05.", digits, "f x 10^%d^"), x/10^floor(log10(abs(x))), floor(log10(abs(x))))
}

a <- tibble(
  constants = c("c", "N~A~", "h", "e", "H~2~0"),
  values = expSup(c(2.998e8, 6.022e-23, 6.626e-34, -1.602e-19, 18.02))
)

knitr::kable(a)
```

enter image description here

An option that probably falls in the no-go zone for this open issue:

  1. Create a html document,
  2. Insert html tags for subscripts,
  3. Open the html file (not the viewer),
  4. ctrl+c, then ctrl+v in your word file.
---
output: html_document
---

```{r table, echo=F, warning=F, message=F}
library(tidyverse)
library(gt)
a <- tibble(
    constants = c("c", "N<sub>A</sub>", "h","e","H<sub>2</sub>O"),
    values = c(2.998e8, 6.022e23, 6.626e-34, -1.602e-19, 18.02)
)
a %>%
  mutate(constants = map(constants, html)) %>%
  gt() %>%
  fmt_scientific(values)

The expSup() function in Maël's answer converted the scientific formats into markdown format. For my script, I modified the function a little:

exp_sup <- function(x, digits = 3) {
  sprintf(paste0("%05.", digits, "f $\\times$ 10^%d^"), x / 10^floor(log10(abs(x))), floor(log10(abs(x))))
}

I changed "f x 10^%d^" to "f $\\times$ 10^%d^", so that it displays proper multiply symbol (×).

Using the format in flextable

The format works great in Kable. However, a large portion of my workflow requires flextable to make caption/cross reference/publication style/etc. Unfortunately, although the expSup function automatically formats scientific notations into markdowns, it cannot make markdown syntax work in flextable.

However, ftExtra::colformat_md() can. Hence, by combining the modified exp_sup() function with ftExtra, I was finally able to produce an academic-looking table:

final formatted results

Below is the code for my final output; if you are also trying to produce reproducible academic reports with lots of tables in Word format, hope this helps!

---
title: "The tables work!"
author: "Satisfied Student"
date: "2022/01/28"
output: 
  bookdown::word_document2:
    reference_docx: styleRef.docx
---
```{r setup, include = F}
library(easypackages)
packages(
  "tidyverse",
  "flextable", # This works best for my workflow
  "ftExtra", # For markdown formatting work in flextable
  "officer" # You can customize appearance/format/etc. of caption *prefixes*
)

knitr::opts_chunk$set(
  warning = FALSE,
  message = FALSE,
  echo = FALSE,

  # Make the table caption format definable in reference_docx styles
  tab.cap.style = "Table Caption",

  # Make "Table 1:" prefixes not bold
  tab.cap.fp_text = fp_text_lite(bold = FALSE)

  # The tab.cap settings MUST be in a separate chunk from tables
)

# Converts scientific format to markdown
exp_sup <- function(x, digits = 3) {
  sprintf(paste0("%05.", digits, "f $\\times$ 10^%d^"), x / 10^floor(log10(abs(x))), floor(log10(abs(x))))
}
# The $\\times$ makes proper multiply symbols
```

```{r table}
a <- tibble(
  constants = c("c", "N~A~", "h", "e", "H~2~O"),
  values = c(2.998e8, 6.022e23, 6.626e-34, -1.602e-19, 18.02)
)

a %>%
  mutate(values = exp_sup(values)) %>%
  flextable() %>%
  set_caption(
    caption = "(ref:foo)", # Produces formatted caption text
    style = "Table Caption"
  ) %>%
  colformat_md() %>% # Subscript/superscript works in flextable now!
  theme_booktabs() %>% # The 3-part-table used in academics
  align(align = "center", part = "all") %>% #Align everything to center
  set_table_properties(layout = "autofit") # Comfortable width/height every cell
```

(ref:foo) A scientifically formatted `flextable` with ^superscripts^ and ~subscripts~

Your code should look like this:

```
{r table, echo=F, warning=F, message=F}
library(tidyverse)
a <- tibble(
  constants = c("c", "NA", "h", "e", "$H_2O$"),
  values = c("$2.998 * {10^{8}}$", "$6.022 * {10^{-23}}$", "$6.626 * {10^{-34}}$", "$-1.602 * {10 ^{-19}}$", "$1.802 * {10^{1}}$")
)

knitr::kable(format = "html", a, digits = 35)
```

Which will give you the output like this:

enter image description here

Related