├── .Rprofile ├── .gitattributes ├── .github ├── .gitignore ├── FUNDING.yml └── workflows │ └── build-book.yaml ├── .gitignore ├── 00-authors.Rmd ├── 01-installation.Rmd ├── 02-overview.Rmd ├── 03-basics.Rmd ├── 04-content.Rmd ├── 05-formatting.Rmd ├── 06-latex.Rmd ├── 07-html.Rmd ├── 08-word.Rmd ├── 09-multiformat.Rmd ├── 10-tables.Rmd ├── 11-chunk-options.Rmd ├── 12-output-hooks.Rmd ├── 13-chunk-hooks.Rmd ├── 14-knitr-misc.Rmd ├── 15-languages.Rmd ├── 16-projects.Rmd ├── 17-workflow.Rmd ├── 18-references.Rmd ├── DESCRIPTION ├── Makefile ├── README.md ├── SETUP.md ├── _bookdown.yml ├── _deploy.sh ├── _knitr-options.Rmd ├── _output.yml ├── _render.R ├── css ├── box.css └── style.css ├── examples ├── README.md ├── awesomebox.Rmd ├── cat-css.Rmd ├── cat-latex.Rmd ├── chunk-custom.Rmd ├── chunk-style.Rmd ├── chunk-wrapper.Rmd ├── columns.css ├── columns.tex ├── cross-ref.Rmd ├── d3.Rmd ├── details-tag.Rmd ├── figures-side.Rmd ├── fold-show.Rmd ├── font-color.Rmd ├── generate-content.Rmd ├── global-device.Rmd ├── hook-html5.Rmd ├── hook-number.Rmd ├── hook-scroll.Rmd ├── hook-secret.Rmd ├── html-logo.Rmd ├── html-scroll.Rmd ├── html-tabs.Rmd ├── knitr-foldable-class.Rmd ├── knitr-hide.Rmd ├── knitr.Rhtml ├── knitr.Rnw ├── latex-animation.Rmd ├── latex-logo.Rmd ├── latex-subfig.Rmd ├── latex-tiny.Rmd ├── multicol-html.Rmd ├── multicol.Rmd ├── officer.Rmd ├── print-method.Rmd ├── purl.Rmd ├── python.Rmd ├── rgl-3d.Rmd ├── spin.R ├── tidy-opts.Rmd ├── tidy-width.Rmd └── wrap-text.Rmd ├── images ├── bookdown-project.png ├── bookdown-ref.png ├── caution.png ├── chunk-bg.png ├── chunk-border.png ├── cover.png ├── details-closed.png ├── details-open.png ├── diagram-params.jpg ├── diagram-profit.jpg ├── hook-html5.png ├── hook-scroll.png ├── html-scroll.png ├── html-tabs.png ├── important.png ├── knit-wd.png ├── knitr-workflow.jpg ├── latex-logo.png ├── latex-subfig.png ├── multicol.png ├── note.png ├── package-vignette.png ├── params-shiny.png ├── rgl-3d.png ├── rmd-containers.jpg ├── rmd-relative.png ├── rmd-wd.png ├── striped-table.png ├── tip.png ├── trackdown1.png ├── trackdown2.png ├── trackdown3.png ├── unnumbered-sections.png ├── visual-edit.png ├── warning.png ├── word-table.png ├── word-template-1.png ├── word-template-2.png ├── workflow.png ├── wrap-listings.png └── wrap-none.png ├── index.Rmd ├── krantz.cls ├── latex ├── after_body.tex ├── before_body.tex └── preamble.tex ├── literature.bib ├── renv.lock ├── renv ├── .gitignore ├── activate.R └── settings.dcf ├── rmarkdown-cookbook.Rproj └── utils.R /.Rprofile: -------------------------------------------------------------------------------- 1 | # source("renv/activate.R") 2 | if (file.exists('~/.Rprofile')) sys.source('~/.Rprofile', envir = environment()) 3 | 4 | options( 5 | htmltools.dir.version = FALSE, formatR.indent = 2, formatR.arrow = TRUE, 6 | width = 55, digits = 4, 7 | warnPartialMatchAttr = FALSE, warnPartialMatchDollar = FALSE 8 | ) 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.tex linguist-vendored 2 | *.cls linguist-vendored 3 | *.Rmd linguist-detectable 4 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [yihui] 2 | patreon: 3 | custom: 4 | -------------------------------------------------------------------------------- /.github/workflows/build-book.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | name: build-book 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | steps: 17 | - name: Checkout repo 18 | uses: actions/checkout@v4 19 | 20 | - name: Setup R 21 | uses: r-lib/actions/setup-r@v2 22 | with: 23 | use-public-rspm: true 24 | 25 | - name: Setup pandoc 26 | uses: r-lib/actions/setup-pandoc@v2 27 | with: 28 | pandoc-version: '2.17.1.1' 29 | 30 | - name: Install TinyTeX 31 | uses: r-lib/actions/setup-tinytex@v2 32 | env: 33 | # install full prebuilt version 34 | TINYTEX_INSTALLER: TinyTeX 35 | 36 | - uses: r-lib/actions/setup-r-dependencies@v2 37 | with: 38 | needs: book 39 | 40 | - name: Install missing system dependencies 41 | if: runner.os == 'Linux' 42 | run: sudo apt-get install -y libgtk2.0-dev asymptote 43 | 44 | - name: Build Gitbook 45 | if: github.event_name == 'pull_request' 46 | run: make gitbook 47 | 48 | - name: Build and Deploy all book 49 | if: github.event_name == 'push' 50 | env: 51 | CONNECT_API_KEY: ${{ secrets.RSC_BOOKDOWN_ORG_TOKEN }} 52 | CONTENT_ID: 7b3dedfa-fd98-45dd-bec4-75d915fb27dd 53 | run: make all 54 | 55 | - uses: actions/github-script@v7 56 | if: github.event_name == 'push' 57 | env: 58 | URL: https://bookdown.org/yihui/rmarkdown-cookbook/ 59 | with: 60 | github-token: ${{secrets.GITHUB_TOKEN}} 61 | script: | 62 | github.rest.repos.createCommitStatus({ 63 | owner: context.repo.owner, 64 | repo: context.repo.repo, 65 | sha: context.sha, 66 | state: "success", 67 | target_url: "${{ env.URL}}", 68 | description: "Book deployed!", 69 | context: "bookdown.org" 70 | }) 71 | 72 | - name: Upload book folder for debug 73 | if: failure() 74 | uses: actions/upload-artifact@v4 75 | with: 76 | name: book-dir 77 | path: _book 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | rsconnect 6 | _book/* 7 | _bookdown_files/* 8 | packages.bib 9 | *-tikzDictionary 10 | latex/blackbox.tex 11 | latex/infobox.tex 12 | 13 | # OS generated files # 14 | ###################### 15 | .DS_Store 16 | .DS_Store? 17 | 18 | # Compiled example files 19 | examples/*.pdf 20 | examples/*.html 21 | examples/*/*.pdf 22 | examples/*/*.html 23 | examples/listings-settings.tex 24 | -------------------------------------------------------------------------------- /00-authors.Rmd: -------------------------------------------------------------------------------- 1 | # About the Authors {#author .unnumbered} 2 | 3 | Yihui typed out most of the words in this book, which is the only justification for him being the "first" author. Christophe has made substantial contribution to this book by helping Yihui organize all the GitHub issues and occasionally writing a few sections. Emily was originally a reviewer of this book. Since Yihui was not patient enough to deal with her lengthy comments, he invited her to become a co-author of this book (out of revenge) to feel his pain of having to deal with so many additional things when he thought he was pretty much done! Just kidding... No, he invited her out of full appreciation, because her comments were so helpful, yet Yihui lacked the time to do all the improvements that she suggested. 4 | 5 | When you see the pronoun "I" in this book, it refers to Yihui. Using "I" instead of "We" does not mean the co-authors were forgotten, but Yihui wanted to express certain opinions completely on his own. He certainly wants to appear smart, but in case he is actually silly, he wants to be silly alone. 6 | 7 | ## Yihui Xie {-} 8 | 9 | Yihui Xie (https://yihui.org) is a software engineer at RStudio (). He earned his PhD from the Department of Statistics, Iowa State University. He is interested in interactive statistical graphics and statistical computing. As an active R user, he has authored several R packages, such as **knitr**, **bookdown**, **blogdown**, **xaringan**, **tinytex**, **rolldown**, **animation**, **DT**, **tufte**, **formatR**, **fun**, **xfun**, **testit**, **mime**, **highr**, **servr**, and **Rd2roxygen**, among which the **animation** package won the 2009 John M. Chambers Statistical Software Award (ASA). He also co-authored a few other R packages, including **shiny**, **rmarkdown**, **pagedown**, and **leaflet**. 10 | 11 | He has authored two books, _Dynamic Documents with knitr_ [@knitr2015], and _bookdown: Authoring Books and Technical Documents with R Markdown_ [@bookdown2016], and co-authored two books, _blogdown: Creating Websites with R Markdown_ [@blogdown2017], and _R Markdown: The Definitive Guide_ [@rmarkdown2018]. 12 | 13 | In 2006, he founded the Capital of Statistics (https://cosx.org), which has grown into a large online community on statistics in China. He initiated the Chinese R conference in 2008, and has been involved in organizing R conferences in China since then. During his PhD training at Iowa State University, he won the Vince Sposito Statistical Computing Award (2011) and the Snedecor Award (2012) in the Department of Statistics. 14 | 15 | He usually reads Twitter messages once a week (https://twitter.com/xieyihui), and most of the time you can find him on GitHub (https://github.com/yihui). 16 | 17 | He has four hobbies: reading, writing (mostly blogging), cooking, and playing badminton. He is actually more interested in cooking than eating. There are not many types of food that he cannot resist eating, and spicy food is one of the few. Since cooking is more fun, he rarely goes to restaurants. When he does go and is asked "how spicy you want your food to be" in the restaurant, he usually answers "as spicy as your chef can make it to be." 18 | 19 | ## Christophe Dervieux {-} 20 | 21 | Christophe Dervieux is an active member of the R community, currently living in France. With a master's degree in energy and economics, he started working with R as an analyst doing economic studies about market designs, before becoming a developer advocate and R admin, promoting R and supporting R users where he works. 22 | 23 | He is interested in helping others get the most from R, and you can find him wandering in the RStudio Community as a sustainer, or on several GitHub issue boards for various R packages. In both places, you may better recognize him by his shorter handle "cderv." 24 | 25 | As an R developer, he is a contributor to several R packages, such as **bookdown**, **rmarkdown**, and **knitr**. He has also co-authored the **crrri** package. His own projects can be found on GitHub (https://github.com/cderv), and sometimes he shares his ideas on Twitter (https://twitter.com/chrisderv). 26 | 27 | He does not like spicy food, but he enjoys playing badminton every week. 28 | 29 | ## Emily Riederer {-} 30 | 31 | Emily Riederer works in data science for the consumer finance industry where she leads a team to build analysis tools in R and cultivate an open science culture in industry. Previously, she studied mathematics and statistics at the University of North Carolina at Chapel Hill. 32 | 33 | Emily frequently discusses R on Twitter (https://twitter.com/emilyriederer) and in her blog (https://emily.rbind.io) and shared projects, including her **projmgr** R package, on GitHub (https://github.com/emilyriederer). She has also served as a package reviewer for rOpenSci and a founding co-organizer of the annual satRday Chicago R conference. 34 | 35 | Emily's other interests include reading and weightlifting. She thinks she likes spicy food, but since she has only ever lived in the United States, she has been told that she does not really know what that actually means. 36 | -------------------------------------------------------------------------------- /01-installation.Rmd: -------------------------------------------------------------------------------- 1 | \mainmatter 2 | 3 | # Installation 4 | 5 | To use R Markdown, you have to install R [@R-base] and the R package **rmarkdown** [@R-rmarkdown]. 6 | 7 | ```{r eval=FALSE} 8 | # install the rmarkdown package from CRAN in R 9 | install.packages('rmarkdown') 10 | 11 | # or install from GitHub if you want to test the development version 12 | if (!requireNamespace("remotes")) install.packages('remotes') 13 | remotes::install_github('rstudio/rmarkdown') 14 | ``` 15 | 16 | Unless you have a favorite editor or IDE (Integrated Development Environment), we recommend that you also install the RStudio\index{RStudio} IDE (https://www.rstudio.com). RStudio is not required, but it will make it easier for an average user to work with R Markdown because of the strong editor support. If you choose not to use the RStudio IDE, you will need to install Pandoc\index{Pandoc} (see Section \@ref(install-pandoc)), which is the tool used by **rmarkdown** to convert Markdown to other document formats. 17 | 18 | If you need to create PDF output, you may need to install LaTeX\index{LaTeX} (Section \@ref(install-latex)) and certain LaTeX packages (Section \@ref(install-latex-pkgs)). 19 | 20 | ## Use a Pandoc version not bundled with the RStudio IDE {#install-pandoc} 21 | 22 | The RStudio IDE has bundled a version of Pandoc\index{Pandoc}, so you do not need to install Pandoc by yourself if you use the RStudio IDE. However, for some advanced usage, the bundled version may differ from the exact version you want. You can choose to install a separate copy of Pandoc by yourself. Please remember that the bundled version may be more thoroughly tested with R Markdown, because most RStudio users may just use the bundled version. If you want to go with a different version (especially a higher version), you might run into problems that other R Markdown users or developers have yet to discover. 23 | 24 | Some configurations are required if you have installed a specific Pandoc version by yourself. 25 | 26 | First, before running `rmarkdown::render()`, you may inform the **rmarkdown** package by calling the function `rmarkdown::find_pandoc()` in the same R session, e.g., 27 | 28 | ```{r, eval=FALSE} 29 | # to find a specific version (e.g., if lower version on PATH) 30 | rmarkdown::find_pandoc(version = '2.9.1') 31 | 32 | # to find Pandoc under a specific directory (e.g., if a specific version cannot be in PATH) 33 | rmarkdown::find_pandoc(dir = '~/Downloads/Pandoc') 34 | 35 | # ignore the previously found Pandoc and search again (i.e., opt out of the caching mechanism). 36 | rmarkdown::find_pandoc(cache = FALSE) 37 | ``` 38 | 39 | As you can see in the above code chunk, several ways exist to find a version of Pandoc. By default, `rmarkdown::find_pandoc()` tries to find your system's highest version of Pandoc. Once found, the version information is cached, and you can invalidate the cache with `cache = FALSE`. Please see the help page `?rmarkdown::find_pandoc` for the potential directories where the `pandoc` executable may be found. 40 | 41 | This function needs to be called outside of the Rmd document, as **rmarkdown** may use the Pandoc version information before knitting with code cells execution happens. 42 | 43 | 44 | If you want an Rmd document to be compiled by a specific version of Pandoc installed on your computer, the **pandoc** package [@R-pandoc]\index{R package!pandoc} will help. 45 | This package is designed to help test R code with different Pandoc versions. It allows installing and managing several Pandoc binary versions on the system and easily switching between versions. The function `pandoc::with_pandoc_version()` can help to render a document with a specific version of Pandoc, e.g., the following will render the document with Pandoc 2.9.1: 46 | 47 | ```r 48 | pandoc::with_pandoc_version( 49 | version = '2.9.1', 50 | rmarkdown::render('input.Rmd') 51 | ) 52 | ``` 53 | 54 | The **pandoc** package works by default with its Pandoc binaries installation. See `?pandoc::pandoc_install()` for installation instructions of 2.9.1 in the example and, more generally, the [Get Started](https://cderv.github.io/pandoc/articles/pandoc.html) article. 55 | 56 | 57 | For use with the Knit button in RStudio (See Section \@ref(custom-knit) about Knit button customization.), you can also customize the behavior like this: 58 | 59 | 60 | ````yaml 61 | knit: (function(input, ...) { pandoc::with_pandoc_version("2.9.1", rmarkdown::render(input)) }) 62 | ```` 63 | 64 | `pandoc::with_pandoc_version()` is a wrapper for `rmarkdown::find_pandoc()`, so you could also get inspiration from it to use your own version. See `?pandoc::with_pandoc_version()` for more details. 65 | 66 | ## Install LaTeX (TinyTeX) for PDF reports {#install-latex} 67 | 68 | If you would like to create PDF documents from R Markdown, you will need to have a LaTeX\index{LaTeX} distribution installed. Although there are several traditional options including MiKTeX\index{LaTeX!MiKTeX}, MacTeX, and TeX Live, we recommend that R Markdown users install [TinyTeX.](https://yihui.org/tinytex/) 69 | 70 | TinyTeX\index{LaTeX!TinyTeX} is a custom LaTeX distribution based on TeX Live that is relatively small in size, but functions well in most cases, especially for R users. Installing or running TinyTeX does not require sysadmin privileges.^[Actually, we recommend that you **do not** use your root privilege (i.e., `sudo`) to install TinyTeX on Linux or macOS, if you are the only user of your system.] You can install TinyTeX with the R package **tinytex**\index{LaTeX!tinytex} [@R-tinytex]: 71 | 72 | ```{r eval=!nzchar(tinytex::tinytex_root())} 73 | tinytex::install_tinytex() 74 | # to uninstall TinyTeX, run tinytex::uninstall_tinytex() 75 | ``` 76 | 77 | Please note that "**tinytex**" refers to the R package, and "TinyTeX" refers to the LaTeX distribution. There are two advantages of using TinyTeX: 78 | 79 | 1. TinyTeX is lightweight (compared to other LaTeX distributions), cross-platform, and portable. For example, you can store a copy of TinyTeX on your USB drive or other portable devices, and use it on other computers with the same operating system. 80 | 81 | 1. When R Markdown is converted to PDF, Pandoc converts Markdown to an intermediate LaTeX document first. The R package **tinytex** has provided helper functions to compile LaTeX documents to PDF (the main function is `tinytex::latexmk()`). If you use TinyTeX and certain LaTeX packages are required but not installed, **tinytex** will try to automatically install them for you. It will also try to compile the LaTeX file for a sufficient number of times to make sure all cross-references are resolved. 82 | 83 | If you are interested in the technical details, you may check out the article @tinytex2019 and the FAQ page at https://yihui.org/tinytex/faq/. 84 | 85 | ## Install missing LaTeX packages {#install-latex-pkgs} 86 | 87 | When you compile a document to PDF through LaTeX, you may run into errors like these: 88 | 89 | ```latex 90 | ! LaTeX Error: File `ocgbase.sty' not found. 91 | 92 | !pdfTeX error: pdflatex (file 8r.enc): 93 | cannot open encoding file for reading 94 | 95 | !pdfTeX error: /usr/local/bin/pdflatex (file tcrm0700): 96 | Font tcrm0700 at 600 not found 97 | ``` 98 | 99 | If you are using TinyTeX as introduced in Section \@ref(install-latex), usually you do not need to deal with such errors, since **tinytex** [@R-tinytex] will automatically deal with them, but if you run into such errors anyway, it is still easy to install the missing LaTeX package(s)\index{LaTeX!packages} via `tinytex::parse_install()`\index{tinytex!parse\_install()}. This function takes the path of the LaTeX log file as the input, tries to figure out the missing packages automatically, and installs them if they are found on CTAN (the Comprehensive TEX Archive Network, https://ctan.org). The LaTeX log file typically has the same base name as your input document, and has an extension `.log`. If you cannot find this log file, you can pass the error message to the `text` argument of this function. Both ways should work: 100 | 101 | ```{r, tidy=FALSE, eval=FALSE} 102 | # if the log file is filename.log 103 | tinytex::parse_install("filename.log") 104 | 105 | # or use the `text` argument 106 | tinytex::parse_install( 107 | text = "! LaTeX Error: File `ocgbase.sty' not found." 108 | ) 109 | # it will install the "ocgx2" package 110 | ``` 111 | 112 | If you do not use TinyTeX, the R package **tinytex** can still help you figure out the LaTeX package names from the error log---use the function `tinytex::parse_packages()`\index{tinytex!parse\_packages()}, e.g., 113 | 114 | ```{r, tidy=FALSE, eval=FALSE} 115 | # if the log file is filename.log 116 | tinytex::parse_packages("filename.log") 117 | 118 | # or use the `text` argument 119 | tinytex::parse_packages( 120 | text = "! LaTeX Error: File `ocgbase.sty' not found." 121 | ) 122 | # it should return "ocgx2" 123 | ``` 124 | 125 | Once you know the package names, you can install them with the package manager of your LaTeX distribution. 126 | 127 | If you are using MiKTeX\index{LaTeX!MiKTeX} instead, it can also install missing packages automatically. During the installation of MiKTeX, be sure to check the setting "Always install missing packages on-the-fly." If you have already installed it without this setting, [you can still change it in the MiKTeX Console.](https://github.com/rstudio/rmarkdown/issues/1285#issuecomment-374340175) 128 | -------------------------------------------------------------------------------- /02-overview.Rmd: -------------------------------------------------------------------------------- 1 | # Conceptual Overview {#conceptual-overview} 2 | 3 | The goal of this text is to showcase many tips and tricks for getting the most value from R Markdown. The following chapters demonstrate techniques to write more efficient and succinct code and to customize your output. Before we begin, it may be helpful to learn just a bit more about how R Markdown works, to help you to understand, remember, apply, and "remix" these tricks. In this section, we provide a brief overview of the process of knitting a document and the "key levers to pull" to change the output. This material is not necessary to understand the subsequent chapters (so feel free to skip ahead!), but it may help you to build a richer mental model for how all the pieces fit together. 4 | 5 | ## What happens when we render? {#rmarkdown-process} 6 | 7 | R Markdown combines several different processes together to create documents, and one of the main sources of confusion from R Markdown is how all the components work together.^[Allison Horst has created an amusing artwork that describes the R Markdown process as wizardry: https://github.com/allisonhorst/stats-illustrations/raw/master/rstats-artwork/rmarkdown_wizards.png. As a matter of fact, the cover image of this book was adapted from this artwork.] Fortunately, as a user, it is not essential to understand all the inner workings of these processes to be able to create documents. However, as a user who may be seeking to alter the behavior of a document, it is important to understand which component is responsible for what. This makes it a lot easier to seek help as you can target your searches on the correct area. 8 | 9 | The basic workflow structure for an R Markdown document is shown in Figure \@ref(fig:rmdworkflow), highlighting the steps (arrows) and the intermediate files that are created before producing the output. The whole process is implemented via the function `rmarkdown::render()`. Each stage is explained in further detail below. 10 | 11 | ```{r rmdworkflow, echo = FALSE, fig.cap = "A diagram illustrating how an R Markdown document is converted to the final output document.", out.width='100%'} 12 | knitr::include_graphics("images/workflow.png", dpi = NA) 13 | ``` 14 | 15 | The `.Rmd` document is the original format of the document. It contains a combination of YAML (metadata)\index{YAML}, text (narratives), and code chunks\index{code chunk}. 16 | 17 | First, the `knit()` function in **knitr**\index{knitr} [@R-knitr] is used to execute all code embedded within the `.Rmd` file, and prepare the code output to be displayed within the output document. All these results are converted into the correct markup language to be contained within the temporary `.md` file. 18 | 19 | Then the `.md` file is processed by Pandoc\index{Pandoc}, a multipurpose tool designed to convert files from one markup language to another. It takes any parameters specified within the YAML frontmatter of the document (e.g., `title`, `author`, and `date`) to convert the document to the output format specified in the `output` parameter (such as `html_document` for HTML output). 20 | 21 | If the output format is PDF, there is an additional layer of processing, as Pandoc will convert the intermediate `.md` file into an intermediate `.tex` file. This file is then processed by LaTeX to form the final PDF document. As we mentioned in Section \@ref(install-latex), the **rmarkdown** package calls the `latexmk()` function in the **tinytex** package [@R-tinytex], which in turn calls LaTeX to compile `.tex` to `.pdf`. 22 | 23 | In short, `rmarkdown::render()` = `knitr::knit()` + Pandoc (+ LaTeX for PDF output only). 24 | 25 | Robin Linacre has written a nice summary of the relationship between R Markdown, **knitr**, and Pandoc at https://stackoverflow.com/q/40563479/559676, which contains more technical details than the above overview. 26 | 27 | Note that not all R Markdown documents are eventually compiled through Pandoc. The intermediate `.md` file could be compiled by other Markdown renderers. Below are two examples: 28 | 29 | - The **xaringan**\index{xaringan} package [@R-xaringan] passes the `.md` output to a JavaScript library, which renders the Markdown content in the web browser. 30 | 31 | - The **blogdown**\index{blogdown} package [@R-blogdown] supports the `.Rmarkdown` document format, which is knitted to `.markdown`, and this Markdown document is usually rendered to HTML by an external site generator. 32 | 33 | ## R Markdown anatomy {#rmarkdown-anatomy} 34 | 35 | We can dig one level deeper by considering the different components of an R Markdown. Specifically, let's look at when and how these are altered during the rendering workflow. 36 | 37 | ### YAML metadata 38 | 39 | The YAML metadata\index{YAML} (also called the YAML header) is processed in many stages of the rendering process and can influence the final document in many different ways. It is placed at the very beginning of the document and is read by each of Pandoc, **rmarkdown**, and **knitr**. Along the way, the information that it contains can affect the code, content, and the rendering process. 40 | 41 | A typical YAML header looks like this, and contains basic metadata about the document and rendering instructions: 42 | 43 | ```yaml 44 | --- 45 | title: My R Markdown Report 46 | author: Yihui Xie 47 | output: html_document 48 | --- 49 | ``` 50 | 51 | In this case, the `title` and `author` fields are processed by Pandoc to set the values of template variables. With the default template, the title and author information will appear at the beginning of the resulting document. More details on how Pandoc uses information from the YAML header are included in the Pandoc manual's section on the [YAML metadata block.](https://pandoc.org/MANUAL.html#extension-yaml_metadata_block) 52 | 53 | In contrast, the `output` field is used by **rmarkdown** to apply the output format function `rmarkdown::html_document()` in the rendering process. We can further influence the rendering process by passing arguments to the output format that we are specifying in `output`. For example, writing: 54 | 55 | ```yaml 56 | output: 57 | html_document: 58 | toc: true 59 | toc_float: true 60 | ``` 61 | 62 | is the equivalent of telling `rmarkdown::render()` to apply the output format `rmarkdown::html_document(toc = TRUE, toc_float = TRUE)`. To find out what these options do, and to learn about other possible options, you may run `?rmarkdown::html_document` in your R console and read the help page. Note that `output: html_document` is equivalent to `output: rmarkdown::html_document`. When an output format does not have a qualifier like `rmarkdown::`, it is assumed that it is from the **rmarkdown** package, otherwise it must be prefixed with the R package name, e.g., `bookdown::html_document2`. 63 | 64 | The YAML header can also influence our content and code if we choose to use parameters in YAML, as described in Section \@ref(parameterized-reports). In short, we can include variables and R expressions in this header that can be referenced throughout our R Markdown document. For example, the following header defines `start_date` and `end_date` parameters, which will be reflected in a list called `params` later in the R Markdown document. Thus, if we want to use these values in our R code, we can access them via `params$start_date` and `params$end_date`. 65 | 66 | ```yaml 67 | --- 68 | title: My RMarkdown 69 | author: Yihui Xie 70 | output: html_document 71 | params: 72 | start_date: '2020-01-01' 73 | end_date: '2020-06-01' 74 | --- 75 | ``` 76 | 77 | ### Narrative {#narrative} 78 | 79 | The narrative textual elements of R Markdown may be simpler to understand than the YAML metadata and code chunks. Typically, this will feel quite a bit like writing in a text editor. However, this Markdown content can be more powerful and interesting than simple text---both in how its content is made, and how the document structure is made from it. 80 | 81 | While much of our narrative is human-written, many R Markdown documents will likely wish to reference the code and analysis being used. For this reason, Chapter \@ref(document-elements) demonstrates the many ways that code can help generate parts of the text, such as combining words into a list (Section \@ref(combine-words)) or writing a bibliography (Section \@ref(bibliography)). This conversion is handled by **knitr**\index{knitr} as we convert from `.Rmd` to `.md`. 82 | 83 | Our Markdown text can also provide structure to the document. While we do not have enough space here to review the Markdown syntax,^[Instead, for a review of the Markdown syntax, please see https://bookdown.org/yihui/bookdown/markdown-syntax.html.] one particularly relevant concept is section headers, which are denoted by one or more hashes (`#`) corresponding to different levels, e.g., 84 | 85 | ```md 86 | # First-level header 87 | 88 | ## Second-level header 89 | 90 | ### Third-level header 91 | ``` 92 | 93 | These headers give structure to our entire document as **rmarkdown** converts the `.md` to our final output format. This structure is useful for referencing and formatting these sections by appending certain attributes to them. To create such references, Pandoc syntax allows us to provide a unique identifier by following the header notation with `{#id}`, or attach one or more classes to a section with `{.class-name}`, e.g., 94 | 95 | ```md 96 | ## Second-level header {#introduction .important .large} 97 | ``` 98 | 99 | We can then access this section with many of the tools that you will learn, e.g., by referencing it with its ID or class. As examples, Section \@ref(cross-ref) demonstrates how to use the section ID to make cross-references throughout your document, and Section \@ref(html-tabs) introduces the `.tabset` class to help reorganize subsections. 100 | 101 | The final interesting type of content that we might find in the textual part of our R Markdown is raw content written specifically for our desired output format, e.g., raw LaTeX code for LaTeX output (Section \@ref(raw-latex)), raw HTML code for HTML output, and so on (Section \@ref(raw-content)). Raw content may help you achieve things that cannot be done with plain Markdown, but please keep in mind that it is usually ignored when the output format is a different format, e.g., raw LaTeX commands in Markdown will be ignored when the output format is HTML. 102 | 103 | ### Code chunks 104 | 105 | Code chunks\index{code chunk} are the beating heart of our R Markdown. The code in these chunks is run by **knitr**, and its output is translated to Markdown to dynamically keep our reports in sync with our current scripts and data. Each code chunk consists of a language engine (Chapter \@ref(other-languages)), an optional label, chunk options (Chapter \@ref(chunk-options)), and code. 106 | 107 | To understand some of the modifications that we can make to code chunks, it is worth understanding the **knitr** process in slightly more detail. For each chunk, a **knitr** language engine gets three pieces of input: the knitting environment (`knitr::knit_global()`), the code input, and a list of chunk options. It returns the formatted representations of the code as well as its output. As a side effect, the knitting environment may also be modified, e.g., new variables may have been created in this environment via the source code in the code chunk. This process is illustrated in Figure \@ref(fig:knitr-workflow). 108 | 109 | ```{r knitr-workflow, echo = FALSE, fig.cap = 'A flowchart of inputs and outputs to a language engine.', fig.align='center'} 110 | knitr::include_graphics('images/knitr-workflow.jpg', dpi = NA) 111 | ``` 112 | 113 | 125 | 126 | We can modify this process by: 127 | 128 | - changing our language engine; 129 | 130 | - modifying chunk options, which can be global, local, or engine-specific; 131 | 132 | - and by using hooks (Chapter \@ref(output-hooks) and Chapter \@ref(chunk-hooks)) to further process these inputs and outputs. 133 | 134 | For example, in Section \@ref(hook-hide), you will learn how to add a hook to post-process the code output to redact certain lines in the source code. 135 | 136 | Code chunks also have analogous concepts to the classes and unique identifiers that we explored for narratives in Section \@ref(narrative). A code chunk can specify an optional identifier (often called the "chunk label") immediately after its language engine. It can set classes for code and text output blocks in the output document via the chunk options `class.source` and `class.output`, respectively (see Section \@ref(chunk-styling)). For example, the chunk header ```` ```{r summary-stats, class.output = 'large-text'}```` gives this chunk a label `summary-stats`, and the class `large-text` for the text output blocks. A chunk can have only one label, but can have multiple classes. 137 | 138 | ### Document body 139 | 140 | One important thing to understand when authoring and modifying a document is how code and narrative pieces create different sections, or containers within the document. For example, suppose we have a document that looks like this: 141 | 142 | ````md 143 | # Title 144 | 145 | ## Section X 146 | 147 | This is my introduction. 148 | 149 | ```{r chunk-x}`r ''` 150 | x <- 1 151 | print(x) 152 | ``` 153 | 154 | ### Subsection 1 155 | 156 | Here are some details. 157 | 158 | ### Subsection 2 159 | 160 | These are more details. 161 | 162 | ## Section Y 163 | 164 | This is another section. 165 | 166 | ```{r chunk-y}`r ''` 167 | y <- 2 168 | print(y) 169 | ``` 170 | ```` 171 | 172 | When writing this document, we might think of each piece as linear with independent sections of text and code following in a sequence one after the other. However, what we are actually doing is creating a set of nested containers that conceptually^[In reality, there are many more containers than shown. For example, for a knitted code chunk, the code and output exist in separate containers that share a common parent.] looks more like Figure \@ref(fig:rmd-containers). 173 | 174 | ```{r rmd-containers, echo = FALSE, fig.cap = 'A simple R Markdown document illustrated as a set of nested containers.', out.width = '50%', fig.align='center'} 175 | knitr::include_graphics('images/rmd-containers.jpg', dpi = NA) 176 | ``` 177 | 178 | 188 | 189 | Two key features of this diagram are (1) every section of text or code is its own discrete container, and (2) containers can be nested within one another. This nesting is particularly apparent if you are authoring your R Markdown document in the RStudio IDE and expand the document outline. 190 | 191 | Note that in Figure \@ref(fig:rmd-containers), headers of the same level represent containers at the same level of nesting. Lower-level headers exist inside of the container of higher-level headers. In this case, it is common to call the higher-level sections the "parent" and the minor sections the "child." For example, a subsection is the child of a section. Besides headers, you can also create divisions in your text using `:::`, as demonstrated in Section \@ref(multi-column). 192 | 193 | This structure has important implications as we attempt to apply some of the formatting and styling options that are described in this text. For example, we will see this nested structure when we learn about how Pandoc represents our document as an abstract syntax tree (Section \@ref(lua-filters)), or when we use CSS selectors (Section \@ref(html-css), among others) to style our HTML output. 194 | 195 | Formatting and styling can be applied to either containers of similar types (e.g., all code blocks), or all containers that exist inside of another container (e.g., everything under "Section Y"). Additionally, as explained in Section \@ref(narrative), we can apply the same classes to certain sections to designate them as being similar, and in this case, the common class names denote the common properties or intent of these sections. 196 | 197 | As you read through this cookbook, it may be useful to quiz yourself and think about what sort of container the specific "recipe" is acting upon. 198 | 199 | ## What can we change to change the results? {#what-to-change} 200 | 201 | Let's summarize what we have seen so far and preview what is to come. 202 | 203 | Rendering R Markdown documents with **rmarkdown** consists of converting `.Rmd` to `.md` with **knitr**, and then `.md` to our desired output with Pandoc (typically). 204 | 205 | The `.Rmd`-to-`.md` step handles the execution and "translation" of all code within our report, so most changes to *content* involve editing the `.Rmd` with code for **knitr** to translate. Tools that we have control over these steps include **knitr** chunk options and hooks. 206 | 207 | Our `.md` is a plain text file with no formatting. This is where Pandoc comes in to convert to the final output format such as HTML, PDF, or Word. Along the way, we add structure and style. A wide range of tools to help us in this process include style sheets (CSS), raw LaTeX or HTML code, Pandoc templates, and Lua filters. By understanding the nested structure of an R Markdown document, and by thoughtfully using identifiers and classes, we can apply some of these tools selectively to targeted parts of our output. 208 | 209 | Finally, our YAML metadata may help us toggle any of these steps. Changing parameters can change how our code runs, changing metadata alters the text content, and changing output options provides the `render()` function with a different set of instructions. 210 | 211 | Of course, these are all rough heuristics and should not be taken as absolute facts. Ultimately, there is not a completely clear division of labor. Throughout this book, you will see that there are often multiple valid paths to achieving many of the outcomes described in this book, and these may enter different stages of the pipeline. For example, for the simple task of inserting an image in your document, you may either use the R code `knitr::include_graphics()`, which would execute in the `.Rmd` to `.md` stage, or directly use Markdown syntax (`![]()`). This may seem confusing, and sometimes different approaches will have different advantages. However, do not be concerned---if anything, this often means there are many valid ways to solve your problem, and you can follow whichever approach makes the most sense to you. 212 | 213 | And that's that! In the rest of the book, you can now color in this rough sketch with many more concrete examples of ways to modify all of the components that we have discussed to get the most value out of R Markdown. 214 | -------------------------------------------------------------------------------- /03-basics.Rmd: -------------------------------------------------------------------------------- 1 | # Basics 2 | 3 | In this chapter, we present some key concepts about R Markdown. First, we introduce the basic components of an R Markdown document: the prose and code. Next, we show how to convert R Markdown documents to R scripts, and vice versa. 4 | 5 | For those seeking lower-level basics, please read Chapter 2 of the _R Markdown Definitive Guide_ [@rmarkdown2018]. 6 | 7 | ## Code chunks and inline R code {#r-code} 8 | 9 | An R Markdown document consists of intermingled prose (narratives) and code. There are two types of code in an Rmd document: code chunks\index{code chunk} and inline R code. Below is a quick example: 10 | 11 | ````md 12 | ```{r}`r ''` 13 | x <- 5 # radius of a circle 14 | ``` 15 | 16 | For a circle with the radius `r knitr::inline_expr('x')`, 17 | its area is `r knitr::inline_expr('pi * x^2')`. 18 | ```` 19 | 20 | A code chunk usually starts with ```` ```{}```` and ends with ```` ``` ````. You can write any number of lines of code in it. Inline R code is embedded in the narratives of the document using the syntax `` `r ` ``. In the above example, we defined a variable `x` in a code chunk, which is the radius of a circle, and calculated its area in the next paragraph. 21 | 22 | You can customize the behavior and output of code chunks through chunk options (provided inside the curly brackets `{}`). You will find several examples in Chapter \@ref(chunk-options). You may write code of other languages in code chunks, too (see Chapter \@ref(other-languages)). 23 | 24 | ## Write Markdown in the RStudio visual editor {#rstudio-visual} 25 | 26 | If you are not familiar with Markdown yet, or do not prefer writing Markdown code, RStudio\index{RStudio} v1.4 has included an experimental visual editor for Markdown documents, which feels similar to traditional WYSIWYG editors like Word, as shown in Figure \@ref(fig:visual-edit). You can find the full documentation at https://rstudio.github.io/visual-markdown-editing/. 27 | 28 | ```{r visual-edit, echo=FALSE, fig.cap="The visual Markdown editor in RStudio.", out.width='100%', fig.align='center'} 29 | knitr::include_graphics("images/visual-edit.png", dpi = NA) 30 | ``` 31 | 32 | With the visual editor, you can visually edit almost any Markdown elements supported by Pandoc, such as section headers, figures, tables, footnotes, and so on. That means you do not have remember the syntax for all elements. In case you forget the syntax for a certain element, you may use the RStudio toolbar (see Figure \@ref(fig:visual-edit)) or a keyboard shortcut to add or edit it. 33 | 34 | If you are already a Markdown expert, you can still write your document in the source mode by clicking the rightmost button on the toolbar, which allows you to switch between the source mode and the visual mode. 35 | 36 | ## Render an R script to a report {#spin} 37 | 38 | Even if you are a long-time R Markdown user, you may have missed another possibility. Dean Attali called it ["**knitr**'s best hidden gem".](https://deanattali.com/2015/03/24/knitrs-best-hidden-gem-spin/) That is, you can render a pure R script to a report directly. If you use the RStudio IDE, the keyboard shortcut to render R scripts is the same as when you knit Rmd documents (`Ctrl / Cmd + Shift + K`). Or equivalently, you can call `rmarkdown::render()` on the R script. 39 | 40 | When rendering an R script to a report via `rmarkdown::render()`, the function `knitr::spin()`\index{knitr!spin()} is called under the hood to convert the R script to an Rmd file first. This function is what Dean Attali called **knitr**'s best hidden gem. You will see all text and graphical output in the report. 41 | 42 | If you want granular control over the elements in the report, below are a few syntax rules to help you: 43 | 44 | - Roxygen comments will be treated as normal text. A roxygen comment is an R comment that starts with `#'`. This can help you write narratives in your report. You can use any Markdown syntax in the comments. 45 | 46 | - A comment starting with `#+` is treated as the **knitr** chunk header. For example, `knitr::spin()` will translate the comment `#+ label, fig.width=5` to the chunk header ```` ```{r label, fig.width=5}```` in R Markdown. 47 | 48 | - R code of the form `{{ code }}` is translated to an inline R expression in R Markdown. Please note that `{{ code }}` must be on its own line. 49 | 50 | - The YAML\index{YAML} frontmatter can be written in the beginning of the R script in roxygen comments, too. Please watch out for the indentation in YAML fields. It is very important. If you omit the indentation, the data structure expressed in your YAML will be different and incorrect. For example, the field `keep_tex: true` should be indented for two more spaces under `pdf_document` in the example below. 51 | 52 | - Any text between `/*` and `*/` will be ignored (i.e., they are treated as true comments). 53 | 54 | Below is a full example illustrating the above rules: 55 | 56 | `r import_example('spin.R')` 57 | 58 | When this script is rendered to a report, `knitr::spin()` will convert it to R Markdown: 59 | 60 | ```{r, class.output='md', comment='', echo=FALSE} 61 | xfun::raw_string( 62 | knitr::spin(text = xfun::read_utf8('examples/spin.R'), knit = FALSE) 63 | ) 64 | ``` 65 | 66 | This method of generating reports can be particularly useful when you primarily work with R scripts and do not need a lot of narratives. If the proportion of text is substantial in your report, R Markdown may be a better choice, because you do not need to put all text in roxygen comments. 67 | 68 | ## Convert R Markdown to R script {#purl} 69 | 70 | When you want to extract all R code from an R Markdown document, you can call the function `knitr::purl()`\index{knitr!purl()}. Below is a simple Rmd example with the filename `purl.Rmd`: 71 | 72 | `r import_example('purl.Rmd')` 73 | 74 | If we call `knitr::purl("purl.Rmd")`, it generates the following R script (with the filename `purl.R` by default): 75 | 76 | ```{r, class.output='r', comment='', echo=FALSE} 77 | purl2 = function(file, ...) { 78 | xfun::raw_string(xfun::Rscript_call( 79 | knitr::purl, list(text = xfun::read_utf8(file), quiet = TRUE, ...) 80 | )) 81 | } 82 | purl2('examples/purl.Rmd') 83 | ``` 84 | 85 | The above R script contains the chunk options in a comment. If you want pure R code, you may call `knitr::purl()` with the argument `documentation = 0`, which will generate the R script below: 86 | 87 | ```{r, class.output='r', comment='', echo=FALSE} 88 | purl2('examples/purl.Rmd', documentation = 0) 89 | ``` 90 | 91 | If you want to retain all the text, you may use the argument `documentation = 2`, which generates the R script below: 92 | 93 | ```{r, class.output='r', comment='', echo=FALSE} 94 | purl2('examples/purl.Rmd', documentation = 2) 95 | ``` 96 | 97 | Note that code chunks with the option `purl = FALSE` will be excluded in the R script. 98 | 99 | Inline R expressions are ignored by default. If you want to include them in the R script, you need to set the global R option `options(knitr.purl.inline = TRUE)` before calling `knitr::purl()`. 100 | 101 | ## R Markdown Notebooks {#notebook} 102 | 103 | As mentioned in [Section 2.2](https://bookdown.org/yihui/rmarkdown/compile.html) of the _R Markdown Definitive Guide_ [@rmarkdown2018], there are several ways to compile an Rmd document. One of them is to use R Markdown Notebooks, with the output format `html_notebook`, e.g., 104 | 105 | ```yaml 106 | --- 107 | title: An R Markdown Notebook 108 | output: html_notebook 109 | --- 110 | ``` 111 | 112 | When you use this output format in RStudio, the `Knit` button on the toolbar will become the `Preview` button. 113 | 114 | The main advantage of using notebooks\index{RStudio!notebook} is that you can work on an Rmd document _iteratively in the same R session_. You can run one code chunk at a time by clicking the green arrow button on each chunk, and you will see the text or plot output in the editor. When you click the `Preview` button on the toolbar, it only renders the Rmd document to an HTML output document containing the output of all code chunks that you have already executed. The `Preview` button does not execute any code chunks. By comparison, when you use other output formats and hit the `Knit` button, RStudio launches a new R session to compile the whole document (hence all code chunks are executed at once), which usually takes more time. 115 | 116 | If you do not like RStudio's default behavior of showing the output of code chunks inline when you run them individually, you can uncheck the option "Show output inline for all R Markdown documents" from the menu `Tools -> Global Options -> R Markdown`. After that, when you run a code chunk, the output will be shown in the R console instead of inside the source editor. You can also set this option for an individual Rmd document in its YAML metadata: 117 | 118 | ```yaml 119 | editor_options: 120 | chunk_output_type: console 121 | ``` 122 | -------------------------------------------------------------------------------- /08-word.Rmd: -------------------------------------------------------------------------------- 1 | # Word 2 | 3 | To generate a Word document from R Markdown, you can use the output format `word_document`. If you want to include cross-references in the document, you may consider the output format `bookdown::word_document2`, as mentioned in Section \@ref(cross-ref). 4 | 5 | ```yaml 6 | --- 7 | output: 8 | word_document: default 9 | bookdown::word_document2: default # for cross-references 10 | --- 11 | ``` 12 | 13 | From our experience, the most frequently asked questions about Word output are: 14 | 15 | 1. How can I apply a custom Word template to the document? 16 | 17 | 1. How can I incorporate changes made in Word in the original R Markdown document? 18 | 19 | 1. How can I style individual document elements? 20 | 21 | We will address these questions in this chapter. 22 | 23 | ## Custom Word templates {#word-template} 24 | 25 | You can apply the styles defined in a Word template\index{template!Word} document to new Word documents generated from R Markdown. Such a template document is also called a "style reference document." The key is that you have to create this template document from Pandoc first, and change the style definitions in it later. Then pass the path of this template to the `reference_docx` option of `word_document`\index{output option!reference\_docx}, e.g., 26 | 27 | ```yaml 28 | --- 29 | output: 30 | word_document: 31 | reference_docx: "template.docx" 32 | --- 33 | ``` 34 | 35 | As we just mentioned, the document `template.docx` has to be generated from Pandoc. You can create this template from an arbitrary R Markdown document with the `word_document` output format (the actual content of this document does not matter, but it should contain the type of elements of which you want to style). Then open the `.docx` file, and edit the styles. 36 | 37 | ```{r, word-template-1, echo=FALSE, fig.cap='Find the styles of a specific document element.', out.width='100%'} 38 | knitr::include_graphics('images/word-template-1.png', dpi = NA) 39 | ``` 40 | 41 | Figure \@ref(fig:word-template-1) shows that you can open the "Styles" window from the "HOME" tab in Word. When you move the cursor to a specific element in the document, an item in the styles list will be highlighted. If you want to modify the style of any type of element, you can click the drop-down menu on the highlighted item, and you will see a dialog box like Figure \@ref(fig:word-template-2). 42 | 43 | ```{r, word-template-2, echo=FALSE, fig.cap='Modify the styles of an element in a Word document.', out.width='100%'} 44 | knitr::include_graphics('images/word-template-2.png', dpi = NA) 45 | ``` 46 | 47 | After you finish modifying the styles, you can save the document (with a filename that will not be accidentally overwritten), and use it as the template for future Word documents. When Pandoc renders a new Word document with a reference document (template), it will read the styles in the template and apply them to the new document. 48 | 49 | You may watch a short video at https://vimeo.com/110804387, or read the article at https://rmarkdown.rstudio.com/articles_docx.html for more detailed instructions on how to create a Word template with custom styles. 50 | 51 | Sometimes it may not be straightforward to find the style name for an element. There may be multiple styles applied to the same element, and you will only see one of them highlighted in the list of styles. It may require some guesswork and online searching to figure out the actual style that you want to modify. For example, you have to click the "Manage Styles" button (the third button from left to right at the bottom of the style list in Figure \@ref(fig:word-template-1)), and scroll through a large number of style names before you find the "Table" style (see Figure \@ref(fig:word-table)). Then you can modify this style for your tables (e.g., add borders). 52 | 53 | ```{r, word-table, echo=FALSE, fig.cap='Modify the styles of tables in a Word document.', out.width='100%'} 54 | knitr::include_graphics('images/word-table.png', dpi = NA) 55 | ``` 56 | 57 | ## The two-way workflow between R Markdown and Word {#word-redoc} 58 | 59 | While it is easy to generate a Word document from R Markdown\index{Word!port to and from Rmd}, things can be particularly painful when someone else edits the Word document and you have to manually port the changes back to the original R Markdown document. Luckily, Noam Ross has provided a promising solution to this problem. The **redoc** package\index{R package!redoc} (https://github.com/noamross/redoc) allows you to generate a Word document, revise the Word document, and convert the revised Word document back to R Markdown. Please note that as of this writing (June 2020), the **redoc** package is still experimental, and more unfortunately, its author has suspended the development. Anyway, if you want to try it out, you can install the package from GitHub: 60 | 61 | ```r 62 | remotes::install_github("noamross/redoc") 63 | ``` 64 | 65 | Once the package is installed, you may use the output format `redoc::redoc`: 66 | 67 | ```yaml 68 | --- 69 | output: redoc::redoc 70 | --- 71 | ``` 72 | 73 | This output format generates a Word document that actually stores the original Rmd document, so the Word document can be converted back to Rmd. Tracked changes in Word will be converted to text written with the CriticMarkup syntax\index{CriticMarkup} (http://criticmarkup.com). For example, `{++ important ++}` represents the insertion of the word "important" in the text. 74 | 75 | You can convert the Word document generated by `redoc::redoc` to Rmd via the function `redoc::dedoc()`, e.g., `redoc::dedoc("file.docx")` will generate `file.Rmd`. In this process, you can decide how to deal with tracked changes in Word via the `track_changes` argument, e.g., you may accept or reject changes, or convert tracked changes to CriticMarkup. We recommend that you use `track_changes = 'criticmarkup'` to avoid the permanent loss of tracked changes. 76 | 77 | When editing the Word document, you are expected to edit the parts that are _not_ automatically generated by code chunks or inline R expressions in R Markdown. For example, you must not edit a table if it is automatically generated by `knitr::kable()` in a code chunk, because such changes will be lost when you convert Word to Rmd via `dedoc()`. To avoid accidentally editing the automatic results from code chunks, you may set the option `highlight_outputs` to `true` in the `redoc::redoc` format, which means the automatic output will be highlighted in Word (with a background color). You should tell your collaborator that they should not touch these highlighted parts in the Word document. 78 | 79 | Again, the **redoc** package is still experimental and its future is unclear at the moment, so the introduction here is intentionally brief. When in doubt, we recommend that you read its documentation on GitHub. 80 | 81 | ## Style individual elements {#word-officedown} 82 | 83 | Due to the simplicity of Markdown, you can apply some global styles to the Word document (see Section \@ref(word-template)), but it is not straightforward to style individual elements, such as changing the color of a word, or centering a paragraph. 84 | 85 | Continuing his effort to make it easier to work with Office documents in R, David Gohel started to develop the **officedown** package\index{R package!officedown} [@R-officedown] in 2018, which aims to bring some **officer**\index{R package!officer} [@R-officer] features into R Markdown. As of this writing, this package is still experimental, although its initial version has been published on CRAN. You may either install it from CRAN or GitHub: 86 | 87 | ```r 88 | # install from CRAN 89 | install.packages("officedown") 90 | 91 | # or GitHub 92 | remotes::install_github("davidgohel/officedown") 93 | ``` 94 | 95 | After the package is installed, you need to load it in your R Markdown document, e.g., 96 | 97 | ````md 98 | ```{r, setup, include=FALSE}`r ''` 99 | library(officedown) 100 | ``` 101 | ```` 102 | 103 | There is an output format `rdocx_document` in the **officedown** package, which is based on `rmarkdown::word_document` by default, and has several other features such as styling tables and plots. 104 | 105 | The **officedown** package allows you to style specific Word elements via the **officer** package. For example, you can create a style via the function `officer::fp_text()`, and apply the style to a piece of text via `ftext()` an inline R expression: 106 | 107 | `r import_example('officer.Rmd')` 108 | 109 | Besides functions in **officer**, **officedown** also allows you to use some special HTML comments to perform **officer** tasks. For example, the function `officer::block_pour_docx()` can be used to import an external Word document\index{Word!import external} into the current document, and alternatively, you can use the HTML comment in R Markdown: 110 | 111 | ```html 112 | 113 | ``` 114 | 115 | That is equivalent to the inline R expression: 116 | 117 | ```md 118 | `r knitr::inline_expr("block_pour_docx(file = 'my-file.docx')")` 119 | ``` 120 | 121 | Other things you may do with **officedown** and **officer** include the following: 122 | 123 | - Insert page breaks. 124 | 125 | - Put content in a multi-column layout. 126 | 127 | - Change paragraph settings. 128 | 129 | - Insert a table of contents. 130 | 131 | - Change the orientation of a section (landscape or portrait). 132 | 133 | To learn more about **officedown**, please check out its documentation at https://davidgohel.github.io/officedown/. 134 | -------------------------------------------------------------------------------- /09-multiformat.Rmd: -------------------------------------------------------------------------------- 1 | # Multiple Output Formats {#multi-formats} 2 | 3 | One main advantage of R Markdown is that it can create multiple output formats from a single source, which could be one or multiple Rmd documents. For example, this book was written in R Markdown, and compiled to two formats: PDF for printing, and HTML for the online version. 4 | 5 | Sometimes it can be challenging to make an output element of a code chunk work for all output formats. For example, it is extremely simple to create a rounded and circular image in HTML output with a single CSS rule (`img { border-radius: 50%; }`), but not so straightforward in LaTeX output (typically it will involve TikZ graphics). 6 | 7 | Sometimes it is just impossible for an output element to work for all output formats. For example, you can easily create a GIF animation with the **gifski** package [@R-gifski] (see Section \@ref(animation)), and it will work perfectly for HTML output, but embedding such an animation in LaTeX output is not possible without extra steps of processing the GIF file and using extra LaTeX packages. 8 | 9 | This chapter provides a few examples that can work for multiple formats. If a certain feature is only available to a specific output format, we will show you how to conditionally enable or disable it based on the output format. 10 | 11 | ## LaTeX or HTML output {#latex-html} 12 | 13 | LaTeX and HTML are two commonly used output formats. The function `knitr::is_latex_output()`\index{knitr!is\_latex\_output()} tells you if the output format is LaTeX (including Pandoc output formats `latex` and `beamer`). Similarly, the function `knitr::is_html_output`\index{knitr!is\_html\_output()} tells you if the output format is HTML. By default, these Pandoc output formats are considered HTML formats: `markdown`, `epub`, `html`, `html4`, `html5`, `revealjs`, `s5`, `slideous`, and `slidy`. If you do not think a certain Pandoc format is HTML, you may use the `excludes` argument to exclude it, e.g., 14 | 15 | ```{r, collapse=TRUE} 16 | # do not treat markdown as an HTML format 17 | knitr::is_html_output(excludes = 'markdown') 18 | ``` 19 | 20 | If a certain output element can only be generated in LaTeX or HTML, you can use these functions to conditionally generate it. For example, when a table is too big on a PDF page, you may include the table in an environment of a smaller font size, but such a LaTeX environment certainly will not work for HTML output, so it should not be included in HTML output (if you want to tweak the font size for HTML output, you may use CSS). Below is a full example: 21 | 22 | `r import_example('latex-tiny.Rmd')` 23 | 24 | The key in the above example is the chunk option `include = knitr::is_latex_output()`\index{chunk option!include}. That is, the environment `\begin{tiny} \end{tiny}` is only included when the output format is LaTeX. The two tables in the example will look identical when the output format is not LaTeX. 25 | 26 | In Section \@ref(font-color), we used these functions to change the text color for HTML and LaTeX output. In Section \@ref(animation), we showed an animation example, which also used this trick. The code chunk that generated the animation for HTML output and static images for LaTeX output is like this: 27 | 28 | ````md 29 | ```{r animation.hook=if (knitr::is_html_output()) 'gifski'}`r ''` 30 | for (i in 1:2) { 31 | pie(c(i %% 2, 6), col = c('red', 'yellow'), labels = NA) 32 | } 33 | ``` 34 | ```` 35 | 36 | These conditional functions can be used anywhere. You can use them in other chunk options (e.g., `eval` for conditional evaluation of the chunk), or in your R code, e.g., 37 | 38 | ````md 39 | ```{r, eval=knitr::is_html_output(), echo=FALSE}`r ''` 40 | cat('You will only see me in HTML output.') 41 | ``` 42 | 43 | ```{r}`r ''` 44 | if (knitr::is_latex_output()) { 45 | knitr::asis_output('\n\n\\begin{tiny}') 46 | } 47 | ``` 48 | ```` 49 | 50 | ## Display HTML widgets {#html-widgets} 51 | 52 | HTML widgets ()\index{HTML!widgets} are typically interactive JavaScript applications, which only work in HTML output. If you knit an Rmd document containing HTML widgets to a non-HTML format such as PDF or Word, you may get an error message like this: 53 | 54 | ```md 55 | Error: Functions that produce HTML output found in document 56 | targeting X output. Please change the output type of this 57 | document to HTML. Alternatively, you can allow HTML output in 58 | non-HTML formats by adding this option to the YAML front-matter 59 | of your rmarkdown file: 60 | 61 | always_allow_html: yes 62 | 63 | Note however that the HTML output will not be visible in 64 | non-HTML formats. 65 | ``` 66 | 67 | There is actually a better solution than the one mentioned in the above error message, but it involves extra packages. You can install the **webshot2** package [@R-webshot2]\index{R package!webshot2} in R and also install Google Chrome or any other Chromium-based browser if you do not have such a browser installed: 68 | 69 | ```{r, eval=FALSE} 70 | install.packages('webshot2') 71 | ``` 72 | 73 | Then if you knit an Rmd document with HTML widgets to a non-HTML format, the HTML widgets will be displayed as static screenshots. The screenshots are automatically taken in **knitr**. [Section 2.10](https://bookdown.org/yihui/bookdown/html-widgets.html) of the **bookdown** book contains more information on finer control over the screenshots. 74 | 75 | ## Embed a web page {#include-url} 76 | 77 | If you have the **webshot2** package [@R-webshot2]\index{R package!webshot} and Chrome installed (see Section \@ref(html-widgets)), you can embed any web page in the output document through `knitr::include_url()`\index{knitr!include\_url()}. When you pass a URL of a web page to this function in a code chunk, it will generate an ` 109 | ``` 110 | ```` 111 | 112 | The attribute name is the Pandoc output format name. If you want to know the output format name, you may check the output of the code chunk below inside an Rmd document\index{knitr!pandoc\_toc()}: 113 | 114 | ````md 115 | ```{r}`r ''` 116 | knitr::pandoc_to() 117 | ``` 118 | ```` 119 | 120 | Please note that raw content is only visible to a specific output format. For example, raw LaTeX content will be ignored when the output format is HTML. 121 | 122 | ## Custom blocks (\*) {#custom-blocks} 123 | 124 | 125 | [Section 2.7](https://bookdown.org/yihui/bookdown/custom-blocks.html) of the **bookdown** book mentioned how we can use custom blocks in R Markdown to customize the appearance of blocks of content. This can be a useful way to make some content stand out from your report or book, to make sure that your readers take away the key points from your work. Examples of how these blocks could be used include: 126 | 127 | - display a warning message to make sure users are using up-to-date packages before running your analysis; 128 | 129 | - add a link at the beginning of your document to your GitHub repository containing the source; 130 | 131 | - highlight key results and findings from your analysis. 132 | 133 | In this section, we will explain how to create your own custom blocks for both PDF and HTML output. They can both use the same formatting syntax in the R Markdown document, but require different configurations. 134 | 135 | ### Syntax {#block-syntax} 136 | 137 | The syntax for custom blocks is based on Pandoc's [fenced `Div` blocks.](https://pandoc.org/MANUAL.html#divs-and-spans) `Div` blocks\index{Div} are very powerful, but there is a problem at the moment: they mainly work for HTML output and do not work for LaTeX output. 138 | 139 | Since version 1.16 of the **rmarkdown** package, it has been possible to convert `Div` blocks to both HTML and LaTeX. For HTML output, all attributes of the block will become attributes of the `
` tag. For example, a `Div` can have an ID (after `#`), one or multiple classes (class names are written after `.`), and other attributes. The following `Div` block 140 | 141 | ```md 142 | ::: {#hello .greeting .message style="color: red;"} 143 | Hello **world**! 144 | ::: 145 | ``` 146 | 147 | will be converted to the HTML code below: 148 | 149 | ```html 150 |
151 | Hello world! 152 |
153 | ``` 154 | 155 | For LaTeX output, the first class name will be used as the LaTeX environment name. You should also provide an attribute named `data-latex`\index{Div!LaTeX compatability} in the `Div` block, which will be the arguments of the environment. This attribute can be an empty string if the environment does not need arguments. We show two simple examples below. The first example uses the `verbatim` environment in LaTeX, which does not have any arguments: 156 | 157 | ````md 158 | ::: {.verbatim data-latex=""} 159 | We show some _verbatim_ text here. 160 | ::: 161 | ```` 162 | 163 | Its LaTeX output will be: 164 | 165 | ```tex 166 | \begin{verbatim} 167 | We show some \emph{verbatim} text here. 168 | \end{verbatim} 169 | ``` 170 | 171 | When the block is converted to HTML, the HTML code will be: 172 | 173 | ```html 174 |
175 | We show some verbatim text here. 176 |
177 | ``` 178 | 179 | The second example uses the `center` and `minipage` environments to display some text in a centered box of half of the page width. 180 | 181 | ```md 182 | :::: {.center data-latex=""} 183 | 184 | ::: {.minipage data-latex="{.5\linewidth}"} 185 | This paragraph will be centered on the page, and 186 | its width is 50% of the width of its parent element. 187 | ::: 188 | 189 | :::: 190 | ``` 191 | 192 | Note that we nested the `minipage` block in the `center` block. You need more colons for a parent block to include a child block. In the above example, we used four colons (you can use five or more) for the `center` block. The two blocks will be converted to the LaTeX code below: 193 | 194 | ```tex 195 | \begin{center} 196 | \begin{minipage}{.5\linewidth} 197 | This paragraph will be centered on the page, and 198 | its width is 50\% of the width of its parent element. 199 | \end{minipage} 200 | \end{center} 201 | ``` 202 | 203 | It is up to the user to define the appearance of their `
` blocks via CSS for the HTML output. Similarly, for LaTeX output, you may use the command `\newenvironment` to define the environment if it has not been defined, or `\renewenvironment` to redefine an existing environment in LaTeX. In the LaTeX definitions, you can decide on the appearance of these blocks in PDF. These customizations will normally be contained in their own files such as `style.css` or `preamble.tex`, and then included within the YAML options: 204 | 205 | ```yaml 206 | --- 207 | output: 208 | html_document: 209 | css: style.css 210 | pdf_document: 211 | includes: 212 | in_header: preamble.tex 213 | --- 214 | ``` 215 | 216 | Next we will demonstrate a few more advanced custom blocks that use custom CSS rules and LaTeX environments. You may find an additional example in Section \@ref(multi-column), in which we arranged multiple blocks in a multi-column layout. 217 | 218 | ### Adding a shaded box {#block-shaded} 219 | 220 | First, we show how to include content in a shaded box. The box has a black background with an orange frame with rounded corners. The text in the box is in white. 221 | 222 | For HTML output, we define these rules in a CSS file. If you are unfamiliar with CSS\index{CSS}, there are plenty of free online tutorials, e.g., https://www.w3schools.com/css/. 223 | 224 | ```{embed, file = 'css/box.css'} 225 | ``` 226 | 227 | For LaTeX output, we create a new environment named `blackbox` and based on the LaTeX package **framed**\index{LaTeX package!framed}, with a black background and white text: 228 | 229 | ```{cat, class.source='latex', engine.opts=list(file = 'latex/blackbox.tex')} 230 | \usepackage{color} 231 | \usepackage{framed} 232 | \setlength{\fboxsep}{.8em} 233 | 234 | \newenvironment{blackbox}{ 235 | \definecolor{shadecolor}{rgb}{0, 0, 0} % black 236 | \color{white} 237 | \begin{shaded}} 238 | {\end{shaded}} 239 | ``` 240 | 241 | We used the **framed** package in this book because it is fairly lightweight, but it is not possible to draw a colored frame with rounded corners with this package. To achieve the latter, you will need more sophisticated LaTeX packages such as **tcolorbox** ()\index{LaTeX package!tcolorbox}, which offers a set of very flexible options for creating shaded boxes. You can find many examples in its documentation. The LaTeX environment below will create a shaded box of similar appearance to the above CSS example: 242 | 243 | ```tex 244 | \usepackage{tcolorbox} 245 | 246 | \newtcolorbox{blackbox}{ 247 | colback=black, 248 | colframe=orange, 249 | coltext=white, 250 | boxsep=5pt, 251 | arc=4pt} 252 | ``` 253 | 254 | Now we can use our custom box in both PDF and HTML output formats. The source code of the box is: 255 | 256 | ```md 257 | :::: {.blackbox data-latex=""} 258 | ::: {.center data-latex=""} 259 | **NOTICE!** 260 | ::: 261 | 262 | Thank you for noticing this **new notice**! Your noticing it has 263 | been noted, and _will be reported to the authorities_! 264 | :::: 265 | ``` 266 | 267 | The output is: 268 | 269 | :::: {.blackbox data-latex=""} 270 | ::: {.center data-latex=""} 271 | **NOTICE!** 272 | ::: 273 | 274 | Thank you for noticing this **new notice**! Your noticing it has 275 | been noted, and _will be reported to the authorities_! 276 | :::: 277 | 278 | ### Including icons {#block-image} 279 | 280 | We can make custom blocks even more visually appealing by including images in them. Images can also be an effective way to convey the content of the block. For the following example, we assume that we are working within a directory structure below, which is a simplified version of what is used to build this book: 281 | 282 | ```text 283 | directory/ 284 | ├── your-report.Rmd 285 | ├── style.css 286 | ├── preamble.tex 287 | └── images/ 288 | └── ├── important.png 289 | ├── note.png 290 | └── caution.png 291 | ``` 292 | 293 | We show the source code and output of the example before we explain how everything works: 294 | 295 | ```md 296 | ::: {.infobox .caution data-latex="{caution}"} 297 | **NOTICE!** 298 | 299 | Thank you for noticing this **new notice**! Your noticing it has 300 | been noted, and _will be reported to the authorities_! 301 | ::: 302 | ``` 303 | 304 | The output is: 305 | 306 | ::: {.infobox .caution data-latex="{caution}"} 307 | **NOTICE!** 308 | 309 | Thank you for noticing this **new notice**! Your noticing it has 310 | been noted, and _will be reported to the authorities_! 311 | ::: 312 | 313 | For the HTML output, we can add an image to the box through the `background-image` property in CSS\index{CSS property!background-image}. We insert the image into the background, and add enough padding on the left-hand side to avoid the text overlapping with this image. If you are using local images, the file path to the images is provided relative to the CSS file. For example: 314 | 315 | ```css 316 | .infobox { 317 | padding: 1em 1em 1em 4em; 318 | margin-bottom: 10px; 319 | border: 2px solid orange; 320 | border-radius: 10px; 321 | background: #f5f5f5 5px center/3em no-repeat; 322 | } 323 | 324 | .caution { 325 | background-image: url("images/caution.png"); 326 | } 327 | ``` 328 | 329 | Note that we used two class names, `.infobox` and `.caution`, on the outer block. The `infobox` class will be used to define the shaded box with a colored border, and the `caution` class will be used to include the image. The advantage of using two classes is that we can define more blocks with different icons without repeating the setup of the shaded box. For example, if we need a `warning` box, we only need to define the following CSS rule without repeating rules in `.infobox`: 330 | 331 | ```css 332 | .warning { 333 | background-image: url("images/warning.png"); 334 | } 335 | ``` 336 | 337 | Then you can create a `warning` box with the Markdown source code below: 338 | 339 | ```md 340 | :::: {.infobox .warning data-latex="warning"} 341 | 342 | Include the actual content here. 343 | 344 | :::: 345 | ``` 346 | 347 | For the PDF output, we can create an `infobox` environment based on the `blackbox` environment defined in the previous example, and add the icon to the left side of the box. There are multiple ways of including images in a LaTeX environment. Here is only one of them (it does not precisely reproduce the box style defined in the CSS above): 348 | 349 | ```{cat, class.source='tex', engine.opts=list(file = 'latex/infobox.tex')} 350 | \newenvironment{infobox}[1] 351 | { 352 | \begin{itemize} 353 | \renewcommand{\labelitemi}{ 354 | \raisebox{-.7\height}[0pt][0pt]{ 355 | {\setkeys{Gin}{width=3em,keepaspectratio} 356 | \includegraphics{images/#1}} 357 | } 358 | } 359 | \setlength{\fboxsep}{1em} 360 | \begin{blackbox} 361 | \item 362 | } 363 | { 364 | \end{blackbox} 365 | \end{itemize} 366 | } 367 | ``` 368 | 369 | Below we show more example blocks with different icons: 370 | 371 | ::: {.infobox .warning data-latex="{warning}"} 372 | **NOTICE!** 373 | 374 | Thank you for noticing this **new notice**! Your noticing it has 375 | been noted, and _will be reported to the authorities_! 376 | ::: 377 | 378 | ::: {.infobox .note data-latex="{note}"} 379 | **NOTICE!** 380 | 381 | Thank you for noticing this **new notice**! Your noticing it has 382 | been noted, and _will be reported to the authorities_! 383 | ::: 384 | 385 | ::: {.infobox .important data-latex="{important}"} 386 | **NOTICE!** 387 | 388 | Thank you for noticing this **new notice**! Your noticing it has 389 | been noted, and _will be reported to the authorities_! 390 | ::: 391 | 392 | ::: {.infobox .tip data-latex="{tip}"} 393 | **NOTICE!** 394 | 395 | Thank you for noticing this **new notice**! Your noticing it has 396 | been noted, and _will be reported to the authorities_! 397 | ::: 398 | 399 | Alternatively, you may use the LaTeX package [**awesomebox**](https://ctan.org/pkg/awesomebox)\index{LaTeX package!awesomebox} to generate boxes with icons in the PDF output. This package gives you a much larger number of icons to choose from. We give a brief example below: please refer to the package documentation for the possible LaTeX environments and their arguments. 400 | 401 | `r import_example('awesomebox.Rmd')` 402 | -------------------------------------------------------------------------------- /12-output-hooks.Rmd: -------------------------------------------------------------------------------- 1 | # Output Hooks (\*) {#output-hooks} 2 | 3 | With the **knitr** package, you have control over every piece of output from your code chunks, such as source code, text output, messages, and plots. The control is achieved through "output hooks."\index{output hooks} Output hooks are a series of functions that take a piece of output as the input (typically a character vector), and return a character vector to be written to the output document. This may not be easy to understand for now, but hopefully you can see the idea more clearly with a small example below explaining how the output of a simple code chunk is rendered through **knitr**'s output hooks. 4 | 5 | Consider this code chunk with one line of code: 6 | 7 | ````md 8 | ```{r}`r ''` 9 | 1 + 1 10 | ``` 11 | ```` 12 | 13 | After **knitr** evaluates the code chunk, it gets two output elements, and both are stored as character strings: the source code `"1 + 1"`, and the text output `"[1] 2"`. These character strings will be further processed by chunk hooks for the desired output format. For example, for Markdown documents, **knitr** will wrap the source code in a fenced code block with a language name. This is done through the `source` hook, which more or less looks like this function: 14 | 15 | ```{r, eval=FALSE} 16 | # for the above case, `x` is a character string "1 + 1" 17 | function(x, options) { 18 | # the little "r" here indicates the language name 19 | paste(c('```r', x, '```'), collapse = '\n') 20 | } 21 | ``` 22 | 23 | Similarly, the text output is processed by the `output` hook that looks like this function: 24 | 25 | ```{r, eval=FALSE} 26 | function(x, options) { 27 | paste(c('```', x, '```'), collapse = '\n') 28 | } 29 | ``` 30 | 31 | So the final output of the above code chunk is: 32 | 33 | ````md 34 | ```r 35 | 1 + 1 36 | ``` 37 | 38 | ``` 39 | [1] 2 40 | ``` 41 | ```` 42 | 43 | The actual hooks are more complicated than the two functions above, but the idea is the same. You may obtain the actual hooks from the object `knit_hooks`\index{knitr!knit\_hooks} via the `get()` method, e.g., 44 | 45 | ```{r, eval=FALSE} 46 | # for meaningful output, the code below should be executed 47 | # *inside* a code chunk of a knitr document 48 | knitr::knit_hooks$get('source') 49 | knitr::knit_hooks$get('output') 50 | # or knitr::knit_hooks$get(c('source', 'output')) 51 | ``` 52 | 53 | Unless you are truly interested in making contributions to the **knitr** package, we do not recommend that you read the source code of these built-in hooks. If you are interested, this code can be found in the scripts named in the form `hooks-*.R` at https://github.com/yihui/knitr/tree/master/R (e.g., `hooks-md.R` contains hooks for R Markdown documents). As a **knitr** user, it usually suffices if you know how to create custom output hooks by taking advantage of the built-in hooks. You will learn that in several examples in this chapter, and we show the basic idea below. 54 | 55 | A custom output hook is registered through the `set()` method of `knit_hooks`. Because this method will override the existing default hook, we recommend that you save a copy of an existing hook, process the output elements in your own way, and pass the results to the default hook. The usual syntax is: 56 | 57 | ```{r, eval=FALSE} 58 | # using local() is optional here (we just want to avoid creating unnecessary global variables like `hook_old`) 59 | local({ 60 | hook_old = knitr::knit_hooks$get('NAME') # save the old hook 61 | knitr::knit_hooks$set(NAME = function(x, options) { 62 | # now do whatever you want to do with x, and pass the 63 | # new x to the old hook 64 | hook_old(x, options) 65 | }) 66 | }) 67 | ``` 68 | 69 | Here, `NAME` is the name of the hook, which can be one of the following values: 70 | 71 | - `source`: processing the source code. 72 | 73 | - `output`: processing text output. 74 | 75 | - `warning`: processing warnings (usually from `warning()`). 76 | 77 | - `message`: processing messages (usually from `message()`). 78 | 79 | - `error`: processing error messages (usually from `stop()`). 80 | 81 | - `plot`: processing plot file paths. 82 | 83 | - `inline`: processing output from inline R expressions. 84 | 85 | - `chunk`: processing output from the whole chunk. 86 | 87 | - `document`: processing the whole document. 88 | 89 | The meaning of the argument `x` in the hook functions is explained in the above list. For the `options` argument of a hook, it denotes the chunk options (as a list) for the current code chunk. For example, if you set `foo = TRUE` on a chunk, you can obtain its value via `options$foo` in the hook. The `options` argument is not available to the `inline` and `document` hooks. 90 | 91 | Output hooks give you the ultimate control over every single piece of your chunk and document output. Compared with chunk options, which often have predefined purposes, output hooks can be much more powerful since they are user-defined functions, and you can do anything you want in functions. 92 | 93 | ## Redact source code {#hook-hide} 94 | 95 | Sometimes we may not want to fully display our source code in the report. For example, you may have a password in a certain line of code. We mentioned in Section \@ref(hide-one) that you can use the chunk option `echo` to select which expressions in the R code to display (e.g., show the second expression via `echo = 2`). In this section, we provide a more flexible method that does not require you to specify the indices of expressions. 96 | 97 | The basic idea is that you add a special comment to the code (e.g., `# SECRET!!`). When this comment is detected in a line of code, you omit that line. Below is a full example using the `source` hook: 98 | 99 | `r import_example('hook-secret.Rmd')` 100 | 101 | The key part in the above `source` hook is this line, which matches the trailing comment `# SECRET!!` in the source code vector `x` via `grepl()` and exclude the matches: 102 | 103 | ```{r, eval=FALSE} 104 | x <- x[!grepl('# SECRET!!$', x)] 105 | ``` 106 | 107 | Precisely speaking, the above hook will exclude whole _expressions_ containing the trailing comment `# SECRET!!`, instead of individual lines, because `x` is actually a vector of R expressions. For example, for the code chunk below: 108 | 109 | ```{r, source-hook-x, eval=FALSE} 110 | 1 + 1 111 | if (TRUE) { # SECRET!! 112 | 1:10 113 | } 114 | ``` 115 | 116 | The value of `x` in the `source` hook is: 117 | 118 | ```{r, eval=FALSE} 119 | c("1 + 1", "if (TRUE) { # SECRET!!\n 1:10\n}") 120 | ``` 121 | 122 | 123 | If you want to hide lines instead of expressions of R code, you will have to split `x` into individual lines. You may consider using the function `xfun::split_lines()`\index{xfun!split\_lines()}. The body of the hook function will be: 124 | 125 | ```{r, eval=FALSE} 126 | x <- xfun::split_lines(x) # split into individual lines 127 | x <- x[!grepl('# SECRET!!$', x)] 128 | x <- paste(x, collapse = '\n') # combine into a single string 129 | hook_source(x, options) 130 | ``` 131 | 132 | This example shows you how to manipulate the source code string, and `grepl()` is certainly not the only choice of string manipulation. In Section \@ref(hook-number), we will show another example. 133 | 134 | ## Add line numbers to source code {#hook-number} 135 | 136 | In this section, we show an example of defining a `source` hook to add line numbers as comments to the source code. For example, for this code chunk: 137 | 138 | ````md 139 | ```{r}`r ''` 140 | if (TRUE) { 141 | x <- 1:10 142 | x + 1 143 | } 144 | ``` 145 | ```` 146 | 147 | We want the output to be: 148 | 149 | ```{r, eval=FALSE, tidy=FALSE} 150 | if (TRUE) { # 1 151 | x <- 1:10 # 2 152 | x + 1 # 3 153 | } # 4 154 | ``` 155 | 156 | The full example is below: 157 | 158 | `r import_example('hook-number.Rmd')` 159 | 160 | The main trick in the above example is to determine the number of spaces needed before the comment on each line, so the comments can align to the right. The number depends on the widths of each line of code. We leave it to readers to digest the code in the hook function. Note that the function `strrep()` is used to generate spaces of specified lengths, e.g., 161 | 162 | ```{r} 163 | strrep(' ', c(1, 3, 6, 0)) 164 | ``` 165 | 166 | The method introduced in Section \@ref(number-lines) may be the actual way in which you want to add line numbers to source code. The syntax is cleaner, and it works for both source code and text output blocks. The above `source` hook trick mainly aims to show you one possibility of manipulating the source code with a custom function. 167 | 168 | ## Scrollable text output {#hook-scroll} 169 | 170 | In Section \@ref(html-scroll), we showed how to restrict the heights of code blocks and text output blocks via CSS. In fact, there is a simpler method with the chunk options `attr.source` and `attr.output` to add the `style` attribute to the fenced code blocks in the Markdown output (see Section \@ref(attr-output) for more information on these options). For example, for this code chunk with the `attr.output` option: 171 | 172 | ````md 173 | ```{r, attr.output='style="max-height: 100px;"'}`r ''` 174 | 1:300 175 | ``` 176 | ```` 177 | 178 | Its Markdown output will be: 179 | 180 | ````md 181 | ```r 182 | 1:300 183 | ``` 184 | 185 | ```{style="max-height: 100px;"} 186 | ## [1] 1 2 3 4 5 6 7 8 9 10 187 | ## [11] 11 12 13 14 15 16 17 18 19 20 188 | ## ... ... 189 | ``` 190 | ```` 191 | 192 | Then the text output block will be converted to HTML by Pandoc: 193 | 194 | ```html 195 |
196 | ##   [1]   1   2   3   4   5   6   7   8   9  10
197 | ##  [11]  11  12  13  14  15  16  17  18  19  20
198 | ##  ... ...
199 | 
200 | ``` 201 | 202 | To learn more about Pandoc's fenced code blocks, please read its manual at https://pandoc.org/MANUAL.html#fenced-code-blocks. 203 | 204 | The `attr.source` and `attr.output` options have made it possible for us to specify maximum heights for individual code chunks. However, the syntax is a little clunky, and requires a better understanding of CSS and Pandoc's Markdown syntax. Below we show an example of a custom `output` hook that works with a custom chunk option `max.height`, so you will only need to set the chunk option like `max.height = "100px"` instead of `attr.output = 'style="max-height: 100px;"'`. In this example, we only manipulate the `options` argument, but not the `x` argument. 205 | 206 | `r import_example('hook-scroll.Rmd')` 207 | 208 | Figure \@ref(fig:hook-scroll) shows the output. Note that in the last code chunk with the chunk option `attr.output`, the option will not be overridden by `max.height` because we respect existing attributes by combining them with the `style` attribute generated by `max.height`: 209 | 210 | ```{r, eval=FALSE, tidy=FALSE} 211 | options$attr.output <- c( 212 | options$attr.output, 213 | sprintf('style="max-height: %s;"', options$max.height) 214 | ) 215 | ``` 216 | 217 | ```{r, hook-scroll, echo=FALSE, fig.cap='An example of scrollable text output, with its height specified in the chunk option max.height.'} 218 | knitr::include_graphics('images/hook-scroll.png', dpi = NA) 219 | ``` 220 | 221 | You can use a similar trick in the `source` hook to limit the height of source code blocks. 222 | 223 | ## Truncate text output {#hook-truncate} 224 | 225 | When the text output from a code chunk is lengthy, you may want to only show the first few lines. For example, when printing a data frame of a few thousand rows, it may not be helpful to show the full data, and the first few lines may be enough. Below we redefine the `output` hook so that we can control the maximum number of lines via a custom chunk option `out.lines`: 226 | 227 | ```{r} 228 | # save the built-in output hook 229 | hook_output = knitr::knit_hooks$get("output") 230 | 231 | # set a new output hook to truncate text output 232 | knitr::knit_hooks$set(output = function(x, options) { 233 | if (!is.null(n <- options$out.lines)) { 234 | x = xfun::split_lines(x) 235 | if (length(x) > n) { 236 | # truncate the output 237 | x = c(head(x, n), '....\n') 238 | } 239 | x = paste(x, collapse = '\n') 240 | } 241 | hook_output(x, options) 242 | }) 243 | ``` 244 | 245 | The basic idea of the above hook function is that if the number of lines of the text output is greater than the threshold set in the chunk option `out.lines`\index{chunk option!out.lines} (stored in the variable `n` in the function body), we only keep the first `n` lines and add an ellipsis (`....`) to indicate the output is truncated. 246 | 247 | Now we can test the new `output` hook by setting the chunk option `out.lines = 4` on the chunk below: 248 | 249 | ```{r, out.lines=4} 250 | print(cars) 251 | ``` 252 | 253 | And you see four lines of output as expected. Since we have stored the original `output` hook in `hook_output`, we can restore it by calling the `set()` method again\index{knitr!knit\_hooks}: 254 | 255 | ```{r} 256 | knitr::knit_hooks$set(output = hook_output) 257 | ``` 258 | 259 | As an exercise for readers, you may try to truncate the output in a different way: given the chunk option `out.lines`\index{chunk option!out.lines} to determine the maximum number of lines, can you truncate the output in the middle instead of the end? For example, if `out.lines = 10`, you extract the first and last five lines, and add `....` in the middle like this: 260 | 261 | ```text 262 | ## speed dist 263 | ## 1 4 2 264 | ## 2 4 10 265 | ## 3 7 4 266 | ## 4 7 22 267 | .... 268 | ## 46 24 70 269 | ## 47 24 92 270 | ## 48 24 93 271 | ## 49 24 120 272 | ## 50 25 85 273 | ``` 274 | 275 | Please note that the last line in the output (i.e., the argument `x` of the hook function) might be an empty line, so you may need something like `c(head(x, n/2), '....', tail(x, n/2 + 1))` (`+ 1` to take the last empty line into account). 276 | 277 | ## Output figures in the HTML5 format {#hook-html5} 278 | 279 | By default, plots in R Markdown are included in the tag `` in a `

` or `

` tag in the HTML output. This example below shows how to use the HTML5 `
` tag\index{HTML!figure tag}\index{figure!HTML tag} to display plots. 280 | 281 | `r import_example('hook-html5.Rmd')` 282 | 283 | The figure output is shown in Figure \@ref(fig:hook-html5). Note that we actually overrode the default `plot` hook in this example, while most other examples in this chapter build custom hooks on top of the default hooks. You should completely override default hooks only when you are sure you want to ignore some built-in features of the default hooks. For example, the `plot` hook function in this case did not consider possible chunk options like `out.width = '100%'` or `fig.show = 'animate'`. 284 | 285 | ```{r hook-html5, echo=FALSE, fig.cap="A figure in the HTML5 figure tag."} 286 | knitr::include_graphics("images/hook-html5.png", dpi = NA) 287 | ``` 288 | 289 | This example shows you what you can possibly do with the plot file path `x` in the `plot` hook\index{output hook!plot}. If all you need is to customize the style of figures, you do not have to use the HTML5 tags. Usually the default `plot` hook will output images in the HTML code like this: 290 | 291 | ```html 292 |
293 | 294 |

CAPTION

295 |
296 | ``` 297 | 298 | So you can just define css rules for `div.figure` and `p.caption`. 299 | -------------------------------------------------------------------------------- /13-chunk-hooks.Rmd: -------------------------------------------------------------------------------- 1 | # Chunk Hooks (\*) {#chunk-hooks} 2 | 3 | A chunk hook\index{chunk hook} is a function that is triggered by a chunk option when the value of this chunk option is not `NULL`. Chunk hooks provide a way for you to execute additional tasks beyond running the code in a chunk. For example, you may want to post-process plots (e.g., Section \@ref(crop-plot) and Section \@ref(optipng)), or record the time taken by a code chunk (Section \@ref(time-chunk)). Such tasks may not be essential to the computing or analysis in the report, but they can be useful for other purposes (e.g., enhance plots or help you identify the most time-consuming chunks). 4 | 5 | You can use chunk hooks purely for their side effects (e.g., only printing out certain information to the console), or for their returned values, which will be written to the output document if the value is a character value. 6 | 7 | Like output hooks (see Chapter \@ref(output-hooks)), chunk hooks are also registered via the object `knitr::knit_hooks`\index{knitr!knit\_hooks}. Please note that the names of output hooks are reserved by **knitr**, so you must not use these names for your custom chunk hooks: 8 | 9 | ```{r} 10 | names(knitr:::.default.hooks) 11 | ``` 12 | 13 | A chunk hook is associated with a chunk option\index{chunk option!chunk hook|see {chunk hook}} of the same name. For example, you can register a chunk hook with the name `greet`: 14 | 15 | ```{r} 16 | knitr::knit_hooks$set(greet = function(before) { 17 | if (before) "Hello!" else "Bye!" 18 | }) 19 | ``` 20 | 21 | We will explain the arguments of the hook function in a moment. Now we set the chunk option `greet = TRUE` for the chunk below: 22 | 23 | ````md 24 | ```{r, greet=TRUE}`r ''` 25 | 1 + 1 26 | ``` 27 | ```` 28 | 29 | And you will see that "Hello!" appears before the chunk, and "Bye!" appears after the chunk in the output below (which is because they are character values): 30 | 31 | > ```{r, greet=TRUE} 32 | > 1 + 1 33 | > ``` 34 | 35 | A chunk hook function can possibly take four arguments: `before`, `options`, `envir`, and `name`. In other words, it can be of this form: 36 | 37 | ```r 38 | function(before, options, envir, name) { 39 | 40 | } 41 | ``` 42 | 43 | All four arguments are optional. You can have four, three, two, one, or even no arguments. In the above example, we used one argument (i.e., `before`). The meanings of these arguments are: 44 | 45 | - `before`: Whether the chunk hook is currently being executed before or after the code chunk itself is executed. Note that a chunk hook is executed twice for every code chunk (once before with `hook(before = TRUE)` and once after with `hook(before = FALSE`). 46 | 47 | - `options`: The list of chunk options for the current code chunk, e.g., `list(fig.width = 5, echo = FALSE, ...)`. 48 | 49 | - `envir`: The environment in which the chunk hook is evaluated. 50 | 51 | - `name`: The name of the chunk option that triggered the chunk hook. 52 | 53 | As we mentioned in the beginning of this chapter, non-character values returned by chunk hooks are silently ignored, and character values are written to the output document. 54 | 55 | ## Crop plots {#crop-plot} 56 | 57 | The chunk hook `knitr::hook_pdfcrop()`\index{knitr!hook\_pdfcrop()}\index{chunk hook!crop plot} can be used to crop PDF and other types of plot files, i.e., remove the extra margins in plots. To enable it, set this hook via `knit_hooks$set()`\index{knitr!knit\_hooks} in a code chunk, and turn on the corresponding chunk option, e.g., 58 | 59 | ```{r} 60 | knitr::knit_hooks$set(crop = knitr::hook_pdfcrop) 61 | ``` 62 | 63 | Then you can use the chunk option `crop = TRUE`\index{chunk option!crop} to crop plots in a code chunk. 64 | 65 | The hook `hook_pdfcrop()` calls the external program `pdfcrop` to crop PDF files. This program often comes with a LaTeX distribution (e.g., TeX Live or MiKTeX). You can check if it is available in your system via: 66 | 67 | ```{r} 68 | # if the returned value is not empty, it is available 69 | Sys.which('pdfcrop') 70 | ``` 71 | 72 | If you are using the LaTeX distribution TinyTeX (see Section \@ref(install-latex)), and `pdfcrop` is not available in your system, you may install it via `tinytex::tlmgr_install('pdfcrop')`\index{tinytex!tlmgr\_install()}. 73 | 74 | ```{r include = FALSE, eval=!tinytex:::check_installed("pdfcrop")} 75 | tinytex::tlmgr_install('pdfcrop') 76 | ``` 77 | 78 | For non-PDF plot files such as PNG or JPEG files, this hook function calls the R package **magick** [@R-magick]\index{R package!magick} for cropping. You need to make sure this R package has been installed. Figure \@ref(fig:crop-no) shows a plot that is not cropped, and Figure \@ref(fig:crop-yes) shows the same plot but has been cropped. 79 | 80 | ```{r, crop-no, crop=NULL, echo=FALSE, fig.height=4, fig.cap='A plot that is not cropped.', out.extra=if (knitr::is_latex_output()) '', resize.command='framebox'} 81 | if (!knitr::is_latex_output()) par(bg = 'gray', fg = 'yellow') 82 | plot(cars) 83 | ``` 84 | 85 | ```{r, crop-yes, crop=TRUE, echo=FALSE, fig.height=4, fig.cap='A plot that is cropped.', ref.label='crop-no', out.extra=if (knitr::is_latex_output()) '', resize.command='framebox'} 86 | ``` 87 | 88 | ## Optimize PNG plots {#optipng} 89 | 90 | If you have installed the program OptiPNG ()\index{OptiPNG}, you may use the hook `knitr::hook_optipng()`\index{knitr!hook\_optipng()} to optimize PNG plot files to a smaller size without losing the image quality\index{chunk hook!optimize PNG}\index{figure!optimize PNG}. 91 | 92 | ```{r, eval=FALSE} 93 | knitr::knit_hooks$set(optipng = knitr::hook_optipng) 94 | ``` 95 | 96 | After you set up this hook, you can use the chunk option `optipng`\index{chunk option!optipng} to pass command-line arguments to OptiPNG, e.g., `optipng = '-o7'`. These command-line arguments are optional, which means you can just use `optipng = ''` to enable the hook for a code chunk. Please see the user manual on the website of OptiPNG to know the possible arguments. 97 | 98 | OptiPNG can be easily installed on macOS with Homebrew (https://brew.sh): `brew install optipng`, and on Windows using Scoop (https://scoop.sh/): `scoop install optipng`. 99 | 100 | ## Report how much time each chunk takes to run {#time-chunk} 101 | 102 | By default, **knitr** provides a text-based progress bar to show you the knitting progress. If you want more precise timing information about the chunks, you may register a custom chunk hook to record the time for each chunk. Here is an example hook: 103 | 104 | ```{r, eval=FALSE} 105 | knitr::knit_hooks$set(time_it = local({ 106 | now <- NULL 107 | function(before, options) { 108 | if (before) { 109 | # record the current time before each chunk 110 | now <<- Sys.time() 111 | } else { 112 | # calculate the time difference after a chunk 113 | res <- difftime(Sys.time(), now, units = "secs") 114 | # return a character string to show the time 115 | paste('Time for this code chunk to run:', 116 | round(res, 2), 'seconds') 117 | } 118 | }}) 119 | ) 120 | ``` 121 | 122 | Then you can time a chunk with the chunk option `time_it`, e.g., 123 | 124 | ```` 125 | ```{r, time_it = TRUE}`r ''` 126 | Sys.sleep(2) 127 | ``` 128 | ```` 129 | 130 | If you want to time all code chunks, you can certainly set the option globally: `knitr::opts_chunk$set(time_it = TRUE)`. 131 | 132 | In the above hook function, you can also output more information from the chunk options (i.e., the `options` argument of the function). For example, you may print out the chunk label in the returned value: 133 | 134 | ```{r, eval=FALSE} 135 | paste('Time for the chunk', options$label, 'to run:', res) 136 | ``` 137 | 138 | Or you may record the time without printing it out in the hook: 139 | 140 | ```{r, eval=FALSE} 141 | all_times <- list() # store the time for each chunk 142 | knitr::knit_hooks$set(time_it = local({ 143 | now <- NULL 144 | function(before, options) { 145 | if (before) { 146 | now <<- Sys.time() 147 | } else { 148 | res <- difftime(Sys.time(), now) 149 | all_times[[options$label]] <<- res 150 | } 151 | }}) 152 | ) 153 | ``` 154 | 155 | Then you can access all the time information in the object `all_times`. This object is a named list with the names being chunk labels, and element values being the execution time for each chunk. 156 | 157 | Lastly, as a technical note, we want to explain the use of the `local()` function in the previous hooks because some readers may not be familiar with it. This function allows you to run code in a "local" environment. The main benefit is that variables created in the code are local to that environment, so they will not pollute the outer environment (usually the global environment). For example, we created a variable `now` in `local()`, and used it in the `time_it` hook function. In the hook function, we update the value of `now` via the double arrow `<<-` instead of the normal assignment operator `<-`. This is because `<<-` assigns a value to a variable in the parent environment (which is the environment in `local()` in this case), and `<-` can only assign values to variables in the current environment. Before each code chunk is evaluated, the local variable `now` records the current time. After each code chunk is evaluated, we calculate the time difference between the current time and `now`. Note that `local()` returns the last value in the expression passed to it, which is a (hook) function in this case. In short, `local()` can make your workspace cleaner by not exposing variables that are only used locally but unused in the global environment. If you do not mind creating a variable `now` in the global environment, you can choose not to use `local()`. 158 | 159 | ## Show the chunk header in the output {#show-header} 160 | 161 | Sometimes you may want to show the original code chunk header to your readers. For example, when you write an R Markdown tutorial, you may want to show both the chunk output and the chunk options that you used to generate the output, so your readers can learn how to do it by themselves. 162 | 163 | The original chunk options are actually stored as a character string in the chunk option `params.src`. After you know this, you may write a chunk hook to add `params.src` to the output. Below is a full example: 164 | 165 | `r import_example('chunk-wrapper.Rmd')` 166 | 167 | Basically, we restored the chunk header from `options$params.src` by putting this string inside ```` ```{r, }````. Then we wrapped this line in a pair of four backticks, so it can be displayed verbatim in the output. Note that the original code chunk might be indented (e.g., when it is nested in a list item), so we also need to add the proper indentation, which is stored in the chunk option `options$indent`. 168 | 169 | The output of the bullet list at the end of the above example will be like this: 170 | 171 | > - One bullet. 172 | > 173 | > ```` 174 | > ```{r, eval=TRUE}`r ''` 175 | > ```` 176 | > ```r 177 | > 2 + 2 178 | > ``` 179 | > ``` 180 | > ## [1] 4 181 | > ``` 182 | > ```` 183 | > ``` 184 | > ```` 185 | > 186 | > - Another bullet. 187 | 188 | You can see that the code chunk was evaluated, and the chunk header was also added. 189 | 190 | ## Embed an interactive 3D plot with rgl {#rgl-3d} 191 | 192 | The **rgl** package [@R-rgl]\index{R package!rgl} can be used to generate interactive 3D plots. These plots can still be interactive if they are saved to the WebGL format\index{WebGL}, which can be done through a hook function `rgl::hook_webgl()`\index{chunk hook!WebGL plot}\index{figure!WebGL}. Below is an example that shows you how to set up **rgl** and **knitr** so 3D plots can be saved while preserving the interactivity: 193 | 194 | `r import_example('rgl-3d.Rmd')` 195 | 196 | You should get an interactive 3D scatterplot like Figure \@ref(fig:rgl-3d) after you compile this example. Note that the interactive plots only work when the output format is HTML. 197 | 198 | ```{r, rgl-3d, echo=FALSE, fig.cap='A 3D scatterplot generated from the rgl package.', fig.align='center'} 199 | knitr::include_graphics('images/rgl-3d.png', dpi = NA) 200 | ``` 201 | -------------------------------------------------------------------------------- /18-references.Rmd: -------------------------------------------------------------------------------- 1 | ```{r, child=if(knitr::is_latex_output()) '_knitr-options.Rmd'} 2 | ``` 3 | 4 | \backmatter 5 | 6 | `r if (knitr::is_html_output()) ' 7 | # References {-} 8 | '` 9 | 10 | ```{r include=FALSE, tidy=FALSE, warning = FALSE} 11 | # list cited packages 12 | pkgs <- c( 13 | .packages(), 14 | 'animation', 15 | 'blastula', 16 | 'blogdown', 17 | 'bookdown', 18 | 'broom', 19 | 'Cairo', 20 | 'condformat', 21 | 'dagitty', 22 | 'diagram', 23 | 'DiagrammeR', 24 | 'distill', 25 | 'downloadthis', 26 | 'DT', 27 | 'equatiomatic', 28 | 'ezknitr', 29 | 'flair', 30 | 'flexdashboard', 31 | 'flextable', 32 | 'formatR', 33 | 'formattable', 34 | 'gganimate', 35 | 'ggdag', 36 | 'ggplot2', 37 | 'gifski', 38 | 'googledrive', 39 | 'govdown', 40 | 'gt', 41 | 'gtsummary', 42 | 'here', 43 | 'huxtable', 44 | 'kableExtra', 45 | 'knitcitations', 46 | 'knitr', 47 | 'learnr', 48 | 'magick', 49 | 'nomnoml', 50 | 'officedown', 51 | 'officer', 52 | 'pagedown', 53 | 'pander', 54 | 'pandoc', 55 | 'pixiedust', 56 | 'pkgdown', 57 | 'printr', 58 | 'r2d3', 59 | 'reactable', 60 | 'reticulate', 61 | 'revealjs', 62 | 'rgl', 63 | 'rhandsontable', 64 | 'rmarkdown', 65 | 'rmdformats', 66 | 'roxygen2', 67 | 'rsconnect', 68 | 'rticles', 69 | 'sass', 70 | 'spelling', 71 | 'stargazer', 72 | 'styler', 73 | 'svglite', 74 | 'tables', 75 | 'tangram', 76 | 'tikzDevice', 77 | 'tinytex', 78 | 'trackdown', 79 | 'tufte', 80 | 'usethis', 81 | 'webshot2', 82 | 'workflowr', 83 | 'xaringan', 84 | 'xfun', 85 | 'xtable', 86 | 'yaml', 87 | 'ztable' 88 | # GH Only: 'MonashEBSTemplates', 'plantuml', 'redoc', 'Statamarkdown', 'rmdrive' 89 | # LaTeX: 'float', 'framed', 'tscolorbox', 'booktabs', 'longtable', 'flafter', 'subfig', 'fancyhdr', 'xcolor', 'listings', 'titling', 'animate' 90 | # Bios only: 'shiny', 'leaflet', 'rolldown', 'crrri', 'projmgr', 'Rd2roxyen', 'servr', 'highr', 'mime', 'testit', 'fun', 'tufte' 91 | # JavaScript: 'DataTables' 92 | ) 93 | 94 | # Warn to add new deps in DESCRIPTION 95 | pkg2 <- setdiff(pkgs, xfun::base_pkgs()) 96 | in_desc <- pkg2 %in% desc::desc_get_deps()$package 97 | if (any(!in_desc)) warning("To add to DESCRIPTION: ", paste0(pkg2[!in_desc], collapse = ", ")) 98 | 99 | # automatically create a bib database for R packages 100 | knitr::write_bib(pkgs, file = 'packages.bib', lib.loc = c(.libPaths(), '~/R-tmp')) 101 | 102 | # embed fonts 103 | if (knitr::is_latex_output()) { 104 | pdfs = list.files(knitr::opts_chunk$get('fig.path'), '[.]pdf$', full.names = TRUE) 105 | invisible(lapply(pdfs, embedFonts)) 106 | } 107 | ``` 108 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Type: Book 2 | Package: rmarkdowncookbook 3 | Title: R Markdown Cookbook 4 | Version: 1.0.1 5 | Imports: 6 | animation, 7 | blastula, 8 | blogdown, 9 | bookdown, 10 | broom, 11 | Cairo, 12 | condformat, 13 | dagitty, 14 | desc, 15 | diagram, 16 | DiagrammeR, 17 | distill, 18 | downloadthis, 19 | DT, 20 | equatiomatic, 21 | ezknitr, 22 | flair, 23 | flexdashboard, 24 | flextable, 25 | formatR, 26 | formattable, 27 | gganimate, 28 | ggdag, 29 | ggplot2, 30 | gifski, 31 | googledrive, 32 | govdown, 33 | gt, 34 | gtsummary, 35 | here, 36 | huxtable, 37 | jsonlite, 38 | kableExtra, 39 | knitcitations, 40 | knitr, 41 | learnr, 42 | magick, 43 | nomnoml, 44 | officedown, 45 | officer, 46 | pagedown, 47 | pander, 48 | pandoc, 49 | pdftools, 50 | pixiedust, 51 | pkgdown, 52 | printr, 53 | r2d3, 54 | reactable, 55 | reticulate, 56 | revealjs, 57 | rgl, 58 | rhandsontable, 59 | rmarkdown, 60 | rmdformats, 61 | roxygen2, 62 | rsconnect, 63 | rticles, 64 | sass, 65 | spelling, 66 | stargazer, 67 | styler, 68 | svglite, 69 | tables, 70 | tangram, 71 | tikzDevice, 72 | tinytex, 73 | trackdown, 74 | tufte, 75 | usethis, 76 | webshot2, 77 | workflowr, 78 | xaringan, 79 | xfun, 80 | xtable, 81 | yaml, 82 | ztable 83 | Suggests: 84 | rsconnect 85 | Encoding: UTF-8 86 | Remotes: datalorax/equatiomatic, r-for-educators/flair 87 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | rm -f _book/*.html &&\ 3 | BOOKDOWN_FULL_PDF=false Rscript --quiet _render.R 4 | 5 | pdf: 6 | Rscript --quiet _render.R "bookdown::pdf_book" &&\ 7 | rm -f latex/blackbox.tex latex/infobox.tex &&\ 8 | mv _book/rmarkdown-cookbook.pdf _book/rmarkdown-cookbook-full.pdf &&\ 9 | open _book/rmarkdown-cookbook-full.pdf 10 | 11 | gitbook: 12 | Rscript --quiet _render.R "bookdown::gitbook" 13 | 14 | pdf2: 15 | BOOKDOWN_FULL_PDF=false Rscript --quiet _render.R "bookdown::pdf_book" 16 | 17 | clean: 18 | Rscript -e 'bookdown::clean_book(TRUE)' 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # R Markdown Cookbook 2 | 3 | [![build-book](https://github.com/yihui/rmarkdown-cookbook/actions/workflows/build-book.yaml/badge.svg)](https://github.com/yihui/rmarkdown-cookbook/actions/workflows/build-book.yaml) 4 | 5 | This repository contains the source files of the book _R Markdown Cookbook_, to be published in October 2020 by Chapman & Hall/CRC. 6 | 7 | [![R Markdown Cookbook](https://bookdown.org/yihui/rmarkdown-cookbook/images/cover.png)](https://www.routledge.com/p/book/9780367563837) 8 | 9 | You may also read the free online version at . 10 | 11 | ## Contributions 12 | 13 | If you have any feedback, please feel free to [file an issue on GitHub](https://github.com/yihui/rmarkdown-cookbook/issues/new). Thank you! 14 | -------------------------------------------------------------------------------- /SETUP.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for considering contributing to the R Markdown Cookbook. 4 | 5 | These instructions describe the steps and system requirements needed to build this book. 6 | 7 | When contribution to the book, it is a good practice to build it locally to ensure that any modifications that you have made worked as expected. However, please do not include the built HTML in your pull request. It is easier to merge in plain text R, Markdown, and R Markdown files and to only re-knit the book on the master branch. 8 | 9 | ## System Requirements 10 | 11 | ### R Packages 12 | 13 | For this book, dependencies are required to execute R chunks and to generate references of others packages mentioned. Dependencies are tracked using a DESCRIPTION files. 14 | 15 | To render the book, you can install the required packages using **renv**, **remotes** or **pak** 16 | 17 | #### Using **renv** 18 | 19 | This project contains a lockfile that you can use to reinstall packages in a project library, separated from your other global packages installation. 20 | 21 | ```r 22 | install.packages('renv') 23 | renv::activate() 24 | renv::restore() 25 | ``` 26 | 27 | #### Using **remotes** 28 | 29 | ```r 30 | install.packages("remotes") 31 | remotes::install_deps(".") 32 | ``` 33 | 34 | #### Using **pak** 35 | 36 | **pak** benefits from a caching mechanism of installed packages and offers fast installation. 37 | 38 | ```r 39 | install.packages("pak", repos = "https://r-lib.github.io/p/pak/dev/") 40 | pak::local_install_dev_deps(upgrade = TRUE) 41 | ``` 42 | 43 | This methods is used in CI. 44 | 45 | ### LaTeX Packages 46 | 47 | The TinyTeX LaTeX distribution is required to build the book. This can be done using `tinytex::install_tinytex()`. 48 | 49 | This command will be run on the first render of [01-installation.Rmd](https://github.com/yihui/rmarkdown-cookbook/blob/master/01-installation.Rmd) TinyTeX is not already installed. 50 | 51 | Other required LaTeX packages should be installed automatically during the build of the book. Know required packages are the `pgf`, `preview`, and `xcolor` packages used in [11-chunk-options.Rmd](https://github.com/yihui/rmarkdown-cookbook/blob/master/11-chunk-options.Rmd) and they will be installed as needed when running that chapter. 52 | 53 | ### Other System Dependencies 54 | 55 | For Windows users, the section of the book on the [Asymptote language engine](https://bookdown.org/yihui/rmarkdown-cookbook/eng-asy.html) does not build properly so it will be skipped. For users with other operating systems, this section will be skipped if Asymptote is not installed on your computer. If they wish to build this section, they must download and install the [Asymptote software](https://asymptote.sourceforge.io/) from its website. After you install it, you may confirm that it is on your system's path by running the terminal command `where asy` or `Sys.which("asy")` from within R. 56 | 57 | Chrome is also required for use with the **webshot2** package. 58 | 59 | ## Building the Book 60 | 61 | ### Building the Whole Book 62 | 63 | Once you have set up all needed dependencies, there are multiple ways to build the book. 64 | 65 | If you currently use `make` (or set it up following [Software Carpentry's instructions](https://swcarpentry.github.io/make-novice/)), you may type `make gitbook` in the terminal. Equivalently, if you work in RStudio, you may click `Build All` in the Build pane. 66 | 67 | If you are simply testing changes locally and not planning to publish the version you render, you may also run `bookdown::render_book()` in the R console or click `Addins > Preview Book` in the RStudio IDE. However, these methods will not completely reproduce the book because the custom `_render.R` file will not be executed. 68 | 69 | ### Building a Chapter 70 | 71 | You may also build just one chapter of the book at a time to ensure that any contributions or updates you are making render correctly. To do so, you may run, `bookdown::preview_chapter()` (e.g. `bookdown::preview_chapter("04-content.Rmd")`) in the console. 72 | 73 | 74 | -------------------------------------------------------------------------------- /_bookdown.yml: -------------------------------------------------------------------------------- 1 | book_filename: "rmarkdown-cookbook" 2 | repo: https://github.com/yihui/rmarkdown-cookbook 3 | delete_merged_file: true 4 | clean: [packages.bib] 5 | language: 6 | label: 7 | fig: "FIGURE " 8 | tab: "TABLE " 9 | ui: 10 | chapter_name: "Chapter " 11 | edit: "https://github.com/yihui/rmarkdown-cookbook/edit/master/%s" 12 | before_chapter_script: "utils.R" 13 | -------------------------------------------------------------------------------- /_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | [ -z "${GITHUB_PAT}" ] && exit 0 6 | [ "${TRAVIS_BRANCH}" != "master" ] && exit 0 7 | 8 | git config --global user.email "xie@yihui.name" 9 | git config --global user.name "Yihui Xie" 10 | 11 | git clone -b gh-pages https://${GITHUB_PAT}@github.com/${TRAVIS_REPO_SLUG}.git book-output 12 | cd book-output 13 | 14 | ls | xargs rm -rf 15 | git ls-files --deleted -z | xargs -0 git rm 16 | 17 | cp -r ../_book/* ./ 18 | git add --all * 19 | git commit -m"Update the book" || true 20 | git reset $(git commit-tree HEAD^{tree} -m "Update the book") 21 | git push -f -q origin gh-pages 22 | -------------------------------------------------------------------------------- /_knitr-options.Rmd: -------------------------------------------------------------------------------- 1 | # (APPENDIX) Appendix {-} 2 | 3 | # **knitr**'s Chunk and Package Options {#full-options} 4 | 5 | ```{r, echo=FALSE, results='asis'} 6 | # retrieve md from knitr's website; I have cloned the website repo locally, so 7 | # I'll read the local .md file; if you don't want to clone the repo, you may 8 | # read from Github 9 | url = '../yihui.org/content/knitr/options.md' 10 | # system('git clone git@github.com:rbind/yihui.git ../yihui.org') 11 | if (!file.exists(url)) url = 'https://raw.githubusercontent.com/rbind/yihui/master/content/knitr/options.md' 12 | txt = xfun::read_utf8(url) 13 | 14 | # remove YAML 15 | txt = txt[(which(txt == '---')[2] + 1):length(txt)] 16 | txt = sub('This page documents', 'This appendix describes', txt) 17 | txt = sub('the "Code Decoration" section', 'Section \\@ref(code-decoration)', txt, fixed = TRUE) 18 | txt = sub('## Chunk Options', '## Chunk options {#chunk-options-full}', txt) 19 | txt = sub('## Package Options', '## Package options', txt) 20 | txt = sub('### Animation', '### Animation {#animation-options}', txt) 21 | txt = sub('### Cache', '### Cache {#cache-options}', txt) 22 | # resolve relative URLs to absolute URLs 23 | m = gregexpr('(?<=]\\()[^)]+(?=\\))', txt, perl = TRUE) 24 | regmatches(txt, m) = lapply(regmatches(txt, m), function(x) { 25 | i = grepl('/', x) 26 | if (length(x[i]) == 0) return(x) 27 | i = i & !grepl('^http', x) 28 | x[i] = sub('^[.][.]/', 'https://yihui.org/knitr/', x[i]) 29 | x 30 | }) 31 | # output raw text 32 | cat(txt, sep = '\n') 33 | ``` 34 | -------------------------------------------------------------------------------- /_output.yml: -------------------------------------------------------------------------------- 1 | bookdown::gitbook: 2 | split_by: section 3 | css: [css/style.css, css/box.css] 4 | config: 5 | toc: 6 | before: | 7 |
  • R Markdown Cookbook
  • 8 | after: | 9 |
  • Published with bookdown
  • 10 | sharing: 11 | github: yes 12 | facebook: no 13 | bookdown::pdf_book: 14 | includes: 15 | in_header: [latex/preamble.tex, latex/blackbox.tex, latex/infobox.tex] 16 | before_body: latex/before_body.tex 17 | after_body: latex/after_body.tex 18 | keep_tex: yes 19 | fig_crop: false 20 | latex_engine: xelatex 21 | citation_package: natbib 22 | template: null 23 | pandoc_args: [--top-level-division=chapter, --wrap=none] 24 | toc_depth: 3 25 | toc_unnumbered: no 26 | toc_appendix: yes 27 | highlight_bw: yes 28 | quote_footer: ["\\VA{", "}{}"] 29 | bookdown::epub_book: default 30 | -------------------------------------------------------------------------------- /_render.R: -------------------------------------------------------------------------------- 1 | quiet = "--quiet" %in% commandArgs(FALSE) 2 | formats = commandArgs(TRUE) 3 | 4 | # provide default formats if necessary 5 | if (length(formats) == 0) formats = c('bookdown::pdf_book', 'bookdown::gitbook') 6 | # render the book to all formats unless they are specified via command-line args 7 | for (fmt in formats) { 8 | cmd = sprintf("bookdown::render_book('index.Rmd', '%s', quiet = %s)", fmt, quiet) 9 | res = bookdown:::Rscript(c('-e', shQuote(cmd))) 10 | if (res != 0) stop('Failed to compile the book to ', fmt) 11 | } 12 | unlink('rmarkdown-cookbook.log') 13 | 14 | # creates redirects 15 | redirect = function(from, to) { 16 | to = paste0('https://bookdown.org/yihui/rmarkdown-cookbook/', to) 17 | writeLines(sprintf( 18 | '', to, to 19 | ), paste0('_book/', from)) 20 | } 21 | 22 | redirect('r-markdown-components.html', 'rmarkdown-process.html') 23 | redirect('acknowledgements.html', 'acknowledgments.html') 24 | 25 | 26 | if (length(formats) > 1) { 27 | if (!is.na(Sys.getenv('CI', NA))) { 28 | xfun::pkg_load2("rsconnect") 29 | # On CI connect to server, using API KEY and deploy using appId 30 | rsconnect::addConnectServer('https://bookdown.org', 'bookdown.org') 31 | rsconnect::connectApiUser( 32 | account = 'GHA', server = 'bookdown.org', 33 | apiKey = Sys.getenv('CONNECT_API_KEY') 34 | ) 35 | rsconnect::deploySite( 36 | appId = Sys.getenv('CONTENT_ID'), 37 | server = 'bookdown.org', 38 | render = 'none', logLevel = 'verbose', 39 | forceUpdate = TRUE) 40 | } else if (Sys.getenv('USER') == 'yihui') { 41 | # for local deployment when rsconnect/ is available 42 | bookdown::publish_book('rmarkdown-cookbook', server = 'bookdown.org', render = 'none') 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /css/box.css: -------------------------------------------------------------------------------- 1 | .blackbox { 2 | padding: 1em; 3 | background: black; 4 | color: white; 5 | border: 2px solid orange; 6 | border-radius: 10px; 7 | } 8 | .center { 9 | text-align: center; 10 | } 11 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | .infobox { 2 | padding: 1em 1em 1em 4em; 3 | margin-bottom: 10px; 4 | background: lightgray 5px center/3em no-repeat; 5 | border: 2px solid orange; 6 | border-radius: 10px; 7 | } 8 | .caution { 9 | background-image: url("../images/caution.png"); 10 | } 11 | .important { 12 | background-image: url("../images/important.png"); 13 | } 14 | .note { 15 | background-image: url("../images/note.png"); 16 | } 17 | .tip { 18 | background-image: url("../images/tip.png"); 19 | } 20 | .warning { 21 | background-image: url("../images/warning.png"); 22 | } 23 | 24 | .figure { 25 | text-align: center; 26 | } 27 | p.caption { 28 | color: #777; 29 | margin-top: 10px; 30 | text-align: left; 31 | } 32 | p code { 33 | white-space: inherit; 34 | } 35 | pre { 36 | word-break: normal; 37 | word-wrap: normal; 38 | } 39 | pre code { 40 | white-space: inherit; 41 | } 42 | .flushright { 43 | text-align: right; 44 | } 45 | pre.numberSource code > span > a:first-child::before { 46 | left: -0.3em; 47 | } 48 | .header-section-number { 49 | padding-right: .2em; 50 | font-weight: 500; 51 | } 52 | .level1 .header-section-number { 53 | display: inline-block; 54 | border-bottom: 3px solid; 55 | } 56 | .level1 h1 { 57 | border-bottom: 1px solid; 58 | } 59 | h1, h2, h3, h4, h5, h6 { 60 | font-weight: normal; 61 | } 62 | h1.title { 63 | font-weight: 700; 64 | } 65 | .book .book-body .page-wrapper .page-inner section.normal strong { 66 | font-weight: 600; 67 | } 68 | .book .book-body .page-wrapper .page-inner section.normal pre a { 69 | color: inherit; 70 | } 71 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ## Examples 2 | 3 | This directory contains examples included in this book. 4 | -------------------------------------------------------------------------------- /examples/awesomebox.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Awesome Boxes 3 | output: 4 | pdf_document: 5 | extra_dependencies: awesomebox 6 | --- 7 | 8 | A note box: 9 | 10 | ::: {.noteblock data-latex=""} 11 | Thank you for noticing this **new notice**! Your noticing it has 12 | been noted, and _will be reported to the authorities_! 13 | ::: 14 | 15 | We define an R function `box_args()` to generate the arguments 16 | for the box: 17 | 18 | ```{r} 19 | box_args <- function( 20 | vrulecolor = 'white', 21 | hrule = c('\\abLongLine', '\\abShortLine', ''), 22 | title = '', vrulewidth = '0pt', 23 | icon = 'Bomb', iconcolor = 'black' 24 | ) { 25 | hrule <- match.arg(hrule) 26 | sprintf( 27 | '[%s][%s][\\textbf{%s}]{%s}{\\fa%s}{%s}', 28 | vrulecolor, hrule, title, vrulewidth, icon, iconcolor 29 | ) 30 | } 31 | ``` 32 | 33 | Pass some arguments to the `awesomeblock` environment through 34 | an inline R expression: 35 | 36 | ::: {.awesomeblock data-latex="`r box_args(title = 'NOTICE!')`"} 37 | Thank you for noticing this **new notice**! 38 | 39 | Your noticing it has been noted, and _will be reported to 40 | the authorities_! 41 | ::: 42 | -------------------------------------------------------------------------------- /examples/cat-css.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Create a CSS file from a code chunk" 3 | output: 4 | html_document: 5 | css: custom.css 6 | --- 7 | 8 | The chunk below will be written to `custom.css`, which 9 | will be used during the Pandoc conversion. 10 | 11 | ```{cat, engine.opts = list(file = "custom.css")} 12 | h2 { 13 | color: blue; 14 | } 15 | ``` 16 | 17 | ## And this title will blue 18 | -------------------------------------------------------------------------------- /examples/cat-latex.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Create a .tex file from a chunk" 3 | author: "Jane Doe" 4 | classoption: twoside 5 | output: 6 | pdf_document: 7 | includes: 8 | in_header: preamble.tex 9 | --- 10 | 11 | # How it works 12 | 13 | Write a code chunk to a file `preamble.tex` to define 14 | the header and footer of the PDF output document: 15 | 16 | ```{cat, engine.opts=list(file = 'preamble.tex')} 17 | \usepackage{fancyhdr} 18 | \usepackage{lipsum} 19 | \pagestyle{fancy} 20 | \fancyhead[CO,CE]{This is fancy header} 21 | \fancyfoot[CO,CE]{And this is a fancy footer} 22 | \fancyfoot[LE,RO]{\thepage} 23 | \fancypagestyle{plain}{\pagestyle{fancy}} 24 | ``` 25 | 26 | \lipsum[1-15] 27 | 28 | # More random content 29 | 30 | \lipsum[16-30] 31 | -------------------------------------------------------------------------------- /examples/chunk-custom.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Assign custom classes to chunks 3 | output: html_document 4 | --- 5 | 6 | First we define some CSS rules for a class `watch-out`. 7 | 8 | ```{css, echo=FALSE} 9 | .watch-out { 10 | background-color: lightpink; 11 | border: 3px solid red; 12 | font-weight: bold; 13 | } 14 | ``` 15 | 16 | Then we assign a class `watch-out` to the code chunk via the 17 | chunk option `class.source`. 18 | 19 | ```{r class.source="watch-out"} 20 | mtcars[1:5, "mpg"] 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /examples/chunk-style.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Change the chunk style 3 | output: html_document 4 | --- 5 | 6 | When you subset a data frame, it does not necessarily return 7 | a data frame. For example, if you subset two columns, you get 8 | a data frame, but when you try to subset one column, you get 9 | a vector: 10 | 11 | ```{r class.source="bg-danger", class.output="bg-warning"} 12 | mtcars[1:5, "mpg"] 13 | ``` 14 | 15 | To make sure that we always get a data frame, we have to use 16 | the argument `drop = FALSE`. Now we use the chunk option 17 | `class.source = "bg-success"`. 18 | 19 | ```{r df-drop-ok, class.source="bg-success"} 20 | mtcars[1:5, "mpg", drop = FALSE] 21 | ``` 22 | -------------------------------------------------------------------------------- /examples/chunk-wrapper.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Show chunk headers in the output 3 | --- 4 | 5 | Set up a chunk hook named `wrapper` to wrap the chunk 6 | output inside the original chunk header and footer. 7 | 8 | ```{r, setup, include=FALSE} 9 | knitr::knit_hooks$set(wrapper = function(before, options) { 10 | # the original chunk might be indented 11 | if (is.null(indent <- options$indent)) indent <- '' 12 | 13 | # hide the wrapper=TRUE option 14 | opts <- gsub(', wrapper=TRUE', '', options$params.src) 15 | 16 | if (before) { 17 | # add the header 18 | sprintf('\n\n%s````\n```{r,%s}\n````\n', indent, opts) 19 | } else { 20 | # add the footer 21 | sprintf('\n\n%s````\n```\n````\n', indent) 22 | } 23 | }) 24 | ``` 25 | 26 | Now we apply the hook via the chunk option `wrapper=TRUE`. 27 | Remember to put `wrapper=TRUE` at the end of the header, and 28 | it has to be `wrapper=TRUE` precisely (e.g., not `wrapper=T`), 29 | following a comma and a space, unless you adjust the `gsub()` 30 | call in the above hook. 31 | 32 | ```{r, test-label, collapse=TRUE, wrapper=TRUE} 33 | 1 + 1 34 | plot(cars) 35 | ``` 36 | 37 | You should see the original chunk header appear in 38 | the output. The hook should also work when the chunk 39 | is indented, e.g., 40 | 41 | - One bullet. 42 | 43 | ```{r, eval=TRUE, wrapper=TRUE} 44 | 2 + 2 45 | ``` 46 | 47 | - Another bullet. 48 | -------------------------------------------------------------------------------- /examples/columns.css: -------------------------------------------------------------------------------- 1 | .cols {display: flex; } 2 | -------------------------------------------------------------------------------- /examples/columns.tex: -------------------------------------------------------------------------------- 1 | \newenvironment{cols}[1][]{}{} 2 | 3 | \newenvironment{col}[1]{\begin{minipage}{#1}\ignorespaces}{% 4 | \end{minipage} 5 | \ifhmode\unskip\fi 6 | \aftergroup\useignorespacesandallpars} 7 | 8 | \def\useignorespacesandallpars#1\ignorespaces\fi{% 9 | #1\fi\ignorespacesandallpars} 10 | 11 | \makeatletter 12 | \def\ignorespacesandallpars{% 13 | \@ifnextchar\par 14 | {\expandafter\ignorespacesandallpars\@gobble}% 15 | {}% 16 | } 17 | \makeatother 18 | -------------------------------------------------------------------------------- /examples/cross-ref.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cross-referencing figures, tables, and equations 3 | author: Generated by bookdown 4 | output: 5 | bookdown::html_document2: default 6 | bookdown::pdf_document2: default 7 | --- 8 | 9 | See Figure \@ref(fig:cars-plot). 10 | 11 | ```{r cars-plot, fig.cap="The cars data.", echo=FALSE} 12 | par(mar = c(4, 4, .2, .1)) 13 | plot(cars) # a scatterplot 14 | ``` 15 | 16 | Also see Equation \@ref(eq:mean). 17 | 18 | \begin{equation} 19 | \bar{X} = \frac{\sum_{i=1}^n X_i}{n} (\#eq:mean) 20 | \end{equation} 21 | 22 | And see Table \@ref(tab:mtcars). 23 | 24 | ```{r mtcars, echo=FALSE} 25 | knitr::kable(mtcars[1:5, 1:5], caption = "The mtcars data.") 26 | ``` 27 | -------------------------------------------------------------------------------- /examples/d3.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Generate a chart with D3 3 | output: html_document 4 | --- 5 | 6 | First, load the package **r2d3** to set up the `d3` engine 7 | for **knitr** automatically: 8 | 9 | ```{r setup} 10 | library(r2d3) 11 | ``` 12 | 13 | Now we can generate data in R, pass it to D3, and draw 14 | the chart: 15 | 16 | ```{d3, data=runif(30), options=list(color='steelblue')} 17 | svg.selectAll('rect') 18 | .data(data) 19 | .enter() 20 | .append('rect') 21 | .attr('width', function(d) { return d * 672; }) 22 | .attr('height', '10px') 23 | .attr('y', function(d, i) { return i * 16; }) 24 | .attr('fill', options.color); 25 | ``` 26 | -------------------------------------------------------------------------------- /examples/details-tag.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Use the `
    ` disclosure element 3 | output: html_document 4 | --- 5 | 6 | We show text output inside the `
    ` tags in this 7 | example. We used JavaScript to wrap text output blocks 8 | in `
    `. The JavaScript code needs to 9 | be executed at the end of this document, so it is placed 10 | at the end. Below is a testing code chunk: 11 | 12 | ```{r} 13 | 1:100 14 | ``` 15 | 16 | The actual JavaScript code is below. 17 | 18 | ```{js, echo=FALSE} 19 | (function() { 20 | var codes = document.querySelectorAll('pre:not([class])'); 21 | var code, i, d, s, p; 22 | for (i = 0; i < codes.length; i++) { 23 | code = codes[i]; 24 | p = code.parentNode; 25 | d = document.createElement('details'); 26 | s = document.createElement('summary'); 27 | s.innerText = 'Details'; 28 | //
    Details
    29 | d.appendChild(s); 30 | // move the code into
    31 | p.replaceChild(d, code); 32 | d.appendChild(code); 33 | } 34 | })(); 35 | ``` 36 | -------------------------------------------------------------------------------- /examples/figures-side.Rmd: -------------------------------------------------------------------------------- 1 | ```{r, figures-side, fig.show="hold", out.width="50%"} 2 | par(mar = c(4, 4, .1, .1)) 3 | plot(cars) 4 | plot(mpg ~ hp, data = mtcars, pch = 19) 5 | ``` 6 | -------------------------------------------------------------------------------- /examples/fold-show.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hide all code blocks and show some initially 3 | output: 4 | html_document: 5 | code_folding: hide 6 | --- 7 | 8 | ```{r} 9 | 1 # code is hidden initially 10 | ``` 11 | 12 | ```{r class.source = 'fold-show'} 13 | 2 # code is shown initially 14 | ``` 15 | 16 | ```{r} 17 | 3 # also hidden 18 | ``` 19 | -------------------------------------------------------------------------------- /examples/font-color.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Color text with a Lua filter" 3 | output: 4 | html_document: 5 | pandoc_args: ["--lua-filter=color-text.lua"] 6 | pdf_document: 7 | pandoc_args: ["--lua-filter=color-text.lua"] 8 | keep_tex: true 9 | --- 10 | 11 | First, we define a Lua filter and write it to 12 | the file `color-text.lua`. 13 | 14 | ```{cat, engine.opts = list(file = "color-text.lua")} 15 | Span = function(el) 16 | color = el.attributes['color'] 17 | -- if no color attribute, return unchange 18 | if color == nil then return el end 19 | 20 | -- transform to 21 | if FORMAT:match 'html' then 22 | -- remove color attributes 23 | el.attributes['color'] = nil 24 | -- use style attribute instead 25 | el.attributes['style'] = 'color: ' .. color .. ';' 26 | -- return full span element 27 | return el 28 | elseif FORMAT:match 'latex' then 29 | -- remove color attributes 30 | el.attributes['color'] = nil 31 | -- encapsulate in latex code 32 | table.insert( 33 | el.content, 1, 34 | pandoc.RawInline('latex', '\\textcolor{'..color..'}{') 35 | ) 36 | table.insert( 37 | el.content, 38 | pandoc.RawInline('latex', '}') 39 | ) 40 | -- returns only span content 41 | return el.content 42 | else 43 | -- for other format return unchanged 44 | return el 45 | end 46 | end 47 | ``` 48 | 49 | Now we can test the filter with some text in brackets with 50 | the `color` attribute, e.g., 51 | 52 | > Roses are [red and **bold**]{color="red"} and 53 | > violets are [blue]{color="blue"}. 54 | -------------------------------------------------------------------------------- /examples/generate-content.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Generate content programmatically 3 | --- 4 | 5 | With the chunk option `results = 'asis'`, you can 6 | write out text as raw Markdown content, which can 7 | also be mixed with plots. 8 | 9 | ```{r, mtcars-plots, results='asis'} 10 | for (i in names(mtcars)) { 11 | cat('\n\n# Summary of the variable `', i, '`\n\n') 12 | x <- mtcars[, i] 13 | if (length(unique(x)) <= 6) { 14 | cat('`', i, '` is a categorical variable.\n\n') 15 | plot(table(x), xlab = i, ylab = 'Frequency', lwd = 10) 16 | } else { 17 | cat('Histogram for the continuous variable `', i, '`.\n\n') 18 | hist(x, xlab = i, main = '') 19 | } 20 | } 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /examples/global-device.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Using a global graphical device to record plots" 3 | --- 4 | 5 | First, turn on a global graphical device: 6 | 7 | ```{r, include=FALSE} 8 | knitr::opts_knit$set(global.device = TRUE) 9 | ``` 10 | 11 | Draw a plot: 12 | 13 | ```{r} 14 | par(mar = c(4, 4, 0.1, 0.1)) 15 | plot(cars) 16 | ``` 17 | 18 | Add a line to the plot in the previous code chunk: 19 | 20 | ```{r} 21 | fit <- lm(dist ~ speed, data = cars) 22 | abline(fit) 23 | ``` 24 | 25 | No longer use the global device: 26 | 27 | ```{r, include=FALSE} 28 | knitr::opts_knit$set(global.device = FALSE) 29 | ``` 30 | 31 | Draw another plot: 32 | 33 | ```{r} 34 | plot(pressure, type = 'b') 35 | ``` 36 | -------------------------------------------------------------------------------- /examples/hook-html5.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Output figures in `
    ` tags 3 | output: html_document 4 | --- 5 | 6 | Given a plot file path `x` and a figure caption in the chunk 7 | option `options$fig.cap`, we want to write the plot in the 8 | HTML5 tag in this form: 9 | 10 | ```html 11 |
    12 | CAPTION 13 |
    CAPTION
    14 |
    15 | ``` 16 | 17 | Now we redefine the `plot` hook (only when the output format 18 | is HTML): 19 | 20 | ```{r} 21 | if (knitr::is_html_output()) knitr::knit_hooks$set( 22 | plot = function(x, options) { 23 | cap <- options$fig.cap # figure caption 24 | tags <- htmltools::tags 25 | as.character(tags$figure( 26 | tags$img(src = x, alt = cap), 27 | tags$figcaption(cap) 28 | )) 29 | } 30 | ) 31 | ``` 32 | 33 | The plot from the code chunk below will be placed in the 34 | `
    ` tag: 35 | 36 | ```{r, fig.cap='A scatterplot for the cars data.'} 37 | par(mar = c(4.5, 4.5, .2, .2)) 38 | plot(cars, pch = 19, col = 'red') 39 | ``` 40 | 41 | We add some CSS styles to "see" the `
    ` and 42 | `
    ` tags better (the `figure` has a dashed 43 | border, and the caption has a light pink background): 44 | 45 | ```{css, echo=FALSE} 46 | figure { 47 | border: 2px dashed red; 48 | margin: 1em 0; 49 | } 50 | figcaption { 51 | padding: .5em; 52 | background: lightpink; 53 | font-size: 1.3em; 54 | font-variant: small-caps; 55 | } 56 | ``` 57 | 58 | -------------------------------------------------------------------------------- /examples/hook-number.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Add line numbers to source code 3 | --- 4 | 5 | We set up a `source` hook to add line numbers to the source 6 | code. The numbers appear in comments at the end of each line. 7 | 8 | ```{r, include=FALSE} 9 | local({ 10 | hook_source <- knitr::knit_hooks$get('source') 11 | knitr::knit_hooks$set(source = function(x, options) { 12 | x <- xfun::split_lines(x) 13 | n <- nchar(x, 'width') 14 | i <- seq_along(x) # line numbers 15 | n <- n + nchar(i) 16 | s <- strrep(' ', max(n) - n) 17 | x <- paste(x, s, ' # ', i, sep = '', collapse = '\n') 18 | hook_source(x, options) 19 | }) 20 | }) 21 | ``` 22 | 23 | Now we can test the new hook. When you knit this document, you 24 | will see line numbers in trailing comments. 25 | 26 | ```{r} 27 | if (TRUE) { 28 | x <- 1:10 29 | x + 1 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /examples/hook-scroll.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Scrollable code blocks 3 | output: 4 | html_document: 5 | highlight: tango 6 | --- 7 | 8 | We set up an `output` hook to add a `style` attribute to the 9 | text output when the chunk option `max.height` is set. 10 | 11 | ```{r, include=FALSE} 12 | options(width = 60) 13 | local({ 14 | hook_output <- knitr::knit_hooks$get('output') 15 | knitr::knit_hooks$set(output = function(x, options) { 16 | if (!is.null(options$max.height)) options$attr.output <- c( 17 | options$attr.output, 18 | sprintf('style="max-height: %s;"', options$max.height) 19 | ) 20 | hook_output(x, options) 21 | }) 22 | }) 23 | ``` 24 | 25 | Without the `max.height` option, you will see the full output, 26 | e.g., 27 | 28 | ```{r} 29 | 1:100 30 | ``` 31 | 32 | Now we set `max.height` to `100px`. You will see a scrollbar 33 | in the text output because its height is larger than 100px. 34 | 35 | ```{r, max.height='100px'} 36 | 1:100 37 | ``` 38 | 39 | Essentially the `max.height` option is converted to the 40 | `attr.output` option. It works even if the `attr.output` 41 | option is present, i.e., it will not override the 42 | `attr.output` option, e.g., we show line numbers on the left 43 | side of the text output via the `.numberLines` attribute: 44 | 45 | ```{r, max.height='100px', attr.output='.numberLines'} 46 | 1:100 47 | ``` 48 | -------------------------------------------------------------------------------- /examples/hook-secret.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using the `source` hook to hide certain lines of code 3 | --- 4 | 5 | First, we set up a `source` hook to exclude the lines of code 6 | that contain the string `# SECRET!!` at the end. 7 | 8 | ```{r, include=FALSE} 9 | local({ 10 | hook_source <- knitr::knit_hooks$get('source') 11 | knitr::knit_hooks$set(source = function(x, options) { 12 | x <- x[!grepl('# SECRET!!$', x)] 13 | hook_source(x, options) 14 | }) 15 | }) 16 | ``` 17 | 18 | Now we can test the new hook. When you knit this document, you 19 | will not see the lines with the special comment `# SECRET!!`. 20 | 21 | ```{r} 22 | 1 + 1 # normal code to be displayed 23 | 24 | # please use your real username and password 25 | auth <- httr::authenticate("user", "passwd") 26 | auth <- httr::authenticate("yihui", "horsebattery") # SECRET!! 27 | httr::GET("http://httpbin.org/basic-auth/user/passwd", auth) 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/html-logo.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Title" 3 | author: "Author" 4 | output: html_document 5 | --- 6 | 7 | 8 | ```{r, echo=FALSE} 9 | htmltools::img(src = knitr::image_uri(file.path(R.home("doc"), "html", "logo.jpg")), 10 | alt = 'logo', 11 | style = 'position:absolute; top:0; right:0; padding:10px;') 12 | ``` 13 | 14 | --- 15 | 16 | # 1. Header 17 | 18 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. -------------------------------------------------------------------------------- /examples/html-scroll.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Scrollable code blocks 3 | output: html_document 4 | --- 5 | 6 | ```{css, echo=FALSE} 7 | pre { 8 | max-height: 300px; 9 | overflow-y: auto; 10 | } 11 | 12 | pre[class] { 13 | max-height: 100px; 14 | } 15 | ``` 16 | 17 | We have defined some CSS rules to limit the height of 18 | code blocks. Now we can test if these rules work on code 19 | blocks and text output: 20 | 21 | ```{r} 22 | # pretend that we have a lot of code in this chunk 23 | if (1 + 1 == 2) { 24 | # of course that is true 25 | print(mtcars) 26 | # we just printed a lengthy data set 27 | } 28 | ``` 29 | 30 | Next we add rules for a new class `scroll-100` to limit 31 | the height to 100px, and add the class to the output of 32 | a code chunk via the chunk option `class.output`: 33 | 34 | ```{css, echo=FALSE} 35 | .scroll-100 { 36 | max-height: 100px; 37 | overflow-y: auto; 38 | background-color: inherit; 39 | } 40 | ``` 41 | 42 | ```{r, class.output="scroll-100"} 43 | print(mtcars) 44 | ``` 45 | -------------------------------------------------------------------------------- /examples/html-tabs.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Use tabs to organize content 3 | output: html_document 4 | --- 5 | 6 | You can turn parallel sections to tabs in `html_document` output. 7 | 8 | ## Results {.tabset} 9 | 10 | ### Plots 11 | 12 | We show a scatter plot in this section. 13 | 14 | ```{r, fig.dim=c(5, 3)} 15 | par(mar = c(4, 4, .5, .1)) 16 | plot(mpg ~ hp, data = mtcars, pch = 19) 17 | ``` 18 | 19 | ### Tables 20 | 21 | We show the data in this tab. 22 | 23 | ```{r} 24 | head(mtcars) 25 | ``` 26 | -------------------------------------------------------------------------------- /examples/knitr-foldable-class.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hide CSS code chunk 3 | output: 4 | html_document: 5 | code_folding: hide 6 | --- 7 | 8 | This is a CSS chunk using the `css` chunk engine, and it can't be hidden even if 9 | `code_folding = 'hide'`. 10 | 11 | ```{css} 12 | pre { 13 | background-color: lightblue; 14 | } 15 | ``` 16 | 17 | But it can if you add the `foldable` class to the chunk source code. 18 | 19 | ```{css, class.source = 'foldable'} 20 | pre.foldable { 21 | background-color: lightgreen; 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /examples/knitr-hide.Rmd: -------------------------------------------------------------------------------- 1 | Hide source code: 2 | 3 | ```{r, echo=FALSE} 4 | 1 + 1 5 | ``` 6 | 7 | Hide text output (you can also use `results = FALSE`): 8 | 9 | ```{r, results='hide'} 10 | print("You will not see the text output.") 11 | ``` 12 | 13 | Hide messages: 14 | 15 | ```{r, message=FALSE} 16 | message("You will not see the message.") 17 | ``` 18 | 19 | Hide warning messages: 20 | 21 | ```{r, warning=FALSE} 22 | # this will generate a warning but it will be suppressed 23 | 1:2 + 1:3 24 | ``` 25 | 26 | Hide plots: 27 | 28 | ```{r, fig.show='hide'} 29 | plot(cars) 30 | ``` 31 | 32 | Note that the plot will be generated in the above chunk. It is 33 | just not displayed in the output. 34 | -------------------------------------------------------------------------------- /examples/knitr.Rhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A minimal knitr example in HTML 5 | 6 | 7 | 10 | 11 |

    This is a minimal example that shows 12 | how knitr works with pure HTML 13 | pages.

    14 | 15 |

    Boring stuff as usual:

    16 | 17 | 24 | 25 |

    We can also produce plots (centered by the 26 | option fig.align='center'):

    27 | 28 | 31 | 32 |

    Errors, messages and warnings can be put into 33 | divs with different classes:

    34 | 35 | 40 | 41 |

    Well, everything seems to be working. Let's ask R what is 42 | the value of π? Of course it is .

    43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/knitr.Rnw: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage[T1]{fontenc} 3 | 4 | \begin{document} 5 | 6 | Here is a code chunk. 7 | 8 | <>= 9 | 1 + 1 10 | par(mar = c(4, 4, .2, .2)) 11 | plot(rnorm(100)) 12 | @ 13 | 14 | You can also write inline expressions, e.g. $\pi=\Sexpr{pi}$, 15 | and \Sexpr{1.9910214e28} is a big number. 16 | 17 | \end{document} 18 | -------------------------------------------------------------------------------- /examples/latex-animation.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Animations in PDF 3 | output: 4 | pdf_document: 5 | extra_dependencies: animate 6 | --- 7 | 8 | The animation below can only be viewed in Acrobat Reader. 9 | 10 | ```{r, fig.show='animate'} 11 | for (i in 1:2) { 12 | pie(c(i %% 2, 6), col = c('red', 'yellow'), labels = NA) 13 | } 14 | ``` 15 | -------------------------------------------------------------------------------- /examples/latex-logo.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Adding a Logo to LaTeX Title 3 | author: Michael Harper 4 | date: December 7th, 2018 5 | output: pdf_document 6 | header-includes: 7 | - \usepackage{titling} 8 | - \pretitle{\begin{center} 9 | \includegraphics[width=2in,height=2in]{logo.jpg}\LARGE\\} 10 | - \posttitle{\end{center}} 11 | --- 12 | 13 | 15 | 16 | \newpage 17 | 18 | This is your report. 19 | 20 | ```{r, include=FALSE} 21 | # copy the R logo to the current directory 22 | file.copy(file.path(R.home("doc"), "html", "logo.jpg"), '.') 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /examples/latex-subfig.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: 3 | pdf_document: 4 | extra_dependencies: "subfig" 5 | --- 6 | 7 | ```{r fig-sub, fig.dim=c(5, 3), fig.cap='Multiple plots from a single code chunk.', fig.subcap=c('A boring scatterplot showing numbers from 1 to 10.', 'Another scatterplot for the cars data, with solid points.', 'A boxplot of the iris data.'), out.width='50%', fig.ncol=2, fig.align='center'} 8 | par(mar = c(4, 4, .1, .1)) 9 | plot(1:10) 10 | plot(cars, pch = 19) 11 | boxplot(Sepal.Width ~ Species, data = iris, horizontal = TRUE, col = 'gray', notch = TRUE) 12 | ``` 13 | -------------------------------------------------------------------------------- /examples/latex-tiny.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Render a table in a tiny environment 3 | output: 4 | pdf_document: default 5 | html_document: default 6 | --- 7 | 8 | ```{r, setup, include=FALSE} 9 | knitr::opts_chunk$set(echo = FALSE) 10 | options(knitr.table.format = function() { 11 | if (knitr::is_latex_output()) 'latex' else 'pandoc' 12 | }) 13 | ``` 14 | 15 | The LaTeX environment `tiny` is only generated for LaTeX output. 16 | 17 | ```{r, include=knitr::is_latex_output()} 18 | knitr::asis_output('\n\n\\begin{tiny}') 19 | ``` 20 | 21 | ```{r} 22 | knitr::kable(mtcars) 23 | ``` 24 | 25 | ```{r, include=knitr::is_latex_output()} 26 | knitr::asis_output('\\end{tiny}\n\n') 27 | ``` 28 | 29 | By comparison, below is the table with the normal font size. 30 | 31 | ```{r} 32 | knitr::kable(mtcars) 33 | ``` 34 | -------------------------------------------------------------------------------- /examples/multicol-html.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: html_document 3 | --- 4 | 5 | :::: {style="display: flex;"} 6 | 7 | ::: {} 8 | Here is the **first** Div. 9 | 10 | ```{r} 11 | str(iris) 12 | ``` 13 | ::: 14 | 15 | ::: {} 16 | And this block will be put on the right: 17 | 18 | ```{r} 19 | plot(iris[, -5]) 20 | ``` 21 | ::: 22 | 23 | :::: 24 | -------------------------------------------------------------------------------- /examples/multicol.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: 3 | html_document: 4 | css: columns.css 5 | pdf_document: 6 | keep_tex: true 7 | includes: 8 | in_header: columns.tex 9 | beamer_presentation: 10 | keep_tex: true 11 | includes: 12 | in_header: columns.tex 13 | --- 14 | 15 | # Two columns 16 | 17 | Below is a Div containing three child Divs side by side. The Div 18 | in the middle is empty, just to add more space between the left 19 | and right Divs. 20 | 21 | :::::: {.cols data-latex=""} 22 | 23 | ::: {.col data-latex="{0.55\textwidth}"} 24 | ```{r, echo=FALSE, fig.width=5, fig.height=4} 25 | par(mar = c(4, 4, .2, .1)) 26 | plot(cars, pch = 19) 27 | ``` 28 | ::: 29 | 30 | ::: {.col data-latex="{0.05\textwidth}"} 31 | \ 32 | 34 | ::: 35 | 36 | ::: {.col data-latex="{0.4\textwidth}"} 37 | The figure on the left-hand side shows the `cars` data. 38 | 39 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do 40 | eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut 41 | enim ad minim veniam, quis nostrud exercitation ullamco laboris 42 | nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor 43 | in reprehenderit in voluptate velit esse cillum dolore eu fugiat 44 | nulla pariatur. 45 | ::: 46 | :::::: 47 | -------------------------------------------------------------------------------- /examples/officer.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Style text with officedown 3 | output: 4 | officedown::rdocx_document: default 5 | --- 6 | 7 | ```{r} 8 | library(officedown) 9 | library(officer) 10 | ft <- fp_text(color = 'red', bold = TRUE) 11 | ``` 12 | 13 | # Test 14 | 15 | The **officedown** package is 16 | `r ftext('awesome', ft)`! 17 | -------------------------------------------------------------------------------- /examples/print-method.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Use a custom `knit_print` method to print data frames 3 | --- 4 | 5 | First, we define a `knit_print` method, and register it: 6 | 7 | ```{r} 8 | knit_print.data.frame = function(x, ...) { 9 | res = paste(c("", "", knitr::kable(x)), collapse = "\n") 10 | knitr::asis_output(res) 11 | } 12 | 13 | registerS3method( 14 | "knit_print", "data.frame", knit_print.data.frame, 15 | envir = asNamespace("knitr") 16 | ) 17 | ``` 18 | 19 | Now we can test this custom printing method on data frames. 20 | Note that you no longer need to call `knitr::kable()` 21 | explicitly. 22 | 23 | ```{r} 24 | head(iris) 25 | ``` 26 | 27 | ```{r} 28 | head(mtcars) 29 | ``` 30 | -------------------------------------------------------------------------------- /examples/purl.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Use `purl()` to extract R code 3 | --- 4 | 5 | The function `knitr::purl()` extracts R code chunks from 6 | a **knitr** document and save the code to an R script. 7 | 8 | Below is a simple chunk: 9 | 10 | ```{r, simple, echo=TRUE} 11 | 1 + 1 12 | ``` 13 | 14 | Inline R expressions like `r 2 * pi` are ignored by default. 15 | 16 | If you do not want certain code chunks to be extracted, 17 | you can set the chunk option `purl = FALSE`, e.g., 18 | 19 | ```{r, ignored, purl=FALSE} 20 | x = rnorm(1000) 21 | ``` 22 | -------------------------------------------------------------------------------- /examples/python.Rmd: -------------------------------------------------------------------------------- 1 | ```{r, setup} 2 | library(reticulate) 3 | ``` 4 | 5 | Create a variable `x` in the Python session: 6 | 7 | ```{python} 8 | x = [1, 2, 3] 9 | ``` 10 | 11 | Access the Python variable `x` in an R code chunk: 12 | 13 | ```{r} 14 | py$x 15 | ``` 16 | 17 | Create a new variable `y` in the Python session using R, 18 | and pass a data frame to `y`: 19 | 20 | ```{r} 21 | py$y <- head(cars) 22 | ``` 23 | 24 | Print the variable `y` in Python: 25 | 26 | ```{python} 27 | print(y) 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/rgl-3d.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Embed 3D plots with rgl 3 | output: html_document 4 | --- 5 | 6 | Set up a hook to save **rgl** plots: 7 | 8 | ```{r, setup} 9 | library(rgl) 10 | knitr::knit_hooks$set(webgl = hook_webgl) 11 | ``` 12 | 13 | See if it works for this 3D plot after we enable the hook 14 | via the chunk option `webgl = TRUE`: 15 | 16 | ```{r, test-rgl, webgl=TRUE} 17 | x <- sort(rnorm(1000)) 18 | y <- rnorm(1000) 19 | z <- rnorm(1000) + atan2(x,y) 20 | plot3d(x, y, z, col = rainbow(1000)) 21 | ``` 22 | -------------------------------------------------------------------------------- /examples/spin.R: -------------------------------------------------------------------------------- 1 | #' --- 2 | #' title: "A report generated from a pure R script" 3 | #' output: 4 | #' pdf_document: 5 | #' keep_tex: true 6 | #' --- 7 | #' 8 | #' This is a report generated by `knitr::spin()`. 9 | #' 10 | #' Let's try some **knitr** options: 11 | 12 | #+ echo=FALSE, fig.width=7 13 | # This is a normal R comment. 14 | plot(cars) 15 | 16 | #' Now write an inline value. We know the value of $\pi$ is 17 | {{ pi }} 18 | #' . 19 | #' 20 | #' Finally please note that all roxygen comments are 21 | #' optional. You do not need chunk options, either, 22 | #' unless you want more control over the output 23 | #' elements such as the size of plots. 24 | 25 | # /* Write comments between /* and */ like C comments: 26 | Sys.sleep(60) 27 | # */ 28 | -------------------------------------------------------------------------------- /examples/tidy-opts.Rmd: -------------------------------------------------------------------------------- 1 | ```{r, tidy=TRUE, tidy.opts=list(arrow=TRUE, indent=2)} 2 | # messy R code... 3 | 1+ 1 4 | x=1:10#some users prefer '<-' as the assignment operator 5 | if(TRUE){ 6 | print('Hello world!') # indent by 2 spaces 7 | } 8 | ``` 9 | -------------------------------------------------------------------------------- /examples/tidy-width.Rmd: -------------------------------------------------------------------------------- 1 | ```{r, tidy=TRUE, tidy.opts=list(width.cutoff=50)} 2 | # a long expression 3 | 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+ 4 | 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 5 | ``` 6 | -------------------------------------------------------------------------------- /examples/wrap-text.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Wrap wide lines in code chunks and output 3 | geometry: margin=2in 4 | fontsize: 12pt 5 | output: 6 | pdf_document: 7 | pandoc_args: --listings 8 | includes: 9 | in_header: listings-settings.tex 10 | --- 11 | 12 | ```{cat, engine.opts=list(file='listings-settings.tex')} 13 | \lstset{ 14 | breaklines=true, 15 | basicstyle=\ttfamily 16 | } 17 | ``` 18 | 19 | You can use the **listings** package to wrap very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long text. 20 | 21 | Wrap source code: 22 | 23 | ```{r} 24 | x <- 'This is a very very very very very very very very very very very very long string.' 25 | ``` 26 | 27 | Wrap text output: 28 | 29 | ```{r} 30 | options(width = 200) 31 | 1:100 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /images/bookdown-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/bookdown-project.png -------------------------------------------------------------------------------- /images/bookdown-ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/bookdown-ref.png -------------------------------------------------------------------------------- /images/caution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/caution.png -------------------------------------------------------------------------------- /images/chunk-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/chunk-bg.png -------------------------------------------------------------------------------- /images/chunk-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/chunk-border.png -------------------------------------------------------------------------------- /images/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/cover.png -------------------------------------------------------------------------------- /images/details-closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/details-closed.png -------------------------------------------------------------------------------- /images/details-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/details-open.png -------------------------------------------------------------------------------- /images/diagram-params.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/diagram-params.jpg -------------------------------------------------------------------------------- /images/diagram-profit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/diagram-profit.jpg -------------------------------------------------------------------------------- /images/hook-html5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/hook-html5.png -------------------------------------------------------------------------------- /images/hook-scroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/hook-scroll.png -------------------------------------------------------------------------------- /images/html-scroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/html-scroll.png -------------------------------------------------------------------------------- /images/html-tabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/html-tabs.png -------------------------------------------------------------------------------- /images/important.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/important.png -------------------------------------------------------------------------------- /images/knit-wd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/knit-wd.png -------------------------------------------------------------------------------- /images/knitr-workflow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/knitr-workflow.jpg -------------------------------------------------------------------------------- /images/latex-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/latex-logo.png -------------------------------------------------------------------------------- /images/latex-subfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/latex-subfig.png -------------------------------------------------------------------------------- /images/multicol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/multicol.png -------------------------------------------------------------------------------- /images/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/note.png -------------------------------------------------------------------------------- /images/package-vignette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/package-vignette.png -------------------------------------------------------------------------------- /images/params-shiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/params-shiny.png -------------------------------------------------------------------------------- /images/rgl-3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/rgl-3d.png -------------------------------------------------------------------------------- /images/rmd-containers.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/rmd-containers.jpg -------------------------------------------------------------------------------- /images/rmd-relative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/rmd-relative.png -------------------------------------------------------------------------------- /images/rmd-wd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/rmd-wd.png -------------------------------------------------------------------------------- /images/striped-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/striped-table.png -------------------------------------------------------------------------------- /images/tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/tip.png -------------------------------------------------------------------------------- /images/trackdown1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/trackdown1.png -------------------------------------------------------------------------------- /images/trackdown2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/trackdown2.png -------------------------------------------------------------------------------- /images/trackdown3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/trackdown3.png -------------------------------------------------------------------------------- /images/unnumbered-sections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/unnumbered-sections.png -------------------------------------------------------------------------------- /images/visual-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/visual-edit.png -------------------------------------------------------------------------------- /images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/warning.png -------------------------------------------------------------------------------- /images/word-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/word-table.png -------------------------------------------------------------------------------- /images/word-template-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/word-template-1.png -------------------------------------------------------------------------------- /images/word-template-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/word-template-2.png -------------------------------------------------------------------------------- /images/workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/workflow.png -------------------------------------------------------------------------------- /images/wrap-listings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/wrap-listings.png -------------------------------------------------------------------------------- /images/wrap-none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/rmarkdown-cookbook/92cd49338d4cfa4e587fddb19ae8aa37350e61b0/images/wrap-none.png -------------------------------------------------------------------------------- /index.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "R Markdown Cookbook" 3 | author: "Yihui Xie, Christophe Dervieux, Emily Riederer" 4 | date: "`r Sys.Date()`" 5 | bibliography: 6 | - packages.bib 7 | - literature.bib 8 | biblio-style: apalike 9 | description: This book showcases short, practical examples of lesser-known tips and tricks to helps users get the most out of these tools. After reading this book, you will understand how R Markdown documents are transformed from plain text and how you may customize nearly every step of this processing. For example, you will learn how to dynamically create content from R code, reference code in other documents or chunks, control the formatting with customer templates, fine-tune how your code is processed, and incorporate multiple languages into your analysis. 10 | documentclass: krantz 11 | link-citations: yes 12 | colorlinks: yes 13 | graphics: yes 14 | lot: yes 15 | lof: yes 16 | fontsize: 11pt 17 | site: bookdown::bookdown_site 18 | github-repo: yihui/rmarkdown-cookbook 19 | url: 'https\://bookdown.org/yihui/rmarkdown-cookbook/' 20 | cover-image: images/cover.png 21 | --- 22 | 23 | ```{r setup, include=FALSE} 24 | set.seed(0728) 25 | 26 | knitr::opts_chunk$set(tidy = TRUE, webshot = "webshot2") 27 | 28 | if (knitr::is_html_output()) { 29 | # ignore percentage widths for HTML output, unless they are used for multiple 30 | # figures side by side 31 | knitr::opts_hooks$set(out.width = function(options) { 32 | if (options$fig.show != 'hold' && grepl('%$', options$out.width)) 33 | options$out.width = NULL 34 | options 35 | }) 36 | } 37 | 38 | options(bookdown.post.latex = function(x) { 39 | # substitute nonbreaking spaces in \texttt{} with normal spaces 40 | m = gregexpr('\\\\texttt\\{[^}]+}', x) 41 | regmatches(x, m) = lapply(regmatches(x, m), function(z) { 42 | gsub('\\\\ ', ' ', z) 43 | }) 44 | # only build a skeleton for the online version 45 | if (Sys.getenv('BOOKDOWN_FULL_PDF', '') == 'false') return(bookdown:::strip_latex_body( 46 | x, '\nThis PDF is only a skeleton. Please either read the free online HTML version, or purchase a hard-copy of this book.\n' 47 | )) 48 | 49 | # fix syntax highlighting: 50 | # \FunctionTok{tufte:}\AttributeTok{:tufte_html: default} -> 51 | # \FunctionTok{tufte::tufte_html:}\AttributeTok{ default} 52 | x = gsub('(\\\\AttributeTok\\{[^:]+:)(})(\\\\FunctionTok\\{)(:[^:]+:)', '\\1\\4\\2\\3', x) 53 | if (length(i <- grep('^\\\\begin\\{longtable\\}', x)) == 0) return(x) 54 | i1 = bookdown:::next_nearest(i, which(x == '\\toprule')) 55 | i2 = bookdown:::next_nearest(i, which(x == '\\endfirsthead')) 56 | x[i1 - 1] = paste0(x[i1 - 1], '\n\\begin{tabular}{', gsub('[^lcr]', '', gsub('.*\\[]', '', x[i])), '}') 57 | x[i] = '\\begin{table}' 58 | x[x == '\\end{longtable}'] = '\\end{tabular}\n\\end{table}' 59 | x[x == '\\endhead'] = '' 60 | x = x[-unlist(mapply(seq, i1, i2, SIMPLIFY = FALSE))] 61 | x 62 | }) 63 | ``` 64 | 65 | # Preface {-} 66 | 67 | ```{asis, echo=!knitr::is_latex_output()} 68 | ::: {.infobox .caution} 69 | **Note**: This book is published by [Chapman & Hall/CRC](https://www.routledge.com/p/book/9780367563837). The online version of this book is free to read here (thanks to Chapman & Hall/CRC), and licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-nc-sa/4.0/). If you have any feedback, please feel free to [file an issue on GitHub](https://github.com/yihui/rmarkdown-cookbook/issues/new). Thank you! 70 | ::: 71 | 72 |

    The R Markdown cookbook cover

    73 | ``` 74 | 75 | R Markdown is a powerful tool for combining analysis and reporting into the same document. Since the birth of the **rmarkdown** package [@R-rmarkdown] in early 2014, R Markdown has grown substantially from a package that supports a few output formats, to an extensive and diverse ecosystem that supports the creation of books, blogs, scientific articles, websites, and even resumes. 76 | 77 | There is a wealth of guidance that has been written over the past few years, and the book [*R Markdown: The Definitive Guide*](https://bookdown.org/yihui/rmarkdown/) [@rmarkdown2018] provides a detailed reference on the built-in R Markdown output formats of the **rmarkdown** package, as well as several other extension packages. However, we have received comments from our readers and publisher that it would be beneficial to provide more practical and relatively short examples to show the interesting and useful usage of R Markdown, because it can be daunting to find out how to achieve a certain task from the aforementioned reference book (put another way, that book is too dry to read). As a result, this cookbook was born. 78 | 79 | Despite the existence of the official documentation, R Markdown users often seek help on Stack Overflow, a popular Q&A forum. At the time of writing, there are more than 6,000 questions with [the `r-markdown` tag.](https://stackoverflow.com/questions/tagged/r-markdown) This huge number means that the use of the forum can be difficult if you do not have a specific problem to search for. Therefore, it may be hard for you to realize all possible things that you could do with R Markdown or how to do them. This book aims to draw together popular posts from Stack Overflow and other online resources (such as blog posts or tutorials) to provide up-to-date solutions for everyday queries that users commonly make. In fact, to help us make decisions on the potential topics to cover in this book, the second author of this book, Christophe, has built an R Markdown dashboard to scrape Stack Overflow daily for the most popular posts. Hopefully, our cookbook can become more useful by including recipes from these popular posts. 80 | 81 | This book is designed to provide a range of examples on how to extend the functionality of your R Markdown documents. As a cookbook, this guide is recommended to new and intermediate R Markdown users who desire to enhance the efficiency of using R Markdown and also explore the power of R Markdown. 82 | 83 | ## How to read this book {-} 84 | 85 | It is recommended that readers have a basic understanding of R Markdown. [Chapter 2](https://bookdown.org/yihui/rmarkdown/basics.html) of *R Markdown: The Definitive Guide* [@rmarkdown2018] provides an overview of the basics of R Markdown and is recommended background reading for any new users of R Markdown. For example, we did not cover Markdown syntax in this book, and expect readers to learn Markdown elsewhere. In particular, we strongly recommend that you go through [the full manual of Pandoc](https://pandoc.org/MANUAL.html) at least once. The manual is quite lengthy, but it is also a gold mine. You do not have to remember everything, but it will be very helpful if you are aware of the possible features of Markdown. [For countless times, I have seen](https://yihui.org/en/2018/11/hard-markdown/) people fail to write verbatim code blocks that contain three backticks, or list items that contain child elements. Without fully reading the Markdown syntax in the manual, perhaps you will never know or understand the rule "`N + 1` outer backticks for `N` inner backticks" or "indent properly to indicate child elements." 86 | 87 | We do not intend to provide a full technical reference for R Markdown in this cookbook. This cookbook aims to supplement, instead of replace, the existing literature. Therefore, readers may explore the following books if they want to seek further information: 88 | 89 | - *R Markdown: The Definitive Guide* [@rmarkdown2018], the technical reference for all R Markdown output formats in the **rmarkdown** package and several other extension packages. 90 | 91 | - Part V ("Communicate") of *R for Data Science* [@wickham2016]. This part is less technical than the above "Definitive Guide," and hence may be a gentler introduction to R Markdown. 92 | 93 | - *Dynamic Documents with R and knitr* [@knitr2015] provides a thorough introduction to the **knitr** package [@R-knitr] (note that R Markdown is only one of the document formats that **knitr** supports). If you want to read a shorter version, you may find Karl Broman's minimal tutorial ["knitr in a knutshell"](https://kbroman.org/knitr_knutshell/) helpful. 94 | 95 | - *bookdown: Authoring Books and Technical Documents with R Markdown* [@bookdown2016] is a short book as the official documentation of the **bookdown** package [@R-bookdown], which is designed to simplify the creation of long-format documents in R Markdown. 96 | 97 | - *blogdown: Creating Websites with R Markdown* [@blogdown2017] introduces how to create websites in R Markdown with the **blogdown** package [@R-blogdown]. 98 | 99 | Where relevant, this book provides references to these existing resources. By the way, the official R Markdown website also contains a lot of resources that you may find helpful: https://rmarkdown.rstudio.com. 100 | 101 | You do not need to read this book in a particular order. Later chapters are not necessarily more challenging than previous chapters. The chapters and sections that we consider to be more advanced than others are marked with an asterisk (`*`) in their titles. It may be most efficient to read this book when you have some specific tasks in mind that you want to do with R Markdown, otherwise you can thumb through the table of contents and see if you are interested in any particular parts. We have tried to make each section and example as self-contained as possible, so you do not have to go back and forth among different parts of this book. In some cases, cross-referencing is unavoidable, and we will refer you to the background knowledge required to understand a certain example. 102 | 103 | If you want to try the examples by yourself, the full source code of this book and examples are freely provided on GitHub at https://github.com/yihui/rmarkdown-cookbook. If you are reading the electronic version of this book, you may also just copy and paste the examples from the pages and run them in your favorite editor. 104 | 105 | ## Structure of the book {-} 106 | 107 | The book is broken down into small "recipes" that aim to demonstrate a single concept at a time. Chapter \@ref(installation) provides instructions on how to install the necessary software tools. Chapter \@ref(conceptual-overview) gives a conceptual overview of R Markdown. Chapter \@ref(basics) introduces the basic components of R Markdown, and how to convert between R Markdown documents and R scripts. Chapter \@ref(document-elements) tells you how to generate certain document elements, such as page breaks, bibliographies, numbered figures, animations, diagrams, etc. Chapter \@ref(formatting) shows how to format content, such as adjusting the figure size and alignment. Chapter \@ref(latex-output) introduces tips and tricks for those who only want LaTeX/PDF output. Similarly, Chapter \@ref(html-output) is for HTML users, and Chapter \@ref(word) is for Word users. If you want to produce output documents in multiple output formats (which is often tricky), you may find Chapter \@ref(multi-formats) useful. Chapter \@ref(tables) is, to be honest, my least favorite chapter, but I know a lot of users really want to learn how to produce tables. I'm not an expert on fancy tables, but hope you will at least find the list of packages there helpful. Chapter \@ref(chunk-options) shows some applications of **knitr**'s chunk options that you may not know. Chapter \@ref(output-hooks) and Chapter \@ref(chunk-hooks) are a little advanced, but should also be very useful because they show you the great power of being able to control **knitr**'s output and behavior with custom hook functions. Chapter \@ref(knitr-misc) introduces a variety of **knitr** tricks. Chapter \@ref(other-languages) shows examples of using other languages in R Markdown, so you know R Markdown is not only for R. It also teaches you how to make **knitr** work with a new language that has not been supported yet. Chapter \@ref(managing-projects) introduces tips on managing projects related to R Markdown. Chapter \@ref(workflow) presents some tips on enhancing your workflow. 108 | 109 | The recipes in this book are usually independent of each other, so you can pick up any one to read if you do not have a specific goal in mind. 110 | 111 | ## Software information and conventions {#software-info .unnumbered} 112 | 113 | The basic R session information when compiling this book is as follows: 114 | 115 | ```{r tidy=FALSE} 116 | xfun::session_info(c( 117 | 'bookdown', 'knitr', 'rmarkdown', 'xfun' 118 | ), dependencies = FALSE) 119 | ``` 120 | 121 | We do not add prompts (`>` and `+`) to R source code in this book, and we comment out the text output with two hashes `##` by default, as you can see from the R session information above. This is for your convenience when you want to copy and run the code (the text output will be ignored since it is commented out). Package names are in bold text (e.g., **rmarkdown**), and inline code and filenames are formatted in a typewriter font (e.g., `knitr::knit('foo.Rmd')`). Function names are followed by parentheses (e.g., `blogdown::serve_site()`). The double-colon operator `::` means accessing an object from a package. 122 | 123 | "Rmd" is the filename extension of R Markdown files, and also an abbreviation of R Markdown in this book. 124 | 125 | ## Acknowledgments {-} 126 | 127 | As usual, first I want to thank my employer RStudio for giving me the freedom to work on this book. Since I started working on it, my weekly meeting time with my manager, Tareef Kawaf, was first reduced from 15 minutes to 5 minutes, and then the meetings were just canceled. I have heard from several friends that they have too many unbearable meetings in their institutions, which waste a lot of their time. In terms of managing distractions, one of them recently lamented, "You may be able to mute Slack for five minutes, but can you possibly mute it for _a whole day_?" "Of course, I can!" I told her. I can probably mute it for a whole month if I like. Do not get me wrong---I do not mean Tareef or my colleagues are distractions. I only mean how much freedom they can offer me. 128 | 129 | I came up with the idea of writing this cookbook after I published the *R Markdown Definitive Guide*, but ideas are often cheap. It is the execution that is hard and expensive. If it were not for [Michael Harper's](http://mikeyharper.uk) initial pushing, I would never have started working on it seriously. Christophe Dervieux has always been around whenever I need help. He used his R and R Markdown skills to build a dashboard (with the **flexdashboard** package) to guide me to the potentially interesting and useful topics to write on. Meanwhile, he has also helped me in numerous other GitHub issues, so I could have more time for writing the book, instead of spending whole days on wrestling with bug reports that do not have minimal reproducible examples attached. Similarly, several people have been helping with answering R Markdown questions on Stack Overflow, including Martin Schmelzer, Marcel Schilling, and Ralf Stubner. Perhaps it was not their intention to save me time, but their effort did save me a lot of time. Recently, Johannes Friedrich also came to my attention on Stack Overflow, after a few times when I opened a new Stack Overflow question only to find it already answered by him. 130 | 131 | David Keyes saved my life in Section \@ref(table-other), since he had written [a wonderful blog post](https://rfortherestofus.com/2019/11/how-to-make-beautiful-tables-in-r/) to introduce several R packages to create tables, with which I was not very familiar. Other online materials that have helped me a lot include Holtz Yan's [post on some R Markdown tips,](https://holtzy.github.io/Pimp-my-rmd/) Nicholas Tierney's book _[R Markdown for Scientists](https://rmd4sci.njtierney.com)_, Maëlle Salmon's [R Markdown course,](https://github.com/maelle/rmd_course_isglobal) Jennifer Thompson's [R Markdown course,](https://github.com/jenniferthompson/RepResearchRMarkdown) Emi Tanaka's [R Markdown workshop,](https://github.com/emitanaka/combine2019) Alison Hill's [R Markdown workshop](https://arm.rbind.io) (co-taught with me), and Alison Hill and Emi Tanaka's [R Markdown workshop.](https://ysc-rmarkdown.netlify.app) 132 | 133 | Many people have made contributions in the GitHub repository of this book by either sending pull requests or filing issues, including Maria Bekker-Nielsen Dunbar, Nathan Eastwood, Johannes Friedrich, Krishnakumar Gopalakrishnan, Xiangyun Huang, Florian Kohrt, Romain Lesur, Jiaxiang Li, Song Li, Ulrik Lyngs, Matt Small, Jake Stephen, Atsushi Yasumoto, Hao Zhu, and John Zobolas. The marvelous cover artwork of this book [was designed by Allison Horst](https://github.com/yihui/rmarkdown-cookbook/issues/180) and the full cover was finalized by Kevin Craig. 134 | 135 | The original idea of this book was partially motivated from a remote talk that I delivered to the RaukR Summer School in 2018, in which I introduced some lesser known features of **knitr**. The audience seemed to like those short introductions of **knitr** features, which were like recipes. I'd like to thank the organizers of the summer school, including Marcin Kierczak and Sebastian Dilorenzo, for inviting me. I have given similar talks later at Genentech and [DahShu.](http://dahshu.org) I want to thank Michael Lawrence and Yuqing Zhang for the invitations, as well as the audience of these talks, for their feedback. Paul Johnson published a very helpful critique of our book _R Markdown: The Definitive Guide_ in the journal _The American Statistician_ in 2020. He complained that the book lacked in-depth examples, therefore the definitive guide was not definitive enough. I truly appreciate and agree with his comments. I hope this new (cook)book can fill the gap. 136 | 137 | This is the fifth book that I have published with my editor John Kimmel. It has always been a pleasure to work with him and the team at Chapman & Hall/CRC. I'm excited every time John tells me the new success of **bookdown** as it is more widely adopted by other authors. I feel honored to hear from John that Suzanne Lassandro, the production editor of my previous books, still tried hard to help with this book even though she has many other responsibilities and rarely works directly with authors now. Suzanne and our proofreader (Rebecca Condit) managed to identify "only" 377 issues in our first draft. Apparently, I was too optimistic [when I wondered last time](https://bookdown.org/yihui/rmarkdown/acknowledgments.html) if I would have less than 30 issues in my next book. The LaTeX expert Shashi Kumar helped us fix a thorny LaTeX issue, which was our last obstacle before the PDF could be printed. 138 | 139 | John reached out to several reviewers for their feedback on the manuscript. Eventually we received nine great reviews. One of them was so great that we could not help inviting her to co-author this book! It was a lot of work to deal with the nine reviews, but it was definitely worth the effort. I'd like to thank all these reviewers for their helpful feedback, including Carl Boettiger, John Blischak, Sharla Gelfand, Johannes Friedrich, Atsushi Yasumoto, and other anonymous reviewers. 140 | 141 | I worked on the last part of this book in the vacant house (without Internet!) of my good old neighbors, Dong Guo and Qian Jia, after they moved to another city. I'm grateful to them for letting me use their house as my temporary office to finish up the book when I felt rather exhausted and needed a quiet environment. It was sad to say goodbye to them. To me, this book, finished in their house, will also be associated with some of my fond memories of this family, including their parents and lovely little daughter. 142 | 143 | Lastly, I will definitely not miss this unique opportunity to thank my two little "super helpful co-workers" (5 and 3) at home during the COVID-19 pandemic, without whom I could have published this book five months earlier. Now I miss the teachers at their daycare center (Small Miracle) and feel daycare centers are perhaps not that expensive... 144 | 145 | ::: {.flushright data-latex=""} 146 | Yihui Xie 147 | Elkhorn, Nebraska 148 | ::: 149 | -------------------------------------------------------------------------------- /latex/after_body.tex: -------------------------------------------------------------------------------- 1 | \printindex 2 | -------------------------------------------------------------------------------- /latex/before_body.tex: -------------------------------------------------------------------------------- 1 | \cleardoublepage\newpage\thispagestyle{empty}\null 2 | \cleardoublepage\newpage\thispagestyle{empty}\null 3 | %\cleardoublepage\newpage 4 | \thispagestyle{empty} 5 | \begin{large} 6 | To the most amazing cooks in my life, Xie Shaobai and Si Zhinan. 7 | \begin{flushright} 8 | ---Yihui 9 | \end{flushright} 10 | 11 | \bigskip 12 | 13 | To my supporting wife, Caroline, and our lovely newborn, Axel. 14 | \begin{flushright} 15 | ---Christophe 16 | \end{flushright} 17 | 18 | \bigskip 19 | 20 | To my mom, who taught me the joy of life-long learning. 21 | \begin{flushright} 22 | ---Emily 23 | \end{flushright} 24 | \end{large} 25 | 26 | \setlength{\abovedisplayskip}{-5pt} 27 | \setlength{\abovedisplayshortskip}{-5pt} 28 | -------------------------------------------------------------------------------- /latex/preamble.tex: -------------------------------------------------------------------------------- 1 | \usepackage{booktabs} 2 | \usepackage{longtable} 3 | \usepackage[bf,singlelinecheck=off]{caption} 4 | 5 | \usepackage{Alegreya} 6 | \usepackage[scale=.8]{sourcecodepro} 7 | 8 | \usepackage{framed,color} 9 | \definecolor{shadecolor}{RGB}{248,248,248} 10 | 11 | \renewcommand{\textfraction}{0.05} 12 | \renewcommand{\topfraction}{0.8} 13 | \renewcommand{\bottomfraction}{0.8} 14 | \renewcommand{\floatpagefraction}{0.75} 15 | 16 | \renewenvironment{quote}{\begin{VF}}{\end{VF}} 17 | \usepackage{hyperref} %require for pandoc 2.18 and above 18 | \let\oldhref\href 19 | \renewcommand{\href}[2]{#2\footnote{\url{#1}}} 20 | 21 | \ifxetex 22 | \usepackage{letltxmacro} 23 | \setlength{\XeTeXLinkMargin}{1pt} 24 | \LetLtxMacro\SavedIncludeGraphics\includegraphics 25 | \def\includegraphics#1#{% #1 catches optional stuff (star/opt. arg.) 26 | \IncludeGraphicsAux{#1}% 27 | }% 28 | \newcommand*{\IncludeGraphicsAux}[2]{% 29 | \XeTeXLinkBox{% 30 | \SavedIncludeGraphics#1{#2}% 31 | }% 32 | }% 33 | \fi 34 | 35 | \usepackage{makeidx} 36 | \makeindex 37 | 38 | \urlstyle{tt} 39 | 40 | \usepackage{amsthm} 41 | \makeatletter 42 | \def\thm@space@setup{% 43 | \thm@preskip=8pt plus 2pt minus 4pt 44 | \thm@postskip=\thm@preskip 45 | } 46 | \makeatother 47 | 48 | \frontmatter 49 | -------------------------------------------------------------------------------- /literature.bib: -------------------------------------------------------------------------------- 1 | @book{wickham2016, 2 | title={R for Data Science}, 3 | author={Wickham, Hadley and Grolemund, Garrett}, 4 | year={2016}, 5 | publisher={O'Reilly Media, Inc.} 6 | } 7 | -------------------------------------------------------------------------------- /renv/.gitignore: -------------------------------------------------------------------------------- 1 | library/ 2 | local/ 3 | lock/ 4 | python/ 5 | staging/ 6 | -------------------------------------------------------------------------------- /renv/activate.R: -------------------------------------------------------------------------------- 1 | 2 | local({ 3 | 4 | # the requested version of renv 5 | version <- "0.13.2-50" 6 | 7 | # the project directory 8 | project <- getwd() 9 | 10 | # avoid recursion 11 | if (!is.na(Sys.getenv("RENV_R_INITIALIZING", unset = NA))) 12 | return(invisible(TRUE)) 13 | 14 | # signal that we're loading renv during R startup 15 | Sys.setenv("RENV_R_INITIALIZING" = "true") 16 | on.exit(Sys.unsetenv("RENV_R_INITIALIZING"), add = TRUE) 17 | 18 | # signal that we've consented to use renv 19 | options(renv.consent = TRUE) 20 | 21 | # load the 'utils' package eagerly -- this ensures that renv shims, which 22 | # mask 'utils' packages, will come first on the search path 23 | library(utils, lib.loc = .Library) 24 | 25 | # check to see if renv has already been loaded 26 | if ("renv" %in% loadedNamespaces()) { 27 | 28 | # if renv has already been loaded, and it's the requested version of renv, 29 | # nothing to do 30 | spec <- .getNamespaceInfo(.getNamespace("renv"), "spec") 31 | if (identical(spec[["version"]], version)) 32 | return(invisible(TRUE)) 33 | 34 | # otherwise, unload and attempt to load the correct version of renv 35 | unloadNamespace("renv") 36 | 37 | } 38 | 39 | # load bootstrap tools 40 | bootstrap <- function(version, library) { 41 | 42 | # attempt to download renv 43 | tarball <- tryCatch(renv_bootstrap_download(version), error = identity) 44 | if (inherits(tarball, "error")) 45 | stop("failed to download renv ", version) 46 | 47 | # now attempt to install 48 | status <- tryCatch(renv_bootstrap_install(version, tarball, library), error = identity) 49 | if (inherits(status, "error")) 50 | stop("failed to install renv ", version) 51 | 52 | } 53 | 54 | renv_bootstrap_tests_running <- function() { 55 | getOption("renv.tests.running", default = FALSE) 56 | } 57 | 58 | renv_bootstrap_repos <- function() { 59 | 60 | # check for repos override 61 | repos <- Sys.getenv("RENV_CONFIG_REPOS_OVERRIDE", unset = NA) 62 | if (!is.na(repos)) 63 | return(repos) 64 | 65 | # if we're testing, re-use the test repositories 66 | if (renv_bootstrap_tests_running()) 67 | return(getOption("renv.tests.repos")) 68 | 69 | # retrieve current repos 70 | repos <- getOption("repos") 71 | 72 | # ensure @CRAN@ entries are resolved 73 | repos[repos == "@CRAN@"] <- getOption( 74 | "renv.repos.cran", 75 | "https://cloud.r-project.org" 76 | ) 77 | 78 | # add in renv.bootstrap.repos if set 79 | default <- c(FALLBACK = "https://cloud.r-project.org") 80 | extra <- getOption("renv.bootstrap.repos", default = default) 81 | repos <- c(repos, extra) 82 | 83 | # remove duplicates that might've snuck in 84 | dupes <- duplicated(repos) | duplicated(names(repos)) 85 | repos[!dupes] 86 | 87 | } 88 | 89 | renv_bootstrap_download <- function(version) { 90 | 91 | # if the renv version number has 4 components, assume it must 92 | # be retrieved via github 93 | nv <- numeric_version(version) 94 | components <- unclass(nv)[[1]] 95 | 96 | methods <- if (length(components) == 4L) { 97 | list( 98 | renv_bootstrap_download_github 99 | ) 100 | } else { 101 | list( 102 | renv_bootstrap_download_cran_latest, 103 | renv_bootstrap_download_cran_archive 104 | ) 105 | } 106 | 107 | for (method in methods) { 108 | path <- tryCatch(method(version), error = identity) 109 | if (is.character(path) && file.exists(path)) 110 | return(path) 111 | } 112 | 113 | stop("failed to download renv ", version) 114 | 115 | } 116 | 117 | renv_bootstrap_download_impl <- function(url, destfile) { 118 | 119 | mode <- "wb" 120 | 121 | # https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17715 122 | fixup <- 123 | Sys.info()[["sysname"]] == "Windows" && 124 | substring(url, 1L, 5L) == "file:" 125 | 126 | if (fixup) 127 | mode <- "w+b" 128 | 129 | utils::download.file( 130 | url = url, 131 | destfile = destfile, 132 | mode = mode, 133 | quiet = TRUE 134 | ) 135 | 136 | } 137 | 138 | renv_bootstrap_download_cran_latest <- function(version) { 139 | 140 | spec <- renv_bootstrap_download_cran_latest_find(version) 141 | 142 | message("* Downloading renv ", version, " ... ", appendLF = FALSE) 143 | 144 | type <- spec$type 145 | repos <- spec$repos 146 | 147 | info <- tryCatch( 148 | utils::download.packages( 149 | pkgs = "renv", 150 | destdir = tempdir(), 151 | repos = repos, 152 | type = type, 153 | quiet = TRUE 154 | ), 155 | condition = identity 156 | ) 157 | 158 | if (inherits(info, "condition")) { 159 | message("FAILED") 160 | return(FALSE) 161 | } 162 | 163 | # report success and return 164 | message("OK (downloaded ", type, ")") 165 | info[1, 2] 166 | 167 | } 168 | 169 | renv_bootstrap_download_cran_latest_find <- function(version) { 170 | 171 | # check whether binaries are supported on this system 172 | binary <- 173 | getOption("renv.bootstrap.binary", default = TRUE) && 174 | !identical(.Platform$pkgType, "source") && 175 | !identical(getOption("pkgType"), "source") && 176 | Sys.info()[["sysname"]] %in% c("Darwin", "Windows") 177 | 178 | types <- c(if (binary) "binary", "source") 179 | 180 | # iterate over types + repositories 181 | for (type in types) { 182 | for (repos in renv_bootstrap_repos()) { 183 | 184 | # retrieve package database 185 | db <- tryCatch( 186 | as.data.frame( 187 | utils::available.packages(type = type, repos = repos), 188 | stringsAsFactors = FALSE 189 | ), 190 | error = identity 191 | ) 192 | 193 | if (inherits(db, "error")) 194 | next 195 | 196 | # check for compatible entry 197 | entry <- db[db$Package %in% "renv" & db$Version %in% version, ] 198 | if (nrow(entry) == 0) 199 | next 200 | 201 | # found it; return spec to caller 202 | spec <- list(entry = entry, type = type, repos = repos) 203 | return(spec) 204 | 205 | } 206 | } 207 | 208 | # if we got here, we failed to find renv 209 | fmt <- "renv %s is not available from your declared package repositories" 210 | stop(sprintf(fmt, version)) 211 | 212 | } 213 | 214 | renv_bootstrap_download_cran_archive <- function(version) { 215 | 216 | name <- sprintf("renv_%s.tar.gz", version) 217 | repos <- renv_bootstrap_repos() 218 | urls <- file.path(repos, "src/contrib/Archive/renv", name) 219 | destfile <- file.path(tempdir(), name) 220 | 221 | message("* Downloading renv ", version, " ... ", appendLF = FALSE) 222 | 223 | for (url in urls) { 224 | 225 | status <- tryCatch( 226 | renv_bootstrap_download_impl(url, destfile), 227 | condition = identity 228 | ) 229 | 230 | if (identical(status, 0L)) { 231 | message("OK") 232 | return(destfile) 233 | } 234 | 235 | } 236 | 237 | message("FAILED") 238 | return(FALSE) 239 | 240 | } 241 | 242 | renv_bootstrap_download_github <- function(version) { 243 | 244 | enabled <- Sys.getenv("RENV_BOOTSTRAP_FROM_GITHUB", unset = "TRUE") 245 | if (!identical(enabled, "TRUE")) 246 | return(FALSE) 247 | 248 | # prepare download options 249 | pat <- Sys.getenv("GITHUB_PAT") 250 | if (nzchar(Sys.which("curl")) && nzchar(pat)) { 251 | fmt <- "--location --fail --header \"Authorization: token %s\"" 252 | extra <- sprintf(fmt, pat) 253 | saved <- options("download.file.method", "download.file.extra") 254 | options(download.file.method = "curl", download.file.extra = extra) 255 | on.exit(do.call(base::options, saved), add = TRUE) 256 | } else if (nzchar(Sys.which("wget")) && nzchar(pat)) { 257 | fmt <- "--header=\"Authorization: token %s\"" 258 | extra <- sprintf(fmt, pat) 259 | saved <- options("download.file.method", "download.file.extra") 260 | options(download.file.method = "wget", download.file.extra = extra) 261 | on.exit(do.call(base::options, saved), add = TRUE) 262 | } 263 | 264 | message("* Downloading renv ", version, " from GitHub ... ", appendLF = FALSE) 265 | 266 | url <- file.path("https://api.github.com/repos/rstudio/renv/tarball", version) 267 | name <- sprintf("renv_%s.tar.gz", version) 268 | destfile <- file.path(tempdir(), name) 269 | 270 | status <- tryCatch( 271 | renv_bootstrap_download_impl(url, destfile), 272 | condition = identity 273 | ) 274 | 275 | if (!identical(status, 0L)) { 276 | message("FAILED") 277 | return(FALSE) 278 | } 279 | 280 | message("OK") 281 | return(destfile) 282 | 283 | } 284 | 285 | renv_bootstrap_install <- function(version, tarball, library) { 286 | 287 | # attempt to install it into project library 288 | message("* Installing renv ", version, " ... ", appendLF = FALSE) 289 | dir.create(library, showWarnings = FALSE, recursive = TRUE) 290 | 291 | # invoke using system2 so we can capture and report output 292 | bin <- R.home("bin") 293 | exe <- if (Sys.info()[["sysname"]] == "Windows") "R.exe" else "R" 294 | r <- file.path(bin, exe) 295 | args <- c("--vanilla", "CMD", "INSTALL", "-l", shQuote(library), shQuote(tarball)) 296 | output <- system2(r, args, stdout = TRUE, stderr = TRUE) 297 | message("Done!") 298 | 299 | # check for successful install 300 | status <- attr(output, "status") 301 | if (is.numeric(status) && !identical(status, 0L)) { 302 | header <- "Error installing renv:" 303 | lines <- paste(rep.int("=", nchar(header)), collapse = "") 304 | text <- c(header, lines, output) 305 | writeLines(text, con = stderr()) 306 | } 307 | 308 | status 309 | 310 | } 311 | 312 | renv_bootstrap_platform_prefix <- function() { 313 | 314 | # construct version prefix 315 | version <- paste(R.version$major, R.version$minor, sep = ".") 316 | prefix <- paste("R", numeric_version(version)[1, 1:2], sep = "-") 317 | 318 | # include SVN revision for development versions of R 319 | # (to avoid sharing platform-specific artefacts with released versions of R) 320 | devel <- 321 | identical(R.version[["status"]], "Under development (unstable)") || 322 | identical(R.version[["nickname"]], "Unsuffered Consequences") 323 | 324 | if (devel) 325 | prefix <- paste(prefix, R.version[["svn rev"]], sep = "-r") 326 | 327 | # build list of path components 328 | components <- c(prefix, R.version$platform) 329 | 330 | # include prefix if provided by user 331 | prefix <- renv_bootstrap_platform_prefix_impl() 332 | if (!is.na(prefix) && nzchar(prefix)) 333 | components <- c(prefix, components) 334 | 335 | # build prefix 336 | paste(components, collapse = "/") 337 | 338 | } 339 | 340 | renv_bootstrap_platform_prefix_impl <- function() { 341 | 342 | # if an explicit prefix has been supplied, use it 343 | prefix <- Sys.getenv("RENV_PATHS_PREFIX", unset = NA) 344 | if (!is.na(prefix)) 345 | return(prefix) 346 | 347 | # if the user has requested an automatic prefix, generate it 348 | auto <- Sys.getenv("RENV_PATHS_PREFIX_AUTO", unset = NA) 349 | if (auto %in% c("TRUE", "True", "true", "1")) 350 | return(renv_bootstrap_platform_prefix_auto()) 351 | 352 | # empty string on failure 353 | "" 354 | 355 | } 356 | 357 | renv_bootstrap_platform_prefix_auto <- function() { 358 | 359 | prefix <- tryCatch(renv_bootstrap_platform_os(), error = identity) 360 | if (inherits(prefix, "error") || prefix %in% "unknown") { 361 | 362 | msg <- paste( 363 | "failed to infer current operating system", 364 | "please file a bug report at https://github.com/rstudio/renv/issues", 365 | sep = "; " 366 | ) 367 | 368 | warning(msg) 369 | 370 | } 371 | 372 | prefix 373 | 374 | } 375 | 376 | renv_bootstrap_platform_os <- function() { 377 | 378 | sysinfo <- Sys.info() 379 | sysname <- sysinfo[["sysname"]] 380 | 381 | # handle Windows + macOS up front 382 | if (sysname == "Windows") 383 | return("windows") 384 | else if (sysname == "Darwin") 385 | return("macos") 386 | 387 | # check for os-release files 388 | for (file in c("/etc/os-release", "/usr/lib/os-release")) 389 | if (file.exists(file)) 390 | return(renv_bootstrap_platform_os_via_os_release(file, sysinfo)) 391 | 392 | # check for redhat-release files 393 | if (file.exists("/etc/redhat-release")) 394 | return(renv_bootstrap_platform_os_via_redhat_release()) 395 | 396 | "unknown" 397 | 398 | } 399 | 400 | renv_bootstrap_platform_os_via_os_release <- function(file, sysinfo) { 401 | 402 | # read /etc/os-release 403 | release <- utils::read.table( 404 | file = file, 405 | sep = "=", 406 | quote = c("\"", "'"), 407 | col.names = c("Key", "Value"), 408 | comment.char = "#", 409 | stringsAsFactors = FALSE 410 | ) 411 | 412 | vars <- as.list(release$Value) 413 | names(vars) <- release$Key 414 | 415 | # get os name 416 | os <- tolower(sysinfo[["sysname"]]) 417 | 418 | # read id 419 | id <- "unknown" 420 | for (field in c("ID", "ID_LIKE")) { 421 | if (field %in% names(vars) && nzchar(vars[[field]])) { 422 | id <- vars[[field]] 423 | break 424 | } 425 | } 426 | 427 | # read version 428 | version <- "unknown" 429 | for (field in c("UBUNTU_CODENAME", "VERSION_CODENAME", "VERSION_ID", "BUILD_ID")) { 430 | if (field %in% names(vars) && nzchar(vars[[field]])) { 431 | version <- vars[[field]] 432 | break 433 | } 434 | } 435 | 436 | # join together 437 | paste(c(os, id, version), collapse = "-") 438 | 439 | } 440 | 441 | renv_bootstrap_platform_os_via_redhat_release <- function() { 442 | 443 | # read /etc/redhat-release 444 | contents <- readLines("/etc/redhat-release", warn = FALSE) 445 | 446 | # infer id 447 | id <- if (grepl("centos", contents, ignore.case = TRUE)) 448 | "centos" 449 | else if (grepl("redhat", contents, ignore.case = TRUE)) 450 | "redhat" 451 | else 452 | "unknown" 453 | 454 | # try to find a version component (very hacky) 455 | version <- "unknown" 456 | 457 | parts <- strsplit(contents, "[[:space:]]")[[1L]] 458 | for (part in parts) { 459 | 460 | nv <- tryCatch(numeric_version(part), error = identity) 461 | if (inherits(nv, "error")) 462 | next 463 | 464 | version <- nv[1, 1] 465 | break 466 | 467 | } 468 | 469 | paste(c("linux", id, version), collapse = "-") 470 | 471 | } 472 | 473 | renv_bootstrap_library_root_name <- function(project) { 474 | 475 | # use project name as-is if requested 476 | asis <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT_ASIS", unset = "FALSE") 477 | if (asis) 478 | return(basename(project)) 479 | 480 | # otherwise, disambiguate based on project's path 481 | id <- substring(renv_bootstrap_hash_text(project), 1L, 8L) 482 | paste(basename(project), id, sep = "-") 483 | 484 | } 485 | 486 | renv_bootstrap_library_root <- function(project) { 487 | 488 | path <- Sys.getenv("RENV_PATHS_LIBRARY", unset = NA) 489 | if (!is.na(path)) 490 | return(path) 491 | 492 | path <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT", unset = NA) 493 | if (!is.na(path)) { 494 | name <- renv_bootstrap_library_root_name(project) 495 | return(file.path(path, name)) 496 | } 497 | 498 | prefix <- renv_bootstrap_profile_prefix() 499 | paste(c(project, prefix, "renv/library"), collapse = "/") 500 | 501 | } 502 | 503 | renv_bootstrap_validate_version <- function(version) { 504 | 505 | loadedversion <- utils::packageDescription("renv", fields = "Version") 506 | if (version == loadedversion) 507 | return(TRUE) 508 | 509 | # assume four-component versions are from GitHub; three-component 510 | # versions are from CRAN 511 | components <- strsplit(loadedversion, "[.-]")[[1]] 512 | remote <- if (length(components) == 4L) 513 | paste("rstudio/renv", loadedversion, sep = "@") 514 | else 515 | paste("renv", loadedversion, sep = "@") 516 | 517 | fmt <- paste( 518 | "renv %1$s was loaded from project library, but this project is configured to use renv %2$s.", 519 | "Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.", 520 | "Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.", 521 | sep = "\n" 522 | ) 523 | 524 | msg <- sprintf(fmt, loadedversion, version, remote) 525 | warning(msg, call. = FALSE) 526 | 527 | FALSE 528 | 529 | } 530 | 531 | renv_bootstrap_hash_text <- function(text) { 532 | 533 | hashfile <- tempfile("renv-hash-") 534 | on.exit(unlink(hashfile), add = TRUE) 535 | 536 | writeLines(text, con = hashfile) 537 | tools::md5sum(hashfile) 538 | 539 | } 540 | 541 | renv_bootstrap_load <- function(project, libpath, version) { 542 | 543 | # try to load renv from the project library 544 | if (!requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) 545 | return(FALSE) 546 | 547 | # warn if the version of renv loaded does not match 548 | renv_bootstrap_validate_version(version) 549 | 550 | # load the project 551 | renv::load(project) 552 | 553 | TRUE 554 | 555 | } 556 | 557 | renv_bootstrap_profile_load <- function(project) { 558 | 559 | # if RENV_PROFILE is already set, just use that 560 | profile <- Sys.getenv("RENV_PROFILE", unset = NA) 561 | if (!is.na(profile) && nzchar(profile)) 562 | return(profile) 563 | 564 | # check for a profile file (nothing to do if it doesn't exist) 565 | path <- file.path(project, "renv/local/profile") 566 | if (!file.exists(path)) 567 | return(NULL) 568 | 569 | # read the profile, and set it if it exists 570 | contents <- readLines(path, warn = FALSE) 571 | if (length(contents) == 0L) 572 | return(NULL) 573 | 574 | # set RENV_PROFILE 575 | profile <- contents[[1L]] 576 | if (nzchar(profile)) 577 | Sys.setenv(RENV_PROFILE = profile) 578 | 579 | profile 580 | 581 | } 582 | 583 | renv_bootstrap_profile_prefix <- function() { 584 | profile <- renv_bootstrap_profile_get() 585 | if (!is.null(profile)) 586 | return(file.path("renv/profiles", profile)) 587 | } 588 | 589 | renv_bootstrap_profile_get <- function() { 590 | profile <- Sys.getenv("RENV_PROFILE", unset = "") 591 | renv_bootstrap_profile_normalize(profile) 592 | } 593 | 594 | renv_bootstrap_profile_set <- function(profile) { 595 | profile <- renv_bootstrap_profile_normalize(profile) 596 | if (is.null(profile)) 597 | Sys.unsetenv("RENV_PROFILE") 598 | else 599 | Sys.setenv(RENV_PROFILE = profile) 600 | } 601 | 602 | renv_bootstrap_profile_normalize <- function(profile) { 603 | 604 | if (is.null(profile) || profile %in% c("", "default")) 605 | return(NULL) 606 | 607 | profile 608 | 609 | } 610 | 611 | # load the renv profile, if any 612 | renv_bootstrap_profile_load(project) 613 | 614 | # construct path to library root 615 | root <- renv_bootstrap_library_root(project) 616 | 617 | # construct library prefix for platform 618 | prefix <- renv_bootstrap_platform_prefix() 619 | 620 | # construct full libpath 621 | libpath <- file.path(root, prefix) 622 | 623 | # attempt to load 624 | if (renv_bootstrap_load(project, libpath, version)) 625 | return(TRUE) 626 | 627 | # load failed; inform user we're about to bootstrap 628 | prefix <- paste("# Bootstrapping renv", version) 629 | postfix <- paste(rep.int("-", 77L - nchar(prefix)), collapse = "") 630 | header <- paste(prefix, postfix) 631 | message(header) 632 | 633 | # perform bootstrap 634 | bootstrap(version, libpath) 635 | 636 | # exit early if we're just testing bootstrap 637 | if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA))) 638 | return(TRUE) 639 | 640 | # try again to load 641 | if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) { 642 | message("* Successfully installed and loaded renv ", version, ".") 643 | return(renv::load()) 644 | } 645 | 646 | # failed to download or load renv; warn the user 647 | msg <- c( 648 | "Failed to find an renv installation: the project will not be loaded.", 649 | "Use `renv::activate()` to re-initialize the project." 650 | ) 651 | 652 | warning(paste(msg, collapse = "\n"), call. = FALSE) 653 | 654 | }) 655 | -------------------------------------------------------------------------------- /renv/settings.dcf: -------------------------------------------------------------------------------- 1 | external.libraries: 2 | ignored.packages: 3 | package.dependency.fields: Imports, Depends, LinkingTo 4 | r.version: 5 | snapshot.type: explicit 6 | use.cache: TRUE 7 | vcs.ignore.library: TRUE 8 | vcs.ignore.local: TRUE 9 | -------------------------------------------------------------------------------- /rmarkdown-cookbook.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Makefile 19 | -------------------------------------------------------------------------------- /utils.R: -------------------------------------------------------------------------------- 1 | import_example <- function(file, lang = xfun::file_ext(file)) { 2 | x = xfun::read_utf8(file.path("examples", file)) 3 | lang = tolower(lang) 4 | if (nchar(lang) > 1) { 5 | lang = sub('^r', '', lang) 6 | if (lang == 'nw') lang = 'tex' 7 | } 8 | knitr::asis_output(paste(c(sprintf("````%s", lang), x, "````"), collapse = '\n')) 9 | } 10 | --------------------------------------------------------------------------------