Author: Ethan Heinzen

# The write2 function

#### Ethan Heinzen

# Introduction

The `write2*()` functions were designed as an alternative to SAS’s `ODS` procedure for useRs who want to save R Markdown tables to separate Word, HTML, or PDF files without needing separate R Markdown programs.

There are three shortcut functions for the most common output types: HTML, PDF, and Word. Each of these three functions calls `write2()`, an S3 function which accepts many file output types (see the help pages for `rmarkdown::render()`). Methods have been implemented for `tableby()`, `modelsum()`, and `freqlist()`, but also `knitr::kable()`, `xtable::xtable()`, and `pander::pander_return()`.

The two most important things to recognize with `write2()` are the following:

1. Which function is being used to output the object. Sometimes the `write2` functions use `summary()`, while other times they will use `print()`. The details for each object specifically are described below.

2. How the `...` arguments are passed. To change the options for the summary-like or print-like function, you can pass named arguments which will in turn get passed to the appropriate function. Details for each object specifically are described below.

# A note on piping

`arsenal` is piping-compatible!

The `write2*()` functions are probably the most useful place to take advantage of the `magrittr` package’s piping framework, since commands are often nested several functions deep in the context of `write2*()`. Piping also allows the `arsenal` package to become a part of more standard analysis pipelines; instead of needing to write separate R Markdown programs, intermediate analysis tables and output can be easily incorporated into piped statements.

This vignette will sprinkle the foward pipe (`%>%`) throughout as a hint at the power and flexibility of `arsenal` and piping.

# Examples Using `arsenal` Objects

    library(arsenal)
    library(magrittr)
    data(mockstudy)
    tmpdir <- tempdir()

## `tableby`

For `tableby` objects, the output function in `write2()` is `summary()`. For `summary.tableby` objects, the output function is `print()`. For available arguments, see the help pages for `summary.tableby()`. Don’t use the option `text = TRUE` with the `write2` functions.

    mylabels <- list(sex = "SEX", age ="Age, yrs")
    tab1 <- tableby(arm ~ sex + age, data=mockstudy)

    write2html(
      tab1, paste0(tmpdir, "/test.tableby.html"), quiet = TRUE,
      title = "My test table",      # passed to summary.tableby
      labelTranslations = mylabels, # passed to summary.tableby
      total = FALSE                 # passed to summary.tableby
    )

## `modelsum`

For `modelsum` objects, the output function in `write2()` is `summary()`. For `summary.modelsum` objects, the output function is `print()`. For available arguments, see the help pages for `summary.modelsum()`. Don’t use the option `text = TRUE` with the `write2` functions.

    tab2 <- modelsum(alk.phos ~ arm + ps + hgb, adjust= ~ age + sex, family = "gaussian", data = mockstudy)

    write2pdf(
      tab2, paste0(tmpdir, "/test.modelsum.pdf"), quiet = TRUE,
      title = "My test table", # passed to summary.modelsum
      show.intercept = FALSE,  # passed to summary.modelsum
      digits = 5               # passed to summary.modelsum
    )

## `freqlist`

For `freqlist` objects, the output function in `write2()` is `summary()`. For `summary.freqlist` objects, the output function is `print()`. For available arguments, see the help pages for `summary.freqlist()`.

    mockstudy[, c("arm", "sex", "mdquality.s")] %>%
      table(useNA = "ifany") %>%
      freqlist(groupBy = c("arm", "sex")) %>%
      write2word(
        paste0(tmpdir, "/test.freqlist.doc"), quiet = TRUE,
        single = FALSE,         # passed to summary.freqlist
        title = "My cool title" # passed to summary.freqlist
      )

## `comparedf`

For `comparedf` objects, the output function in `write2()` is `summary()`. For `summary.comparedf` objects, the output function is `print()`.

# Examples Using Other Objects

## `knitr::kable()`

For objects resulting from a call to `kable()`, the output function in `write2()` is `print()`. There aren’t any arguments to the `print.knitr_kable()` function.

    mockstudy %>%
      head() %>%
      knitr::kable() %>%
      write2html(paste0(tmpdir, "/test.kable.html"), quiet = TRUE)

## `xtable::xtable()`

For `xtable` objects, the output function in `write2()` is `print()`. For available arguments, see the help pages for `print.xtable()`.

    mockstudy %>%
      head() %>%
      xtable::xtable(caption = "My xtable") %>%
      write2pdf(
        paste0(tmpdir, "/test.xtable.pdf"), quiet = TRUE,
        comment = FALSE, # passed to print.xtable to turn off the default message about xtable version
        include.rownames = FALSE, # passed to print.xtable
        caption.placement = "top" # passed to print.xtable
      )

To make an HTML document, use the `print.xtable()` option `type = "html"`.

    mockstudy %>%
      head() %>%
      xtable::xtable(caption = "My xtable") %>%
      write2html(
        paste0(tmpdir, "/test.xtable.html"), quiet = TRUE,
        type = "html",            # passed to print.xtable
        comment = FALSE, # passed to print.xtable to turn off the default message about xtable version
        include.rownames = FALSE, # passed to print.xtable
        caption.placement = "top" # passed to print.xtable
      )

User beware! `xtable()` is not compatible with `write2word()`.

## `pander::pander_return()`

Pander is a little bit more tricky. Since `pander::pander()` doesn’t return an object, the useR should instead use `pander::pander_return()`. For this (and for all character vectors), the the output function in `write2()` is `cat(sep = '\n')`.

    write2word(pander::pander_return(head(mockstudy)), file = paste0(tmpdir, "/test.pander.doc"), quiet = TRUE)

# Output Multiple Tables to One Document

To output multiple tables into a document, simply make a list of them and call the same function as before.

    mylist <- list(
      tableby(sex ~ age, data = mockstudy),
      freqlist(table(mockstudy[, c("sex", "arm")])),
      knitr::kable(head(mockstudy))
    )

    write2pdf(mylist, paste0(tmpdir, "/test.mylist.pdf"), quiet = TRUE)

One neat side-effect of this function is that you can output text and headers, etc. The possibilities are endless!

    mylist2 <- list(
      "# Header 1",
      "This is a small paragraph introducing tableby.",
      tableby(sex ~ age, data = mockstudy),
      "<hr>",
      "# Header 2",
      "<font color='red'>I can change color of my text!</font>"
    )
    write2html(mylist2, paste0(tmpdir, "/test.mylist2.html"), quiet = TRUE)

In fact, you can even recurse on the lists!

    write2pdf(list(mylist2, mylist), paste0(tmpdir, "/test.mylists.pdf"), quiet = TRUE)

# Output Other Objects Monospaced (as if in a terminal)

It may be useful at times to write output that would normally be copied from the terminal. The default method for `write2()` does this automatically. To output the results of `summary.lm()`, for example:

    lm(age ~ sex, data = mockstudy) %>%
      summary() %>%
      write2pdf(paste0(tmpdir, "/test.lm.pdf"), quiet = TRUE)

The `verbatim()` function is another option to explicitly alert `write2()` to do this. This becomes particularly helpful to overrule existing S3 methods.

For example, suppose you wanted to just print a tableby object (as if it were to print in the terminal):

    tab4 <- tableby(arm ~ sex + age, data=mockstudy)
    write2html(verbatim(tab4), paste0(tmpdir, "/test.print.tableby.html"), quiet = TRUE)

Or suppose you wanted to print a character vector (as if it were to print in the terminal):

    chr <- paste0("MyVector", 1:10)
    write2pdf(verbatim(chr), paste0(tmpdir, "/test.character.pdf"), quiet = TRUE)

Note that you can combine multiple objects in one call:

    write2pdf(verbatim(tab4, chr), paste0(tmpdir, "/test.verbatim.pdf"), quiet = TRUE)

# Add a YAML Header to the Output

You can add a YAML header to `write2()` output using the `yaml()` function.

    mylist3 <- list(
      yaml(title = "Test YAML Title", author = "My cool author name"),
      "# Header 1",
      "This is a small paragraph introducing tableby.",
      tableby(sex ~ age, data = mockstudy)
    )
    write2html(mylist3, paste0(tmpdir, "/test.yaml.html"), quiet = TRUE)

In fact, all detected YAML pieces will be moved as the first output, so that the above code chunk gives the same output as this one:

    mylist4 <- list(
      "# Header 1",
      "This is a small paragraph introducing tableby.",
      yaml(title = "Test YAML Title"),
      tableby(sex ~ age, data = mockstudy),
      yaml(author = "My cool author name")
    )
    write2html(mylist4, paste0(tmpdir, "/test.yaml2.html"), quiet = TRUE)

# Add a Code Chunk to the Output

It is now possible to add code chunks to the output `.Rmd`:

    mylist5 <- list(
      "# What is 1 + 2?",
      code.chunk(a <- 1, b <- 2),
      code.chunk(a + b, chunk.opts = "r echo=FALSE, eval=TRUE")
    )
    write2html(mylist5, paste0(tmpdir, "/test.code.chunk.html"), quiet = TRUE)

This allow flexibility to create objects on-the-fly, to read in saved objects to the temporary `.Rmd`, etc. The possibilities are endless!

# FAQs

## How do I suppress the note about my document getting rendered?

This is easily accomplished by using the argument `quiet = TRUE` (passed to the `rmarkdown::render()` function).

    write2html(
      knitr::kable(head(mockstudy)), paste0(tmpdir, "/test.kable.quiet.html"),
      quiet = TRUE # passed to rmarkdown::render
    )

## How do I look at the temporary `.Rmd` file?

This is easily accomplished by using the option `keep.rmd = TRUE`.

    write2html(
      knitr::kable(head(mockstudy)), paste0(tmpdir, "/test.kable.keep.rmd.html"),
      quiet = TRUE, # passed to rmarkdown::render
      keep.rmd = TRUE
    )

## How do I prevent my document from being rendered?

This is easily accomplished by using the option `render. = FALSE`. Note that this will then default to `keep.rmd = TRUE`.

    write2html(
      knitr::kable(head(mockstudy)), paste0(tmpdir, "/test.kable.dont.render.html"),
      render. = FALSE
    )

## How do I output headers, raw HTML/LaTeX, paragraphs, etc.?

One can simply abuse the list S3 method for `write2()`!

    mylist2 <- list(
      "# Header 1",
      "This is a small paragraph introducing tableby.",
      tableby(sex ~ age, data = mockstudy),
      "<hr>",
      "# Header 2",
      "<font color='red'>I can change color of my text!</font>"
    )
    write2html(mylist2, paste0(tmpdir, "/test.mylist2.html"), quiet = TRUE)

## How do I tweak the default format from `write2word()`, `write2html()`, or `write2pdf()`?

You can pass arguments to the format functions used behind the scenes.

    write2html(
      knitr::kable(head(mockstudy)), paste0(tmpdir, "/test.kable.theme.html"),
      quiet = TRUE,  # passed to rmarkdown::render
      theme = "yeti" # passed to rmarkdown::html_document
    )

See the help pages for `rmarkdown::word_document()`, `rmarkdown::html_document()`, and `rmarkdown::pdf_document()`.

## How do I output to a file format other than word, HTML, and PDF?

This can be done using the generic `write2()` function. The last argument in the function can be another format specification. For details on the acceptable inputs, see the help page for `write2()`.

    write2(
      knitr::kable(head(mockstudy[, 1:4])), paste0(tmpdir, "/test.kable.rtf"),
      quiet = TRUE,  # passed to rmarkdown::render
      output_format = rmarkdown::rtf_document
    )

## How do I avoid prefixes on my table captions in PDF?

You can do this pretty easily with the `yaml()` function:

    mylist5 <- list(
      yaml("header-includes" = list("\\usepackage[labelformat=empty]{caption}")),
      "# Header 1",
      "This is a small paragraph introducing tableby.",
      tableby(sex ~ age, data = mockstudy)
    )
    write2pdf(mylist5, paste0(tmpdir, "/test.noprefixes.pdf"), title = "My tableby")

## How do I output multiple tables with different titles?

There are now `write2()` methods for the summary objects of `arsenal` functions. This allows you to specify a title for each table:

    mylist6 <- list(
      summary(tableby(sex ~ age, data = mockstudy), title = "A Title for tableby"),
      summary(modelsum(age ~ sex, data = mockstudy), title = "A Title for modelsum"),
      summary(freqlist(~ sex, data = mockstudy), title = "A Title for freqlist")
    )
    write2pdf(mylist6, paste0(tmpdir, "/test.multiple.titles.pdf"))

## Why is `write2()` not working in R Markdown/R Studio?

It’s possible that a global option in R Studio is preventing the tables from rendering. Consider turning off (i.e., unchecking) the option Tools > Global Options > R Markdown > Show output inline for all R Markdown documents.