Related
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
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.
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.
Output first, format in Word later: Again, needed manipulation one-by-one. Would be an option if it would work everything out automatically.
Convincing the school bureaucrats to accept html/pdf/latex as output: this is forever an impossible option.
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:
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)
```
An option that probably falls in the no-go zone for this open issue:
Create a html document,
Insert html tags for subscripts,
Open the html file (not the viewer),
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:
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:
I am creating a document with Rmarkdown. The document will have lots of different versions and all of the text for the different versions has been written in an Excel spreadsheet, which is then read into the Rmarkdown file. Inside the text, which sometimes differs between reports, there are keywords in square brackets which need to be replaced with r code. I am having trouble getting the rcode to evaluate inside the text and print out properly in the Rmarkdown output.
# Text like that in the Excel spreadsheet
report_text <- ("There are [NumberFruit] fruits in the fruitbowl. [HighestPercent]% of the fruit are [HighestPercentType].")
#Extract variables within the square brackets
variables <- str_extract_all(report_text, "\\[[A-Z,a-z]+\\]")
# Define all varaibles - the variables are the same in each report. The data in the actual report differs and is defined from a dataframe.
for (i in unlist(variables)){
if(grepl("NumberFruit", i)){
NumberFruit <- 10
} else if(grepl("HighestPercent", i)){
HighestPercent <- 56
} else if(grepl("HighestPercentType", i)){
HighestPercentType <- "apples"
} else if (length(unlist(variables)) > 3){
stop("Additional VARIABLE:",i, "has not been assigned")
}
}
Once the variables have been defined I would normally use something like below, but as the text isn't hardcoded into the Rmarkdown file this approach isn't possible.
final_text <- paste0("There are ",NumberFruit, " fruits in the fruitbowl. ",HighestPercent, "% are ",HighestPercentType, ".")
So I have tried formatting the text as per the paste option above, but this does not produce the desired output.
report_text2 <- gsub('\\[', '",',(gsub('\\]', ', "', report_text)))
#Also tried
report_text2 <- paste0(gsub('\\[', '",',(gsub('\\]', ', "', report_text))))
I then use r final text in the Rmarkdown text to create the text in the report. Both versions of the above code have the same result shown below.
Current Rmarkdown output: There are ",NumberFruit," fruits in the fruitbowl. ",HighestPercent, "% of the fruit are ",HighestPercentType, "."
Desired Rmarkdown output: There are 10 fruits in the fruitbowl. 56% of the fruit are apples.
I have googled for clues on what else to try but have not been able to find anything and am a bit stuck on where to go from here. Any advice on how to get this working would be greatly appreciated. I do not normally deal with text strings and feel like I am missing something fundamental here.
If we need to interpolate the variables created in RMD file, an option is with glue
library(glue)
library(stringr)
final_text <- glue::glue(str_replace_all(str_replace_all(report_text,
"\\[", "{"), "\\]", "}"))
Full code in RMD file
---
title: "test"
author: "akrun"
date: "16/03/2021"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
## R Markdown
```{r test1}
report_text <- "There are [NumberFruit] fruits in the fruitbowl. [HighestPercent]% of the fruit are [HighestPercentType]."
NumberFruit <- 10
HighestPercent <- 56
HighestPercentType <- "apples"
```
```{r test2}
library(stringr)
final_text <- glue::glue(str_replace_all(str_replace_all(report_text, "\\[", "{"), "\\]", "}"))
```
`r final_text`
-output
I'm creating a table in a rmarkdown/html document and it's inadvertently changing cell values to an ordered list when in the format of for example (123). An example is below. Any help would be most welcomed.
---
output: html_document
---
df <-
data.frame(
col1 = c("a", "b"),
col2 = c("(123)", "(61)"),
stringsAsFactors = FALSE
)
knitr::kable(
df,
format = "html",
align = c('lr')
)
The values are recognized as markdown and pandoc thinks that (123) and (61) are list items. Try
df <- data.frame(col1 = c("a", "b"),
col2 = c("(123)", "(61)"),
stringsAsFactors = F)
df$col2 <- gsub("\\(", "(", df$col2)
df$col2 <- gsub("\\)", ")", df$col2)
to replace the parenthesis by their HTML entity code.
Note: stringsAsFactors = F is the new default in R > 4.0
Another approach is to disable the fancy_lists extension. This will apply globally to the document, so you might prefer #MartinSchmelzer's solution.
Just put this in your YAML:
output:
html_document:
md_extensions: "-fancy_lists"
After you've done this, you'll only get ordered lists with numbers (no letters or Roman numerals), and they have to be in the format 1., not (1) or 1), etc.
I have read the bookdown book and still cannot figure this out. I am trying to create a Word report through bookdown. I want to use kableExtra to add striping to my tables and also to bold my last table row. Can kableExtra be used when knitting to Word ?
This is a subset of my code :
library(dplyr)
knitr::opts_chunk$set(echo = TRUE)
library(knitr) # required for kable
library(kableExtra) # required for kableExtra
options(knit.r.table.format = "markdown")
myRegion <- c("a", "b", "c")
Current_Perc_1 <- c(85.9, 90.8, 89.7)
Current_Perc_2 <- c(88.0, 91.0, 89.0)
tab_curr_est_2_times <- cbind(myRegion, Current_Perc_1, Current_Perc_2)
tab_curr_est_2_times <- as.data.frame(tab_curr_est_2_times, stringsAsFactors = FALSE)
tab_curr_est_2_times$Current_Perc_1 <- as.double(tab_curr_est_2_times$Current_Perc_1)
tab_curr_est_2_times$Current_Perc_2 <- as.double(tab_curr_est_2_times$Current_Perc_2)
tab_curr_est_2_times$curr_change_1_to_2 <- tab_curr_est_2_times$Current_Perc_2 - tab_curr_est_2_times$Current_Perc_1
tab_1_curr <- tab_curr_est_2_times
tab_1_curr[ nrow(tab_1_curr)+1 , ] <- NA
tab_1_curr$myRegion[ nrow(tab_1_curr) ] <- "BRITISH COLUMBIA"
tab_1_curr$Current_Perc_1[ nrow(tab_1_curr) ] <- 88.4
tab_1_curr$Current_Perc_2[ nrow(tab_1_curr) ] <- 89.3
tab_1_curr$curr_change_1_to_2[ nrow(tab_1_curr) ] <- 0.9
knitr::kable(tab_1_curr, digits = 1, align = "lccc", position = "c",
caption = "\\: my table caption here") %>%
kable_styling("striped") %>%
row_spec(nrow(tab_1_curr), bold = TRUE)
My bookdown settings are as follows:
---
title: "My Report"
author: "Me"
date: "`r Sys.Date()`"
site: "bookdown::bookdown_site"
output:
bookdown::word_document2:
fig_caption: true
documentclass: book
---
When I click on the Knit button in RStudio, I get this table:
I want the last row to be bold and I want the table striped. How do I do this ? (I also get the following error: "Currently generic markdown table using pandoc is not supported.")
This was not possible but since pandoc V2 is out, you can do it with package flextable (>= 0.4.0)(and pandoc V2). Below the code you should add into a code chunk:
library(magrittr)
library(flextable)
tab_1_curr <- structure(list(myRegion = c("a", "b", "c", "BRITISH COLUMBIA"
), Current_Perc_1 = c(85.9, 90.8, 89.7, 88.4), Current_Perc_2 = c(88,
91, 89, 89.3), curr_change_1_to_2 = c(2.09999999999999, 0.200000000000003,
-0.700000000000003, 0.9)), .Names = c("myRegion", "Current_Perc_1",
"Current_Perc_2", "curr_change_1_to_2"), row.names = c(NA, 4L
), class = "data.frame")
regulartable(tab_1_curr) %>%
bold(i = ~ myRegion %in% "BRITISH COLUMBIA") %>%
theme_zebra() %>%
autofit()
The huxtable package is available. It includes similar table customization tools as kableExtra. huxtable is designed to output to LaTeX/PDF and HTML (similar to kableExtra). However, huxtable also includes a as_flextable function to convert a huxtable object to a flextable object, which can be output to Word (as noted by David above). After a lot of searching, it seems to me like huxtable is the only available package that can easily output to all of Word, HTML, and PDF with a single package.
Pandoc version ≥2
Yes, this can be done with Pandoc (see David's answer)
Original answer follows:
Pandoc version <2
The conversion to word is made via pandoc. Currently pandoc only creates four type of tables,
simple tables
multiline_tables
grid_tables
pipe_tables
Some of those supported formats are demontrated in pander and in the pandoc manual p 35-39.
So you cannot create the stripped table currently with pandoc.
You also have a good summary of how you can use tables in rmarkdown.rstudio.
I have a simple data structure: cases are countries and for each country I have a couple of numeric variables. Like so:
dat <- data.frame(country = c("Belgium", "Germany", "Holland", "Ireland"), Var1 = 1:4, Var2 = 11:14)
print(dat, row.names = FALSE)
country Var1 Var2
Belgium 1 11
Germany 2 12
Holland 3 13
Ireland 4 14
The table needs to be formatted still, with headings in bold, and rows colored in grey or white, alternatingly.
Now, what I want is to add two additional columns, in between "country" and "Var1". The first new column is called "flag" and should contain the country's flag. The second new column is called "flagged" and contains an image of a red flag is the country scores very bad on a particular human rights issue, an orange flag if it scores mediocre and nothing elsewise.
How can I create an object that prints that way in R? How to combine images with data in such a layout?
(eventually this will part of a larger document created with knitr)
If you are using knitr with the rmarkdown package, it is pretty easy -- just use the Markdown syntax ![]() to include images, e.g.
---
title: "Flags"
author: "Yihui Xie"
date: "2014/08/03"
output: html_document
---
```{r results='asis'}
dat <- data.frame(
country = c('Canada', 'United Kindom'),
abbr = c('ca', 'gb'),
var1 = c(1, 2),
var2 = rnorm(2)
)
dat$flag <- sprintf('![](http://flagpedia.net/data/flags/mini/%s.png)', dat$abbr)
library(knitr)
kable(dat)
```
If you need LaTeX/PDF output, you have to download these images by yourself. Here is an example:
---
title: "Flags"
author: "Yihui Xie"
date: "2014/08/03"
output: html_document
---
```{r}
dat <- data.frame(
country = c('Canada', 'United Kindom'),
abbr = c('ca', 'gb'),
var1 = c(1, 2),
var2 = rnorm(2)
)
dat$file <- paste0(dat$abbr, '.png')
dat$link <- paste0('http://flagpedia.net/data/flags/mini/', dat$file)
dat$flag <- sprintf('![](%s)', dat$file)
for (i in seq_len(nrow(dat))) {
if (!file.exists(dat$file[i])) xfun::download_file(dat$link[i])
}
knitr::kable(dat[, -c(5, 6)])
```
With this experimental fork of gtable, you can do,
require(gtable)
dat <- data.frame(country = c("Belgium", "Germany", "Holland", "Ireland"), Var1 = 1:4, Var2 = 11:14)
g <- gtable_table(dat)
library(png)
# pirate-land flag for illustration
img <- readPNG(system.file("img", "Rlogo.png", package="png"), native = FALSE)
imgRgb <- rgb(img[,,1],img[,,2],img[,,3])
dim(imgRgb) <- dim(img)[1:2]
flags <- replicate(nrow(g), rasterGrob(imgRgb), simplify = FALSE)
g <- gtable_add_cols(g, unit(1,"cm"), 0)
g <- gtable_add_grob(g, flags, t = seq_len(nrow(g)), l=1, r=1, z=1)
grid.newpage()
grid.draw(g)
formatting options described here
The question was asked with pdf as the output, here is an answer using knitr, and something more appropriate than just \includegraphics
The trick is to use the adjustbox package in latex, with the following arguments:
the height (later used as the argument to the R function get_picture_code) is the height of the picture.
the valign argument (which default here to valign=m), will perform vertical adjustment according to the text,
the margin here defined as 1ex surrounding the picture allows to separate the flags.
So we just use this function
get_picture_code <- function(path,height,col=NULL)
{
paste0("\\adjustimage{height=",height,",valign=m,margin*=1ex}{",path,"}")
}
To get a vector of images added to the table.
Finally we use xtable with argument sanitize.text.function = identity to print the tex code :
\documentclass{article}
\usepackage{adjustbox}
\begin{document}
<<load_libraries, echo = FALSE, eval = TRUE, results ="hide">>=
library(knitr)
library(xtable)
#
<<include_images, echo = FALSE, eval = TRUE, results ="hide">>=
get_picture_code <- function(path,height,col=NULL){
paste0("\\adjustimage{height=",height,",valign=m,margin*=1ex}{",path,"}")
}
#
<<test, echo = FALSE, eval = TRUE, results ="hide">>=
dat <- data.frame(country = c("Belgium", "Germany", "Holland", "Ireland"),
Var1 = 1:4, Var2 = 11:14)
mypath <- paste0("images/",dat$country,".png")
dat$flag <- get_picture_code(path=mypath,height="0.8cm")
dat$test <-NA
dat$test[2:3] <-get_picture_code(path="images/orange_flag",height="0.6cm")
print(xtable(dat,
align = c("l","l","l","l","c","c"),
caption = "Example with flags"),
sanitize.text.function = identity,
file="table_with_images.tex")
#
\input{table_with_images.tex}
\end{document}
The adjustbox documentation contains many other options, including background colors, trim, horizonal alignment which will allow you to do some very fine adjustment of the position of the images...
There is also a nice example of the use of this package in TeX-Latex stackexchange