class: center, middle, inverse, title-slide .title[ # A tour of {knitr} engines: {knitr} not only knits R ] .author[ ### Christophe Dervieux ] .date[ ### userR!2022 - 22nd of June ] --- # {knitr} in the the R Markdown workflow .center.f3[.ba.b--blue.pa2.br4[`knitr::knit()`] + Pandoc (+ LaTeX for PDF) = `rmarkdown::render()`] .fl.w-50.pa2[ .center[![:scale 100%, R Markdown wizard illustration by Alison Horst](rmarkdown_wizards.png)] .source-fig.center[Source: https://github.com/allisonhorst/stats-illustrations] ] .fl.w-50.pa2[ .center[![:scale 80%, Diagram showing the different step of rendering, using knitr first from Rmd to md, then Pandoc from md to output format like HTML, DOC and PDF (using LaTeX)](https://bookdown.org/yihui/rmarkdown-cookbook/images/workflow.png)] .source-fig.center[ source: [R Markdown Cookbook](https://bookdown.org/yihui/rmarkdown-cookbook) ] ] ??? First a bit of context regarding **knitr** in the R Markdown process. R Markdown is a tool to create some publication in several output formats by writing text and executable code together in the same document. **knitr** is the tool in the chain that will bring the literate programming feature by sewing source code and result with other text in the knitting process. --- # What is a {knitr} engine ? .subtitle[Using R as example] .fl.w-50.pa2[ ````markdown ```{r setup, include = FALSE} knitr::opts_chunk$set(echo = FALSE) ``` ```` ] .fl.w-50.pa2[ ````markdown ```{<engine> <label>, <keys = values>} <code content> ``` ```` ] .fl.w-100[ * `<engine>` defines **how** the `<code content>` will be processed, * `<keys = values>` are **configurations** for the engine, * `<label>` is the name of the chunk</br>(equivalent to `label = <label>`). ] ??? Usually, R engine is used with **knitr** chunks to evaluate R code and showing source and outputs depending on providing options. Syntax looks this way with engine, a label and some option. An engine is the main component of **knitr** which will take code chunk content and process it so that source and results can be sewed in the document as text in the Markdown output. --- # There is more than just R ! .subtitle[Meet the other engines !] A subset of available engines ```r names(knitr::knit_engines$get()) ``` ``` ## [1] "awk" "bash" "coffee" "gawk" "groovy" "haskell" ## [7] "lein" "mysql" "node" "octave" "perl" "psql" ## [13] "Rscript" "ruby" "sas" "scala" "sed" "sh" ## [19] "stata" "zsh" "asis" "asy" "block" "block2" ## [25] "bslib" "c" "cat" "cc" "comment" "css" ## [31] "ditaa" "dot" "embed" "exec" "fortran" "fortran95" ## [37] "go" "highlight" "js" "julia" "python" "R" ## [43] "Rcpp" "sass" "scss" "sql" "stan" "targets" ## [49] "tikz" "verbatim" ``` .center[Let's take a tour! 🚌] ??? This principle is used for various engines and not just evaluating code chunk content with R. Let's take a tour ! --- layout: true # Processing chunk content as-is --- .subtitle[Meet the `verbatim` engine] .center.f2[Include chunk content in a code block] .fl.w-50.pa2[ .ba[ .title-file[.Rmd before knitting] `````markdown Let's show an example of Rmd file content: *````{verbatim, lang = "markdown"} We can output arbitrary content **verbatim**. ```{r} 1 + 1 ``` The content can contain inline code like `r pi * 5^2`, too. ```` ````` ] ] .fl.w-50.pa2[ .ba[ .title-file[.md after knitting] `````markdown Let's show an example of Rmd file content: *````markdown We can output arbitrary content **verbatim**. ```{r} 1 + 1 ``` The content can contain inline code like `r pi * 5^2`, too. ```` ````` ] ] --- .subtitle[Meet the `embed` engine] .center.f2[Include file content in a code block] .ba[ .title-file[.Rmd before knitting] ````markdown *```{embed, file = "macros.js"} ``` ```` ] .ba.mt2[ .title-file[.md after knitting] ````default *```js remark.macros.scale = function(w, alt="") { var url = this; return '<img src="' + url + '" style="width: ' + w + ';" alt="' + alt + '" />'; }; remark.macros.scaleh = function(h, alt="") { var url = this; return '<img src="' + url + '" style="height: ' + h + ';" alt="' + alt + '" />'; }; ``` ```` ] --- .subtitle[Look at the `asis` engine] .fl.w-60.pa2[ ````default ```{r} getRandomNumber <- function() { sample(1:6, 1) } ``` *```{asis, echo = getRandomNumber() == 4} According to https://xkcd.com/221/, we just generated a **true** random number! ``` ```` ] .fl.w-40.pa2[ ![](https://imgs.xkcd.com/comics/random_number.png) .source-fig.right[https://xkcd.com/221/] ] --- .subtitle[Look at the `asis` engine] .fl.w-50.pa2[ .f3[For `getRandomNumber() != 4`] ````default ```r getRandomNumber <- function() { sample(1:6, 1) } ``` ```` ] .fl.w-50.pa2[ .f3[For `getRandomNumber() == 4`] ````default ```r getRandomNumber <- function() { sample(1:6, 1) } ``` According to https://xkcd.com/221/, we just generated a **true** random number! ```` ] ??? https://bookdown.org/yihui/rmarkdown-cookbook/eng-asis.html --- layout: false # Adding dependencies .subtitle[Include CSS and JS easily in HTML] .fl.w-50.pa2[ ````markdown ```{css, echo=FALSE} /* Some CSS code that will will be included in HTML document a `<style>` tag. */ ``` ```` ] .fl.w-50.pa2[ ````markdown ```{js, echo=FALSE} // some JS code that will be included in HTML document in a`<script>` tag. ``` ```` ] .fl.w-100.pa2[ Useful to customize output directly from the Rmd file without external resource ] --- layout: true # Running other tools than R --- .subtitle[Some built-in support] .fl.w-50.pa2[ ````markdown ```{python} import os os.env ``` ```` ] .fl.w-50.pa2[ ````markdown ```{stata} sysuse auto summarize ``` ```` ] .fl.w-50.pa2[ ````markdown ```{bash} ls *.Rmd | head -n 5 ``` ```` ] .fl.w-50.pa2[ ````markdown ```{perl} $test = "jello world"; $test =~ s/j/h/; print $test ``` ```` ] .fl.w-100[ Chunk content is passed to the tools through `system2()` ] --- .subtitle[Extend using `exec` engine] Using `node` CLI (which require `.js` extension for scripts) ````markdown ```{exec, command='node', engine.opts = list(ext = ".js")} function Display(x) { console.log(`Your number is ${x}`); } Display(100); ``` ```` ````markdown ```javascript function Display(x) { console.log(`Your number is ${x}`); } Display(100); ``` ``` ## Your number is 100 ``` ```` More in [knitr-examples repo](https://github.com/yihui/knitr-examples): [`124-exec-engine.Rmd`](https://github.com/yihui/knitr-examples/blob/master/124-exec-engine.Rmd) ??? The `exec` engine allows to execute an arbitrary command on the code chunk content, optionally with arguments. If any, they are passed to `engine.opts` and they could be one of the following: The `exec` engine allows to execute an arbitrary command on the code chunk content, optionally with arguments. In your Rmd file --- layout: false # Working with interoperability .subtitle[More engines powered by other 📦] * Use the .color1[`sql`] engine to run queries using **DBI** on compatible .icon-link[databases [
](https://bookdown.org/yihui/rmarkdown/language-engines.html#sql)] * Use the .color1[`python`] engine with **reticulate** to work seemlessly with R and Python chunks .icon-link[together [
](https://rstudio.github.io/reticulate/articles/r_markdown.html)] * Use the .color1[`scss`] or .color1[`sass`] engine to process a chunk content with **sass** package to insert a CSS in .icon-link[HTML [
](https://rstudio.github.io/sass/articles/sass.html#rmarkdown)] * Use the .color1[`bslib`] engine to add rules to **bslib** themes withing .icon-link[Rmd [
](https://rstudio.github.io/bslib/articles/bslib.html#advanced)] ??? Some R packages are great to work with other tools like **DBI** for SQL queries, **reticulate** for working with Python from R, **sass** to work with SASS or **bslib** to customize Bootstrap website. **knitr** engines can be integrated with other Packages to bring more feature from within a Rmd document. --- layout: false # Extending {knitr} with custom engines .subtitle[Any package can provide custom way to process chunk content] * **glue** has a .color1[`glue`] engine to process chunk content as if passed to `glue::glue()` .icon-link[function [
](https://glue.tidyverse.org/articles/engines.html)] * **texPreview** has a .color1[`texpreview`] engine to render TeX snippet from code chunk, in non-LaTeX output .icon-link[document [
](https://yonicd.github.io/texPreview/articles/engine.html)] * **targets** offers a .color1[`targets`] engine so that literate programming can be used to create a **targets** workflow .icon-link[(Target Markdown) [
](https://books.ropensci.org/targets/markdown.html#target-markdown)] * **d3** has a .color1[`d3`] engine where chunk content will be processed with .icon-link[**r2d3** [
](https://bookdown.org/yihui/rmarkdown-cookbook/d3.html#d3)] ??? https://yonicd.github.io/texPreview/ https://books.ropensci.org/targets/markdown.html --- layout: true # Extending {knitr} with custom engines .subtitle[How to create a new engine ?] --- ```r knitr::knit_engines$set(foo = function(options) { # the source code is in options$code; just do # whatever you want with it }) ``` * Use `knit_engines$set()` to register by name * All knitr options are passed to the engine * Code chunk content is in `options$code` --- .fl.w-100.pa2[ This engine will take the chunk content and make it upper case. ] .fl.w-60.pa2[ .v-mid[ ```r knitr::knit_engines$set(upper = function(options) { code <- paste(options$code, collapse = "\n") # Allow to hide result if (options$results == 'hide') return() # Allow to prevent processing if (options$eval) {} toupper(code) } else { code } }) ``` ] ] .fl.w-40.pa2[ ````markdown ```{upper} Hello, **knitr** engines! ``` ```` .small.center[
] ```markdown HELLO, **KNITR** ENGINES! ``` ] --- .fl.w-100.pa2[A custom `py` engine to run `python`] .fl.w-60.pa2[ ```r knitr::knit_engines$set(py = function(options) { code <- paste(options$code, collapse = '\n') out <- system2( 'python', c('-c', shQuote(code)), stdout = TRUE ) knitr::engine_output(options, code, out) }) ``` ] .fl.w-40.pa2[ ````markdown ```{py} import os print(os.environ.get('USERNAME')) ``` ```` .small.center[
] ````markdown ```py import os print(os.environ.get('USERNAME')) ``` ``` ## chris ``` ```` ] ??? https://bookdown.org/yihui/rmarkdown-cookbook/custom-engine.html `knit_engines` is the way to register an engine by name for **knitr** to know it is available. Full set of `options` is passed and `options$code` contains the chunk content. Do what you need with that to process it and outputting a what will be sewed in the document. --- layout: false class: center, middle, annexe count: false # Thank you ! .f3[https://cderv.rbind.io/slides/user2022-knitr-engines] .f3[https://github.com/cderv/user2022-knitr-engines] .pull-left[ .center[
</br> [@cderv](https://github.com/cderv) ] ] .pull-right[ .center[
</br> [@chrisderv](https://twitter.com/chrisderv) ] ]