Chapter 6 Formatting

There are many ways to customize the formatting of an R Markdown document. In the YAML header, you can specify different parameter options or reference external documents. You can also edit templates directly in certain cases. For local formatting, you can include inline code in the markdown sections.

6.1 YAML parameters

There are several general YAML options that you can include in the YAML header to format your documents. You can also add params to the YAML header that you can specify when you render your document and call in your code chunks to make parameterized reports. Some YAML options require quotation marks, while others don’t. In general, if the YAML option is a string of text that you’re specifying, like the title or your name, then it should be in quotes. If you’re setting a programmatic option, like the output type, then it shouldn’t be in quotes.

For the string options, there’s some custom formatting you can do. The example below shows how you can center the document title and force a line break in a particular place.

---
title: |
  <center> A very impressive title: </center>
  <center> An equally impressive subtitle </center>
---

This example shows how you can automatically pull the date and time when you knit your document and format it in a particular way. This is actually R code that is embedded in a string by putting the function call between backticks with a lowercase r. You can use the same principle to put R code and functions in the text of an R Markdown document, as described in Section 5.4.

---
date: "March 24, 2021"
---
# Show output from date formatting code
  # You can run ?Sys.time in the console for more information 
  # on the options you can pass to format
format(Sys.time(), '%B %d, %Y')
## [1] "March 24, 2021"

There are also template-specific parameters, but you’ll need to look at the specific package documentation to know what these are.

6.2 YAML references

In your YAML header, you can reference other documents for formatting and content.

6.2.1 .bib

To cite references, you need to set the bibliography option in the YAML header. I use BibDesk for my reference manager, which creates a .bib file, or you can create a .bib file directly in LaTeX. You can also construct a .bib file through R. See Section 9.1.5 for examples with the scholar package.

---
bibliography: packages.bib
---

By default, if you reference a .bib file, the references will appear at the very end of the document. This is usually fine, but sometimes you may want to control the placement of the references, as with a CV. In this case, you can use a .lua filter as described in Section 6.2.4 to place your references in a particular spot.

To create in-text citations, you’ll use the cite key from your .bib file with the @ symbol. Full information on citing syntax can be found here. When you cite something from your .bib file, it will appear in your references section when you knit your document. If you want to include all of the references from your .bib file in your reference section, regardless of whether you’ve cited them or not in the document, set the nocite option in the YAML header to “@*”.

---
nocite: "@*"
---

6.2.2 .csl

To determine the type of formatting for your references, you can include a citation style language or .csl file. There are other ways to set the format of bibliographies, but a .csl file allows fine-grained control over citations that you can also customize. My CV repository has a customized APA .csl file for arranging references in descending order by date.

---
csl: apa.csl
---

6.2.3 .cls and .css

You can include .cls files (not to be confused with the .csl files above) for LaTeX styling or .css files for HTML styling. You can see an example of the .cls file that formats my CV here, which in this case is automatically generated by the package when rendering the document. This guide itself uses .css files that you can see here to control the formatting.

6.2.4 .lua

Sometimes, you need to interact with Pandoc directly in order to achieve a particular formatting outcome. To do this, you need to use a .lua filter.

The multiple-bibliographies.lua file is incredibly useful. It allows you to use multiple bibliographies in one document, as I do in my CV (one for publications, another for conference presentations). Even if you don’t have multiple bibliographies, using this .lua filter will allow you to place your reference section in a particular part of your document.

---
output: 
  vitae::awesomecv:
    pandoc_args: ["--lua-filter", "/path/multiple-bibliographies.lua"]
---

First, you need to add a name to each bibliography in the YAML header with an underscore.

---
bibliography_main: my_cv.bib
bibliography_present: my_presentations.bib
---

Then, you can place the reference section for each bibliography in the appropriate place. This example also shows how you can alter the formatting locally for just the reference section with LaTeX commands.

# Publications

\setlength{\parindent}{-0.2in}
\setlength{\leftskip}{0.2in}
\noindent

::: {#refs-main}
:::

Another very useful .lua file handles in-text APA citations. By default, Pandoc uses “&” for in-text citations where APA would require “and,” even if you’re using a .csl file for APA formatting. Rather than downloading a file like multiple-bibliographies.lua, the file shown below is already stored with Pandoc; you just need to reference it. Follow these instructions to access and reference this .lua filter

The example below from my Master’s thesis shows how you can reference multiple .lua filters in one document.

---
output:
  bookdown::word_document2:
    pandoc_args: [
      "--lua-filter", "/path/multiple-bibliographies.lua",
      "--lua-filter", "/path/replace_ampersands.lua"]
---

6.3 Editing templates

To make extremely custom edits to templates, sometimes you have to edit the template documents directly. If the template generates a style document (e.g., .cls) in the directory with your .Rmd file, you can usually edit that without going to the package. Otherwise, you have to find out where your computer stores your R packages and edit the template there.

6.3.1 Directory documents

Some templates output formatting documents in your working directory. These are easier to access and edit. For example, knitting the vitae::awesomecv template creates a .cls file that can be edited to change font sizes/colors.

6.3.2 Package documents

To find out where your packages “live,” you can call installed.packages. When you leave the parentheses blank (i.e., if you don’t provide any arguments), as I’ve done below, the function will return all of the packages you have installed. I’ve stored them as a dataframe, and then filtered for an example. Here, I’m looking at the posterdown package.

# Make dataframe with installed packages
pkgs <- installed.packages() %>%
  as.data.frame()

# Pull posterdown package 
pstr <- pkgs %>% 
  select(Package, LibPath, Version, Depends, Imports) %>%
  dplyr::filter(Package == "posterdown")

# Make table
kable(pstr) %>%
  kable_styling(bootstrap_options = "condensed", full_width = FALSE, font_size = 12)
Package LibPath Version Depends Imports
posterdown posterdown /Library/Frameworks/R.framework/Versions/4.0/Resources/library 1.0 NA pagedown, rmarkdown, yaml

To edit package documents once you’ve located them, you should proceed with caution. Be sure to:

  • Save the original template and move it to a different location (in the example below, I made the original folder and put the original template there)
  • Make one change at a time, and then re-knit the document to see what changed
  • Name the updated template with the same name in the same place as the original (here, in the “resources” folder)

6.4 Inline code

R Markdown supports inline code for custom formatting.

6.4.1 LaTeX

In PDFs, you can use code, typesetting commands (e.g., \vspace{12pt}), and specific packages from LaTeX. There are useful lists of symbols here and here. Check out Writing Your Thesis with R Markdown and Section 6.4.1 below for examples using LaTeX packages and typesetting commands.

Below, I’ve included an example from my statistics homework.

---
output: pdf_document
header-includes:
- \usepackage{fancyhdr}
---

\pagestyle{fancy}
\fancyhead[LE,RO]{Holly Zaharchuk}
\fancyhead[LO,RE]{PSY 508}

# Problem Set 12

6.4.2 CSS/HTML

In HTML documents, you can include CSS commands. I have some examples in these revealjs slides, where I wanted to left-align slide text while keeping titles centered.

Here are some examples of using CSS from my Psychonomics poster. This first example includes custom CSS in a knitr::kable table to add borders of a particular color between some rows.

# Knit table
knitr::kable(stim_table) %>%
  kableExtra::kable_styling(font_size=35) %>%
  collapse_rows(columns = 1:6, valign = "middle") %>%
  row_spec(4, extra_css="border-top: 5px solid #d7d8db; 
                         border-bottom: 5px solid #d7d8db") %>%
  row_spec(3, extra_css="border-top: 5px solid #d7d8db") %>%
  footnote(general="ERP time-locked to second modal (could or should) 
                    in double modal sentences to compare to standard single modal",
           general_title="")

The example below changes the font size of the references section at the end of the poster.

# References and acknowledgements

<font size=3><div id="refs"></div></font>

6.5 Custom functions

If you’re fairly comfortable with R, you can write formatting functions for yourself to make your life easier when you’re referencing variables in your text.

When I was working on my Master’s thesis, I wrote some custom functions for statistical values that I had to report over and over. This was particularly useful when I had to switch from referring to any p values over .05 as p > .05 to p = the exact value. I just had to change the one function by commenting out a couple lines and re-knit my document.

You can find all of my convenience functions in the source script in my reproducible scientific manuscripts template.

# Make number formatting function for p values
# val is the p value I want to format
# format_code specifies whether I want to include the symbol or not
  # format_code defaults to including the symbol
p_formatting <- function(val, format_code = 1) {
  
  # If I want to include the symbol (when I'm calling this variable in text)
  if (format_code == 1) {
    
    # If the p value is less than .001, get the less than symbol
      # Otherwise, get the equals symbol
      # The next line, which I commented out for my thesis,
      # looks to see if the value was greater than .05
    sign_type <- if_else(val < 0.001, "<", "=")
    sign_type <- if_else(val > 0.05, ">", sign_type)
    
    # If the p value is less than .001, set the value equal to .001
    if (sign_type == "<") {
      val <- ".001"
      
    # If the p value is greater than .05, set the value equal to .05 
      # I commented out these next two lines for my thesis
    } else if (sign_type == ">") {
      val <- ".05"
      
    # Otherwise, get the actual p value,
      # round it to three decimal places, and
      # remove the leading zero
    } else {
      val <- sprintf("%.3f", val)
      val <- substring(val, 2)
    }
    
    # Combine the new/formatted p value with >, <, or =,
      # depending on the p value
    val_string <- paste(sign_type, val, sep = " ")
    val_string
  
  # If I don't want the symbol (as in a table),
    # just round the value to three decimal places
    # and remove the leading zero
  } else if (format_code == 0) {
    
    val <- sprintf("%.3f", val)
    val_string <- substring(val, 2)
    val_string
    
  }
  
}

I’ve included an example with this function below to demonstrate how you can really streamline your analysis-to-presentation pipeline with a simple function.

# Create arbitrary p value
p_value <- 0.0123

# Format p value including symbol
p_value_1 <- p_formatting(p_value)

# Format p value not including symbol
p_value_0 <- p_formatting(p_value, format_code = 0)

Here is the original p value: 0.0123. I can write *p* `r p_value_1` to reference the variable that already has the symbol: p = .012. Or I can reference the variable without the symbol, like so `r p_value_0`: .012.