diff --git a/.Rbuildignore b/.Rbuildignore index 0a9fe26..db68a9a 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -11,3 +11,4 @@ ^inst/JSS4145_prescreened$ ^inst/jss$ ^inst/r-medicine-2020$ +^\.github$ diff --git a/.github/.gitignore b/.github/.gitignore new file mode 100644 index 0000000..2d19fc7 --- /dev/null +++ b/.github/.gitignore @@ -0,0 +1 @@ +*.html diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml new file mode 100644 index 0000000..fb4fc50 --- /dev/null +++ b/.github/workflows/R-CMD-check.yaml @@ -0,0 +1,107 @@ +# NOTE: This workflow is overkill for most R packages +# check-standard.yaml is likely a better choice +# usethis::use_github_action("check-standard") will install it. +# +# For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. +# https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + +name: R-CMD-check + +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: macOS-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: windows-latest, r: '3.6'} + - {os: ubuntu-18.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest", http-user-agent: "R/4.0.0 (ubuntu-18.04) R (4.0.0 x86_64-pc-linux-gnu x86_64 linux-gnu) on GitHub Actions" } + - {os: ubuntu-18.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest"} + - {os: ubuntu-18.04, r: 'oldrel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest"} + - {os: ubuntu-18.04, r: '3.5', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest"} + - {os: ubuntu-18.04, r: '3.4', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest"} + - {os: ubuntu-18.04, r: '3.3', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest"} + + env: + RSPM: ${{ matrix.config.rspm }} + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v2 + + - uses: r-lib/actions/setup-r@v1 + id: install-r + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + + - uses: r-lib/actions/setup-pandoc@v1 + + - name: Install pak and query dependencies + run: | + install.packages("pak", repos = "https://r-lib.github.io/p/pak/dev/") + saveRDS(pak::pkg_deps("local::.", dependencies = TRUE), ".github/r-depends.rds") + shell: Rscript {0} + + - name: Restore R package cache + uses: actions/cache@v2 + with: + path: | + ${{ env.R_LIBS_USER }}/* + !${{ env.R_LIBS_USER }}/pak + key: ${{ matrix.config.os }}-${{ steps.install-r.outputs.installed-r-version }}-1-${{ hashFiles('.github/r-depends.rds') }} + restore-keys: ${{ matrix.config.os }}-${{ steps.install-r.outputs.installed-r-version }}-1- + + - name: Install system dependencies + if: runner.os == 'Linux' + run: | + pak::local_system_requirements(execute = TRUE) + pak::pkg_system_requirements("rcmdcheck", execute = TRUE) + shell: Rscript {0} + + - name: Install dependencies + run: | + pak::local_install_dev_deps(upgrade = TRUE) + pak::pkg_install("rcmdcheck") + shell: Rscript {0} + + - name: Session info + run: | + options(width = 100) + pkgs <- installed.packages()[, "Package"] + sessioninfo::session_info(pkgs, include_base = TRUE) + shell: Rscript {0} + + - name: Check + env: + _R_CHECK_CRAN_INCOMING_: false + run: | + options(crayon.enabled = TRUE) + rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") + shell: Rscript {0} + + - name: Show testthat output + if: always() + run: find check -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + + - name: Upload check results + if: failure() + uses: actions/upload-artifact@main + with: + name: ${{ matrix.config.os }}-r${{ matrix.config.r }}-results + path: check diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4770f3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +inst/JSS4145_prescreened +inst/JSS4145_prescreened.zip +inst/r-medicine-2020 +inst/jss diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index eba8c65..0000000 --- a/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -# R for travis: see documentation at https://docs.travis-ci.com/user/languages/r - -language: R -cache: packages diff --git a/R/build-site.r b/R/build-site.r index d7ab9a2..64ec313 100644 --- a/R/build-site.r +++ b/R/build-site.r @@ -345,10 +345,6 @@ ld_create_doc <- ) if (ldb$cc_in_memory) { - if (is.null(data_dir) && is.null(cc_file_name)) { - data_dir <- "../data" - cc_file_name <- basename(tempfile(pattern = "data", fileext = ".rds")) - } if (is.null(data_dir)) { data_dir <- "../data" warning("Argument `data_dir` is not specified, ../data will be used.") diff --git a/R/chunk-writer.r b/R/chunk-writer.r index 1148b5d..00c1e79 100644 --- a/R/chunk-writer.r +++ b/R/chunk-writer.r @@ -251,26 +251,26 @@ ld_make_chunks.listdown <- function(ld, rmd_dir = ".") { " from directory ", rmd_dir) }) - if (is.character(cc_list)) { - cc_list <- tryCatch( - { - setwd(rmd_dir) - ret <- eval(parse(text = cc_list)) - setwd(wd) - ret - }, - error = function(e) { - setwd(wd) - stop("Can't evaluate", ld$load_cc_expr, "from directory", rmd_dir) - }) - cc_list <- eval(parse(text = cc_list)) - } +# if (is.character(cc_list)) { +# cc_list <- tryCatch( +# { +# setwd(rmd_dir) +# ret <- eval(parse(text = cc_list)) +# setwd(wd) +# ret +# }, +# error = function(e) { +# setwd(wd) +# stop("Can't evaluate", ld$load_cc_expr, "from directory", rmd_dir) +# }) +# cc_list <- eval(parse(text = cc_list)) +# } - if (inherits(ld$load_cc_expr, "call")) { - cc_list <- expr_to_string(ld$load_cc_expr) - } else { - stop("The load_cc_expr element should be a call object.") - } +# if (inherits(ld$load_cc_expr, "call")) { +# cc_list <- expr_to_string(ld$load_cc_expr) +# } else { +# stop("The load_cc_expr element should be a call object.") +# } ret_string <- "" if (length(ld$setup_expr)) { ret_string <- c(ret_string, diff --git a/README.md b/README.md index 108a465..4225751 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,52 @@ ---- -output: github_document ---- - - # listdown + [](https://cran.r-project.org/package=listdown) -[](https://travis-ci.org/kaneplusplus/listdown) -[](https://ci.appveyor.com/project/kaneplusplus/listdown) -[](https://codecov.io/gh/kaneplusplus/listdown) -[](https://cran.r-project.org/package=listdown) -[](https://travis-ci.com/kaneplusplus/listdown) +[](https://github.com/kaneplusplus/listdown/actions) -## Overview - -The {listdown} package provides functions to programmatically create R Markdown files from -named lists. It is intended for data analysis pipelines where the presentation of the results -is separated from their creation. For this use case, a data processing (or analysis) is performed -and the results are provided in a single named list, organized hierarchically. With the list and a {listdown} object a workflowr, pdf, word, or html page. List element names denote sections, subsections, -subsubsection, etc. and the list elements contain the data structure to be presented including -graphs and tables. The goal of the package is not to provide a finished, readable document. It is to provide a document with all tables and visualization that will appear (_computational_ components). This serves as a starting point from which a user can organize outputs, describe a study, discuss results, and provide conclusions (_narrative_ components). - -{listdown} provides a reproducible means for producing a document with specified computational components. It is most compatible with data analysis pipelines where the data format is fixed but the analyses are either being updated, which may affect narrative components including the result discussion and conclusion, or where the experiment is different, which affects all narrative components If the narrative components are not changing with the data being pushed through your analysis pipeline, then you may be better off writing the R Markdown code manually. +## Overview + +The {listdown} package provides functions to programmatically create R +Markdown files from named lists. It is intended for data analysis +pipelines where the presentation of the results is separated from their +creation. For this use case, a data processing (or analysis) is +performed and the results are provided in a single named list, organized +hierarchically. With the list and a {listdown} object a workflowr, pdf, +word, or html page. List element names denote sections, subsections, +subsubsection, etc. and the list elements contain the data structure to +be presented including graphs and tables. The goal of the package is not +to provide a finished, readable document. It is to provide a document +with all tables and visualization that will appear (*computational* +components). This serves as a starting point from which a user can +organize outputs, describe a study, discuss results, and provide +conclusions (*narrative* components). + +{listdown} provides a reproducible means for producing a document with +specified computational components. It is most compatible with data +analysis pipelines where the data format is fixed but the analyses are +either being updated, which may affect narrative components including +the result discussion and conclusion, or where the experiment is +different, which affects all narrative components If the narrative +components are not changing with the data being pushed through your +analysis pipeline, then you may be better off writing the R Markdown +code manually. ## Installation -You can install the released version of listdown from [CRAN](https://CRAN.R-project.org) with: +You can install the released version of listdown from +[CRAN](https://CRAN.R-project.org) with: ``` r install.packages("listdown") ``` -The development version of {listdown} can be installed from [GitHub](https://github.com/) with: +The development version of {listdown} can be installed from +[GitHub](https://github.com/) with: ``` r # install.packages("devtools") @@ -45,14 +55,15 @@ devtools::install_github("kaneplusplus/listdown") ## Example -As a toy example, suppose we would like to create an html document plotting Anscombe's quartet with -each plot having it's own section. To construct the document, we will need to two objects. The first -is a presentation list, whose names indicate section (or subsection) titles and whose elements are -the objects to present. The second is a `listdown` object, which describes how the object should -be rendered in the document. +As a toy example, suppose we would like to create an html document +plotting Anscombe’s quartet with each plot having it’s own section. To +construct the document, we will need to two objects. The first is a +presentation list, whose names indicate section (or subsection) titles +and whose elements are the objects to present. The second is a +`listdown` object, which describes how the object should be rendered in +the document. - -```r +``` r library(listdown) library(ggplot2) @@ -83,50 +94,47 @@ doc <- c( cat(paste(doc, collapse = "\n")) ``` + #> --- + #> title: Anscombe's Quartet + #> author: Francis Anscombe + #> date: '1973' + #> output: html_document + #> --- + #> + #> ```{r} + #> library(ggplot2) + #> + #> cc_list <- readRDS("pres-list.rds") + #> ``` + #> + #> # Linear + #> + #> ```{r} + #> cc_list$Linear + #> ``` + #> + #> # Non Linear + #> + #> ```{r} + #> cc_list$`Non Linear` + #> ``` + #> + #> # Outlier Vertical + #> + #> ```{r} + #> cc_list$`Outlier Vertical` + #> ``` + #> + #> # Outlier Horizontal + #> + #> ```{r} + #> cc_list$`Outlier Horizontal` + #> ``` + +The document can then be written to a file, rendered, and viewed with +the following code. -``` -#> --- -#> title: Anscombe's Quartet -#> author: Francis Anscombe -#> date: '1973' -#> output: html_document -#> --- -#> -#> ```{r} -#> library(ggplot2) -#> -#> cc_list <- readRDS("pres-list.rds") -#> ``` -#> -#> # Linear -#> -#> ```{r} -#> cc_list$Linear -#> ``` -#> -#> # Non Linear -#> -#> ```{r} -#> cc_list$`Non Linear` -#> ``` -#> -#> # Outlier Vertical -#> -#> ```{r} -#> cc_list$`Outlier Vertical` -#> ``` -#> -#> # Outlier Horizontal -#> -#> ```{r} -#> cc_list$`Outlier Horizontal` -#> ``` -``` - -The document can then be written to a file, rendered, and viewed with the following code. - - -```r +``` r library(rmarkdown) writeLines(doc, file("anscombe.Rmd")) @@ -134,8 +142,6 @@ render("anscombe.Rmd") browseURL("anscombe.html") ``` - - [](https://cran.r-project.org/package=listdown) -[](https://travis-ci.org/kaneplusplus/listdown) -[](https://ci.appveyor.com/project/kaneplusplus/listdown) -[](https://codecov.io/gh/kaneplusplus/listdown) -[](https://cran.r-project.org/package=listdown) -[](https://travis-ci.com/kaneplusplus/listdown) +[](https://github.com/kaneplusplus/listdown/actions) ## Overview diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 0000000..c9f059f --- /dev/null +++ b/docs/404.html @@ -0,0 +1,151 @@ + + + +
+ + + + +As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
+We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
+Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
+This Code of Conduct is adapted from the Contributor Covenant (https://www.contributor-covenant.org), version 1.0.0, available at https://contributor-covenant.org/version/1/0/0/.
+Version 2.0, January 2004 <http://www.apache.org/licenses/>
+“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.
+“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”
+“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+END OF TERMS AND CONDITIONS
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets []
replaced with your own identifying information. (Don’t include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same “printed page” as the copyright notice for easier identification within third-party archives.
2020 listdown
+ Copyright
+2.0 (the "License");
+ Licensed under the Apache License, Version in compliance with the License.
+ you may not use this file except
+ You may obtain a copy of the License at
+://www.apache.org/licenses/LICENSE-2.0
+ http
+in writing, software
+ Unless required by applicable law or agreed to "AS IS" BASIS,
+ distributed under the License is distributed on an
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.for the specific language governing permissions and
+ See the License limitations under the License.
R Markdown documents support the construction of reproducible documents by allowing authors to insert R code for data processing, exploration, analysis, table-making, and visualization directly into structured, prose documents. In the context of this document, we will refer to the R code in these documents as computational components since they are generated by computational means (namely the R interpreter). The prose in these documents will be referred to as narrative components. They may serve to help a reader understand the background, goal, theme, and results of the paper as well as contextualizing the computational components.
+R Markdown integrates the process of integrating the computational and narrative components of a document. By itself this is not novel. It was identified as “Literate Programming”
Since computational components are, by definition, computationally derived objects and R Markdown is a well-defined standard it is possible to programmatically create R Markdown documents with computational components. This is the focus of this paper. However, before proceeding down this avenue, we would like to highlight two fundamental limitations to automated R Markdown document construction. First and foremost, without narrative components a document has no context. Quantitative analyses require research questions, hypotheses, reviews, interpretations, and conclusions. Computational components are necessary but not sufficient for constructing an analysis. Second, it is not possible to construct computational components for an arbitrary set of distinct analyses. An analysis itself has a context and it is built with a set of assumptions and goals. Computational components are constructed for a narrow class of problems.
+However, this is not to say the programmatic creation of R Markdown documents is unwarranted or not useful. In fact it has at least two appealing characteristics. The first is convenience. The second is that for fixed analysis pipelines processing uniformly-formatted but domain-distinct data, automated document generation enforces uniformity across those domains being serviced.
+The {listdown} package provides functions to programmatically create R Markdown files from named lists. It is intended for data analysis pipelines where the presentation of the results is separated from their creation. For this use case, a data processing (or analysis) is performed and the results are provided in a single named list, organized hierarchically. With the list and a {listdown} object a workflowr, pdf, word, or html page. List element names denote sections, subsections, subsubsection, etc. and the list elements contain the data structure to be presented including graphs and tables. The goal of the package is not to provide a finished, readable document. It is to provide a document with all tables and visualization that will appear (computational components). This serves as a starting point from which a user can organize outputs, describe a study, discuss results, and provide conclusions (narrative components).
+listdown provides a reproducible means for producing a document with specified computational components. It is most compatible with data analysis pipelines where the data format is fixed but the analyses are either being updated, which may affect narrative components including the result discussion and conclusion, or where the experiment is different, which affects all narrative components If the narrative components are not changing with the data being pushed through your analysis pipeline, then you may be better off writing the R Markdown code manually.
+The package itself is relatively simple with 6 distinct methods that can be easily incorporated into existing analysis pipelines for automatically creating documents that can be used for data exploration and reviewing analysis results as well as a starting point for a more formal write up. These methods include:
+listdown() - Create a listdown object to create an R Markdown document. ld_make_chunks() - Write a listdown object to a string. ld_chunk_opts() - Apply chunk options to a presentation object. ld_workflowr_header() - Create a workflowr header. ld_rmarkdown_header() - Create an R Markdown header. ld_ioslides_header() - Create an ioslides presentation header.
+The rest of this paper is structured as follow. The next section goes over basic usage and commentary. This section is meant to convey the basic approach used by the package and shows how to describe an output document using listdown, create a document, and change how the presentation of computational components can be specialized using listdown decorators. With the user accustomed to the package’s basic usage, section 3 describes the design of the package. Section 4 goes over advanced usage of the package including adding initialization code to and outputted document as well as how to control chunk-level options. Section 5 concludes the paper with a few final remarks.
+Suppose we have just completed and analysis and have collected all of the results into a list where the list elements are roughly in the order we would like to present them in a document. It may be noted that this is not always how computational components derived from data analyses are collated. Often individual components are stored in multiple locations on a single machine or across machines. However, it is important to realize that even for analyses on large-scale data, the digital artifact that will be presented will be relatively small. Centralizing them makes it easier to access them, since they don’t need to be found in multiple locations. Also, storing them as a list provides a hierarchical structure that translates directly to a document as we will see below.
+As a first example, we will consider the a list of visualizations from the anscombe data set. The list is composed of four elements (named Linear, Non Linear, Outlier Vertical, and Outlier Horizontal) each containing a scatter plot from the famous Anscomb Quartet. From the computational_components
list, we would like to create a document with four sections with names corresponding to the list names, each containing their respective visualizations.
+# Use ggplot2 to create the visualizations.
+library(ggplot2)
+
+# Load the Anscombe Quartet.
+data(anscombe)
+
+# Create the ggplot objects to display.
+computational_components <- list(
+ Linear = ggplot(anscombe, aes(x = x1, y = y1)) + geom_point() + theme_bw(),
+ `Non Linear` = ggplot(anscombe, aes(x = x2, y = y2)) + geom_point() + theme_bw(),
+ `Outlier Vertical`= ggplot(anscombe, aes(x = x3, y = y3)) + geom_point() + theme_bw(),
+ `Outlier Horizontal` = ggplot(anscombe, aes(x = x4, y = y4)) + geom_point() + theme_bw())
+
+# Save the file to disk to be read by the output R Markdown document.
+saveRDS(computational_components, "comp-comp.rds")
Creating a document from the computational_components
will require two steps. First, we will create a listdown
object that specifies how the computational_components
object will be loaded into the document, which libraries and code needs to be included, the options for the R chunks, and how the list elements will be presented in the output R markdown document.
+library(listdown)
+
+ld <- listdown(load_cc_expr = readRDS("comp-comp.rds"),
+ package = "ggplot2")
The ld
object, along with the computational components in the comp-comp.rds
file are sufficient to to create the sections, subsections, and R chunks of a document. The only other thing requires to create the document is the header. The listdown package currently supports regular R Markdown, workflowr, and ioslides headers. A complete document can then be written to the console using the code shown below. It could easily be written to a file for rendering using the writeLines()
function, for example.
+doc <- c(
+ as.character(ld_rmarkdown_header("Anscombe's Quartet",
+ author = "Francis Anscombe",
+ date = "1973")),
+ ld_make_chunks(ld))
+
+cat("\n", paste(doc, collapse = "\n"))
+#>
+#> ---
+#> title: Anscombe's Quartet
+#> author: Francis Anscombe
+#> date: '1973'
+#> output: html_document
+#> ---
+#>
+#> ```{r}
+#> library(ggplot2)
+#>
+#> cc_list <- readRDS("comp-comp.rds")
+#> ```
+#>
+#> # Linear
+#>
+#> ```{r}
+#> cc_list$Linear
+#> ```
+#>
+#> # Non Linear
+#>
+#> ```{r}
+#> cc_list$`Non Linear`
+#> ```
+#>
+#> # Outlier Vertical
+#>
+#> ```{r}
+#> cc_list$`Outlier Vertical`
+#> ```
+#>
+#> # Outlier Horizontal
+#>
+#> ```{r}
+#> cc_list$`Outlier Horizontal`
+#> ```
+#>
+#> # Data
+#>
+#> ```{r}
+#> cc_list$Data
+#> ```
The listdown()
function provides document-wide R chunk options for displaying computational components. The chunk options are exactly the same as those in the R markdown document and can be used to tailor the default presentation for a variety of needs. The complete set of options can be found in the R Markdown Reference Guide. As a concrete example, the code used to create present the plots could be hidden in the output document using the following code.
+ld <- listdown(load_cc_expr = readRDS("comp-comp.rds"),
+ package = "ggplot2",
+ echo = FALSE)
+
+cat(paste(ld_make_chunks(ld), collapse = "\n"))
+#>
+#> ```{r echo = FALSE}
+#> library(ggplot2)
+#>
+#> cc_list <- readRDS("comp-comp.rds")
+#> ```
+#>
+#> # Linear
+#>
+#> ```{r echo = FALSE}
+#> cc_list$Linear
+#> ```
+#>
+#> # Non Linear
+#>
+#> ```{r echo = FALSE}
+#> cc_list$`Non Linear`
+#> ```
+#>
+#> # Outlier Vertical
+#>
+#> ```{r echo = FALSE}
+#> cc_list$`Outlier Vertical`
+#> ```
+#>
+#> # Outlier Horizontal
+#>
+#> ```{r echo = FALSE}
+#> cc_list$`Outlier Horizontal`
+#> ```
+#>
+#> # Data
+#>
+#> ```{r echo = FALSE}
+#> cc_list$Data
+#> ```
The first example is simple in part because the ggplot objects both contain the data we want to display and, at the same time, provide the mechanism for presenting them - rendering them in a graph. However, this is not always the case. The objects being stored in the list of computational components may not translate directly to the presentation in a document. In these cases a function is needed that takes the list component and returns an object to be displayed. For example, suppose that, along with showing graphs from the Anscombe Quartet, we would like to include the data themselves. We could add the data to the computational_components
list and then create the document with:
#>
+#> ```{r echo = FALSE}
+#> library(ggplot2)
+#>
+#> cc_list <- readRDS("comp-comp.rds")
+#> ```
+#>
+#> # Linear
+#>
+#> ```{r echo = FALSE}
+#> cc_list$Linear
+#> ```
+#>
+#> # Non Linear
+#>
+#> ```{r echo = FALSE}
+#> cc_list$`Non Linear`
+#> ```
+#>
+#> # Outlier Vertical
+#>
+#> ```{r echo = FALSE}
+#> cc_list$`Outlier Vertical`
+#> ```
+#>
+#> # Outlier Horizontal
+#>
+#> ```{r echo = FALSE}
+#> cc_list$`Outlier Horizontal`
+#> ```
+#>
+#> # Data
+#>
+#> ```{r echo = FALSE}
+#> cc_list$Data
+#> ```
+
+computational_components$Data <- anscombe
+saveRDS(computational_components, "comp-comp.rds")
+cat(paste(ld_make_chunks(ld), collapse = "\n"))
In this case, the {listdown} package will show the entire data set as is the default specified. However, suppose we do not want to show the entire data set in the document. This is common, especially when the data set is large and requires too much vertical space in the outputted document resulting in too much or irrelevant data being shown. Instead, we would like to output to an html document where the data is shown in a datatable thereby controlling the amount of real-estate needed to present the data and, at the same time, providing the user with interactivity to sort and search the data set.
+In {listdown} a function or method that implements the presentation of a computational component is referred to as a {} since if follows the decorator pattern described in the classic software engineering text “Design Patterns” by Gamma et al. A decorator takes the element that will be presented as an argument and returns an object for presentation in the output directory. A decorator is specified using the decorator
parameter of the listdown()
function using a named list where the name corresponds to the type and the element correspond to the function or method that will decorate an object of that type. For example, the anscombe
data set can be decorated with the DT::datatable()
function as follow. It should be noted that the DT
library both loaded in the following code and specified as a package options. This allows ld_make_chunks()
to verify the existence of decorators before generating the chunks.
+library(DT)
+ld <- listdown(load_cc_expr = readRDS("comp-comp.rds"),
+ package = c("ggplot2", "DT"),
+ decorator = list(data.frame = datatable))
+cat(paste(ld_make_chunks(ld), collapse = "\n"))
+#>
+#> ```{r}
+#> library(ggplot2)
+#> library(DT)
+#>
+#> cc_list <- readRDS("comp-comp.rds")
+#> ```
+#>
+#> # Linear
+#>
+#> ```{r}
+#> cc_list$Linear
+#> ```
+#>
+#> # Non Linear
+#>
+#> ```{r}
+#> cc_list$`Non Linear`
+#> ```
+#>
+#> # Outlier Vertical
+#>
+#> ```{r}
+#> cc_list$`Outlier Vertical`
+#> ```
+#>
+#> # Outlier Horizontal
+#>
+#> ```{r}
+#> cc_list$`Outlier Horizontal`
+#> ```
+#>
+#> # Data
+#>
+#> ```{r}
+#> datatable(cc_list$Data)
+#> ```
List names in the decorator
argument provide a key to which a function or method is mapped. The underlying decorator resolution is implemented for a given computational component by going through decorator names sequentially to see if the component inherits from the name using the inherits()
function. The function or method is selected from the corresponding name which the element first inherits from. This means that when customizing the presentation of objects that inherit from a common class, the more abstract classes should appear at the end of the list. This will ensure that specialized classes will be encountered first in the resolution process.
A separate argument, default_decorator
, allows the user to specify the default decorator for an object whose type does not appear in the decorator
list. This allows the user to specify any class name for the decorator and avoids a potential type name collision with a default decorator whose name is determined by convention. By default, this argument is set to identity
but it can be use to not display a computational component by default if the argument is set to NULL
.
The {listdown} package provides functions to programmatically create R Markdown files from named lists. It is intended for data analysis pipelines where the presentation of the results is separated from their creation. For this use case, a data processing (or analysis) is performed and the results are provided in a single named list, organized hierarchically. With the list and a {listdown} object a workflowr, pdf, word, or html page. List element names denote sections, subsections, subsubsection, etc. and the list elements contain the data structure to be presented including graphs and tables. The goal of the package is not to provide a finished, readable document. It is to provide a document with all tables and visualization that will appear (computational components). This serves as a starting point from which a user can organize outputs, describe a study, discuss results, and provide conclusions (narrative components).
+{listdown} provides a reproducible means for producing a document with specified computational components. It is most compatible with data analysis pipelines where the data format is fixed but the analyses are either being updated, which may affect narrative components including the result discussion and conclusion, or where the experiment is different, which affects all narrative components If the narrative components are not changing with the data being pushed through your analysis pipeline, then you may be better off writing the R Markdown code manually.
+You can install the released version of listdown from CRAN with:
+
+install.packages("listdown")
The development version of {listdown} can be installed from GitHub with:
+
+# install.packages("devtools")
+devtools::install_github("kaneplusplus/listdown")
As a toy example, suppose we would like to create an html document plotting Anscombe’s quartet with each plot having it’s own section. To construct the document, we will need to two objects. The first is a presentation list, whose names indicate section (or subsection) titles and whose elements are the objects to present. The second is a listdown
object, which describes how the object should be rendered in the document.
library(listdown)
+library(ggplot2)
+
+data(anscombe)
+
+# Create the ggplot objects to display.
+<- list(
+ pres_list Linear = ggplot(anscombe, aes(x = x1, y = y1)) + geom_point() + theme_bw(),
+ `Non Linear` = ggplot(anscombe, aes(x = x2, y = y2)) + geom_point() + theme_bw(),
+ `Outlier Vertical`= ggplot(anscombe, aes(x = x3, y = y3)) + geom_point() + theme_bw(),
+ `Outlier Horizontal` = ggplot(anscombe, aes(x = x4, y = y4)) + geom_point() + theme_bw())
+
+# Save the pres_list object so that it can be used in the R Markdown document.
+saveRDS(pres_list, "pres-list.rds")
+
+# Create a listdown object.
+<- listdown(load_cc_expr = readRDS("pres-list.rds"), # The expression to load pres_list.
+ ld package = "ggplot2")) # The packges needed to render plots.
+
+# Output an html document to a string.
+<- c(
+ doc as.character(
+ ld_rmarkdown_header("Anscombe's Quartet",
+ author = "Francis Anscombe",
+ date = "1973")),
+ ld_make_chunks(ld))
+
+cat(paste(doc, collapse = "\n"))
#> ---
+#> title: Anscombe's Quartet
+#> author: Francis Anscombe
+#> date: '1973'
+#> output: html_document
+#> ---
+#>
+#> ```{r}
+#> library(ggplot2)
+#>
+#> cc_list <- readRDS("pres-list.rds")
+#> ```
+#>
+#> # Linear
+#>
+#> ```{r}
+#> cc_list$Linear
+#> ```
+#>
+#> # Non Linear
+#>
+#> ```{r}
+#> cc_list$`Non Linear`
+#> ```
+#>
+#> # Outlier Vertical
+#>
+#> ```{r}
+#> cc_list$`Outlier Vertical`
+#> ```
+#>
+#> # Outlier Horizontal
+#>
+#> ```{r}
+#> cc_list$`Outlier Horizontal`
+#> ```
The document can then be written to a file, rendered, and viewed with the following code.
+ + +Please note that the {listdown} project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.
+NEWS.md
+ R/as-yml.r
+ as_ld_yml.Rd
Create an object of type yaml::yml from a list of +computational components. The function recursively descends into the list +and when an element type is not a list the class information substituted +for the object.
+as_ld_yml(x)+ +
x | +a named list of computational components. |
+
---|
+if (require("ggplot2")) { + + cc_list <- list( + Linear = ggplot(anscombe, aes(x = x1, y = y1)) + geom_point(), + `Non Linear` = ggplot(anscombe, aes(x = x2, y = y2)) + geom_point(), + `Outlier Vertical`= ggplot(anscombe, aes(x = x3, y = y3)) + geom_point(), + `Outlier Horizontal` = ggplot(anscombe, aes(x = x4, y = y4)) + + geom_point()) + + as_ld_yml(cc_list) +} +#>#> [1] "Linear:\n- gg:ggplot\nNon Linear:\n- gg:ggplot\nOutlier Vertical:\n- gg:ggplot\nOutlier Horizontal:\n- gg:ggplot\n"
R/add-load-cc-expr.r
+ create_load_cc_expr.Rd
An expression to load a computational component can be either +a raw expression, a variable holding the expression, or a string. The +return is an unevaluated expression.
+create_load_cc_expr(load_cc_expr)+ +
load_cc_expr | +a string or expression that should be use to load +the computational components. |
+
---|
R/build-site.r
+ ld_build_html_site.Rd
This function creates an html website with each tab in the +page being desribed by a listdown document bundle.
+ld_build_html_site( + doc_bundles, + site_yaml, + rmd_dir = file.path(tempdir(), "rmarkdown"), + data_dir = file.path("..", "data"), + html_dir = file.path("..", "html"), + render_site = TRUE, + view = interactive(), + make_data_dir = TRUE, + make_rmd_dir = TRUE, + ... +)+ +
doc_bundles | +a named list of document bundles. There can be up to one +unnamed bundle, which will be assumed to correspond to an index.rmd file. |
+
---|---|
site_yaml | +a list of site information, which will be written +to the _site.yml file. |
+
rmd_dir | +the directory where the R Markdown files will reside. By +default an "rmarkdown" file is written to `tempdir()`. |
+
data_dir | +the location where data can be found for each bundle. +If the data is held in memory for a listdown document bundle, then it will +be written to the specified directory. If mulitple directories are specified, +then the directory is specified per bundle, with index recycling used if +the number of directories is not the same as the number of bundles. |
+
html_dir | +the location of the rendered document, relative to the +directory specified by `rmd_dir`. Note that this is an rmarkdown +convention. By default a directory names "html" is created in the +directory specified by `rmd_dir` and rendered documents are place there. |
+
render_site | +should the page be rendered? If not then the +`html_dir` is not created. |
+
view | +should the output document be opened after rendering? By +default, if `render_doc` is `TRUE` and this argument is `TRUE` then +the browser will open for you to examine the output. |
+
make_data_dir | +if the `data_dir` directory is not present, should it +be created? This can be set to `FALSE` when data already resides on disk +to verify that it is not being created and written. |
+
make_rmd_dir | +if the `rmd_dir` directory is not present, should it +be created? This can be set to `FALSE` when data already resides on disk +to verify that it is not being created and written. |
+
... | +argument to be passed to the `rmarkdown::render_site()` function. |
+
ld_bundle_doc ld_create_doc
A page bundle encapsulates the computational components, +R Markdown header, and listdown object. Together, these three objects +are sufficient to create a document, which can be written with the +`ld_create_document()` function.
+ld_bundle_doc(cc, header, ld)+ +
cc | +the computational component list that will be presented. |
+
---|---|
header | +a `list` with the header information for the document. |
+
ld | +a `listdown` object describing how to present the computational +components. |
+
ld_create_document
R/cc-dendro.r
+ ld_cc_dendro.Rd
This function creates text dendrograms from +a list of computational components. It is useful for +creating a dendrogram of the the computational components of a listdown +object allowing the user to view the components hierarchically.
+ld_cc_dendro(x)+ +
x | +a named list of computational components |
+
---|
+if (require("ggplot2")) { + + cc_list <- list( + Linear = ggplot(anscombe, aes(x = x1, y = y1)) + geom_point(), + `Non Linear` = ggplot(anscombe, aes(x = x2, y = y2)) + geom_point(), + `Outlier Vertical`= ggplot(anscombe, aes(x = x3, y = y3)) + geom_point(), + `Outlier Horizontal` = ggplot(anscombe, aes(x = x4, y = y4)) + + geom_point()) + + ld_cc_dendro(cc_list) +} +#> +#> cc_list +#> |-- Linear +#> | o-- object of type(s):gg ggplot +#> |-- Non Linear +#> | o-- object of type(s):gg ggplot +#> |-- Outlier Vertical +#> | o-- object of type(s):gg ggplot +#> o-- Outlier Horizontal +#> o-- object of type(s):gg ggplot +#>
This function allows the user to set chunk options for +individual elements of a presentation list.
+ld_chunk_opts(pres_obj, chunk_name = NULL, ..., chunk_opts = NULL)+ +
pres_obj | +the presentation list element whose chunk options should +be modified. |
+
---|---|
chunk_name | +the name of the chunk. By default this is NULL, +corresponding to no chunk name. |
+
... | +named chunk options and their values. |
+
chunk_opts | +list of chunk options can be specified. Takes priority +over arguments provided to ... |
+
This function creates a document, defined by a listdown bundle +in a specified location on disk and, optionally, opens the document in the +browser.
+ld_create_doc( + ldb, + rmd_file_name = basename(tempfile(pattern = "rmarkdown", fileext = ".rmd")), + rmd_dir = file.path(tempdir(), "rmarkdown"), + output_dir = file.path(rmd_dir, "pres"), + render_doc = TRUE, + cc_file_name = NULL, + data_dir = NULL, + view = interactive(), + ... +)+ +
ldb | +a listdown doc bundle. |
+
---|---|
rmd_file_name | +the name of the R Markdown file to create. By default, +a temporary file is created. |
+
rmd_dir | +the directory where the output R Markdown file should be +written to. By default, this is `tempdir()`. |
+
output_dir | +the location of the rendered document, relative to the +directory specified by `rmd_dir`. Note that this is an rmarkdown +convention. By default a directory names "pres" is created in the +directory specified by `rmd_dir` and rendered documents are place there. |
+
render_doc | +should the page be rendered? If not then the +`output_dir` is not created. |
+
cc_file_name | +the name of the list specifying the computational +components. If this is `NULL` (the default) then the listdown bundle +is checked to make sure it's `load_cc_expr` attribute has been specified. +If it is specified, and the bundles `load_cc_expr` has not been specified, +then it will be written to disk (in the corresponding data directory, +specified by `data_dir`) and read via the `saveRDS()` function. |
+
data_dir | +the directory where data should be written. If the +`cc_file_name` argument is `NULL` then this argument is ignored. If the +`cc_file_name` argument is specfied but `data_dir` is not, then `tempdir()` +is used. |
+
view | +should the output document be opened after rendering? By +default, if `render_doc` is `TRUE` and this argument is `TRUE` then +the browser will open for you to examine the output. |
+
... | +options to send to the rmarkdown::render() function. |
+
ld_bundle_doc
After a presentation list and listdown object have been +constructed the chunks can be rendered to a string, which can be appended +to a file, with appropriate headers, resulting in a compilable R Markdown +document.
+ld_make_chunks(ld, rmd_dir)+ +
ld | +the listdown object that provides +information on how a presentation object should be displayed in the +output. |
+
---|
Output an R Markdown header with specified parameters.
+ld_rmarkdown_header( + title, + author = NULL, + date = NULL, + output = c("html_document", "pdf_document", "word_document") +)+ +
title | +the title of the page. |
+
---|---|
author | +the author of the page. The default is NULL - no author. |
+
date | +the date for the page. The default is NULL - no date. |
+
output | +the output format of the page. If NULL then no output format. +The default is an html document. |
+
Create a Minimalist Site YAML List
+ld_site_yaml(site_name, tab_name, rmd_name, navbar_title = site_name)+ +
site_name | +the name of the site. |
+
---|---|
tab_name | +the name of the tabs on the site. |
+
rmd_name | +the name of the Rmarkdown files that will generate the +respective tabs. |
+
navbar_title | +the title of the navigation bar (Default is the +`site_name` argument. |
+
This function takes header information and a listdown +object and writes to a specified file.
+ld_write_file(rmd_header, ld, file_name)+ +
rmd_header | +either a character or listdown_header with R Markdown +header information. |
+
---|---|
ld | +the listdown object that provides +information on how a presentation object should be displayed in the +output. |
+
file_name | +the output file to write to. |
+
A listdown object provides information for how a presentation +list should be used to create an R Markdown document. It requires an +unquoted expression indicating how the presentation list will be loaded. +In addition, libraries required by the outputted document and other +parameters can be specified.
+listdown( + package = NULL, + decorator = list(), + decorator_chunk_opts = list(), + default_decorator = identity, + setup_expr = NULL, + init_expr = NULL, + load_cc_expr = NULL, + ..., + chunk_opts = NULL +)+ +
package | +a quoted list of package required by the outputted document. |
+
---|---|
decorator | +a named list mapping the potential types of list elements +to a decorator function. |
+
decorator_chunk_opts | +a named list mapping the potential types of list +elements to chunk options that should be included for those types. |
+
default_decorator | +the decorator to use for list elements whose type +is not inherited from the decorator list. If NULL then the those +elements will not be included when the chunks are written. By default +this is identity, meaning that the elements will be passed directly +(through the identity() function). |
+
setup_expr | +an expression that is added before package are +loaded. The expression is put into a chunk named `setup` with option +`include = FALSE` and is intended for initializing the document. For +example the expression `knitr::opts_chunk$set(echo = FALSE)` could be +used to turn echo'ing off for the entire document. |
+
init_expr | +an initial expression that will be added to the outputted +document after the libraries have been called. This expression appears +after packages are loaded and before data is read. |
+
load_cc_expr | +either an unquoted expression or a character string +that will be turned into an unquoted expression via str2lang to load the +presentation list. |
+
... | +default options sent to the chunks of the outputted document. |
+
chunk_opts | +a named list of options sent to the chunks of outputted +documents. Note: takes priority over argument provided to ... |
+