Skip to content

Instantly share code, notes, and snippets.

@kenjisato
Last active December 1, 2020 05:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kenjisato/38a646042ab6d64788c24c51cf36aae7 to your computer and use it in GitHub Desktop.
Save kenjisato/38a646042ab6d64788c24c51cf36aae7 to your computer and use it in GitHub Desktop.
Cross Referenceable Equation with Preview in RMarkdown: http://en.kenjisato.jp/rmarkdown-math/
---
title: Math environment inside $$
author: Kenji Sato
output:
bookdown::pdf_document2:
toc: false
fig_caption: yes
knit: .render_for_tex
---
# Matheamtical equation with `bookdown::pdf_document2`
You can cross-reference equations in Rmarkdown if you set output to
`bookdown::pdf_document2` and write equations within
`\begin{align} ~ \end{align}` or `\begin{equation} ~ \end{equation}`
An example:
$$
\begin{equation}
f(x) = f(0) + \int_0^x f'(y) dy, (\#eq:ftc)
\end{equation}
$$
which we can cross-reference with a syntax different from LaTeX: Equation \@ref(eq:ftc). See [this stackoverflow answer by Yihui Xie](http://stackoverflow.com/a/38884378/1877682) and
[the related section of his bookdown book](https://bookdown.org/yihui/bookdown/markdown-extensions-by-bookdown.html#equations) for more detail.
`\begin{equation*} ~ \end{equation*}` also produces an equation without
number:
$$
\begin{equation*}
a^n + b^n = c^n
\end{equation*}
$$
# Drawback
A drawback of using `\begin{align} ~ \end{align}` or the like is that
RStudio doesn't support math preview for them (yet). You must embrace
the whole math environment with `$$`. (Figure \@ref(fig:preview))
```{r preview, fig.cap="Math preview", echo=FALSE}
knitr::include_graphics("fig/math.png")
```
The former however causes the "Bad mathe delimiter" error
in the process of tex compilation.
# Workaround
### Step 1 {-}
Put the following code snippet in .Rprofile file of the project.
```{r}
.beginMath = c(
"\\begin{equation}",
"\\begin{equation*}",
"\\begin{align}",
"\\begin{align*}"
)
.endMath = c(
"\\end{equation}",
"\\end{equation*}",
"\\end{align}",
"\\end{align*}"
)
.render_for_tex = function(input, ...){
output_file = gsub("\\.[R|r]md$", ".tex", input)
lines = readLines(input, encoding = "UTF-8");
for (i in seq_along(lines)) {
# Remove $$ before \begin{equation} or the like.
if (stringr::str_trim(lines[i]) == "$$") {
if (any(startsWith(lines[i + 1], .beginMath))) {
lines[i] = ""
} else if (any(endsWith(lines[i - 1], .endMath))) {
lines[i] = ""
}
}
}
writeLines(lines,"temp.Rmd"); on.exit(unlink('temp.Rmd'))
rmarkdown::render("temp.Rmd", output_file = output_file)
}
```
### Step 2 {-}
Add `knit: .render_for_math` to the YAML header of your Rmd file.
Then the Knit button of RStudio is overwritten with the custom renderer
with preprocessing defined in .Rprofile.
.beginMath = c(
"\\begin{equation}",
"\\begin{equation*}",
"\\begin{align}",
"\\begin{align*}"
)
.endMath = c(
"\\end{equation}",
"\\end{equation*}",
"\\end{align}",
"\\end{align*}"
)
.render_for_tex = function(input, ...){
output_file = gsub("\\.[R|r]md$", ".tex", input)
lines = readLines(input, encoding = "UTF-8");
for (i in seq_along(lines)) {
# Remove $$ before \begin{equation} or the like.
if (stringr::str_trim(lines[i]) == "$$") {
if (any(startsWith(lines[i + 1], .beginMath))) {
lines[i] = ""
} else if (any(endsWith(lines[i - 1], .endMath))) {
lines[i] = ""
}
}
}
writeLines(lines,"temp.Rmd"); on.exit(unlink('temp.Rmd'))
rmarkdown::render("temp.Rmd", output_file = output_file)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment