Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building and maintaining OpenIntro using the R ...

Building and maintaining OpenIntro using the R ecosystem (useR 2021)

Building and maintaining OpenIntro using the R ecosystem

OpenIntro's (openintro.org) mission is to make educational products that are free and transparent and that lower barriers to education. The products include textbooks (in print and online), supporting resources for instructors as well as for students. From day one, OpenIntro materials have been built using tools within the R ecosystem. In this talk we will discuss how the OpenIntro project has shaped and grown over the years, our process for developing and publishing open-source textbooks at the high school and college level, and our computing resources such as interactive R tutorials and R packages as well as labs in various languages. We will highlight recent workflows we have developed and lessons learned for converting books from LaTeX to bookdown and give an overview of our project organization and tooling for authoring, collaboration, and maintenance, much of which is built with R, R Markdown, Git, and GitHub. Finally, we will discuss opportunities for getting involved for educators and students contributing to the development of open-source educational resources under the OpenIntro umbrella and beyond.

Mine Cetinkaya-Rundel

July 07, 2021
Tweet

More Decks by Mine Cetinkaya-Rundel

Other Decks in Education

Transcript

  1. building and maintaining using the R ecosystem mine-cetinkaya-rundel [email protected] @minebocek

    🔗 bit.ly/openintro-useR2021 mine çetinkaya-runde l duke university & rstudio
  2. OpenIntro’s mission is to make educational products that are free,

    transparent, and lower barriers to education. mission 🔗 bit.ly/openintro-useR2021
  3. ๏Applications as motivation ๏Real, recent, relatable datasets ๏Emphasis on data

    exploration, multivariable relationships, and statistical reasoning ๏Guided practices and worked examples interspersed in text ๏Lots of end of chapter exercises, with solutions to odd numbered questions in the back pedagogy 🔗 bit.ly/openintro-useR2021
  4. ๏HTML textbook freely available ๏PDF also freely available — distributed

    through LeanPub with a suggested donation (min $0) ๏Paperback available at low cost — cost of printing + minimal royalty that goes back to OpenIntro (US-based nonpro fi t) “business” model 🔗 bit.ly/openintro-useR2021
  5. how it started ๏Each chapter its own LaTeX fi le

    ๏Each fi gure in a folder with associated R code that generates it + how it’s made 🔗 bit.ly/openintro-useR2021
  6. how it’s going ๏Each chapter its own Rmd fi le

    ๏Output to HTML and PDF with the same source code (and lots of customization!) how it’s made 🔗 bit.ly/openintro-useR2021
  7. ๏Challenge: Convert from LaTeX to R Markdown ๏Step 1: Pandoc

    magic! conversion # Load packages ---------------------------------------------------------------- library(fs) library(tidyverse) # Convert from tex to md ------------------------------------------------------- tex_to_md <- function(tex_file_name){ # convert md_file_name <- str_replace(tex_file_name, "\\.tex$", ".md") system_call <- paste("pandoc -s" , tex_file_name, "-o", md_file_name) system(system_call) # delete tex file_delete(tex_file_name) } # Convert from md to rmd ------------------------------------------------------- md_to_rmd <- function(md_file_name){ rmd_file_name <- md_file_name %>% str_replace("\\.md$", ".Rmd") file_move( path = md_file_name, new_path = rmd_file_name ) } # Get tex file names ----------------------------------------------------------- tex_file_names <- dir_info(recurse = 2, glob = "*.tex") %>% pull(path) # Convert from .tex to .md ----------------------------------------------------- walk(tex_file_names, tex_to_md) md_file_name <- str_replace(tex_file_name, "\\.tex$", ".md") system_call <- paste("pandoc -s" , tex_file_name, "-o", md_file_name) system(system_call) tex_file_names <- dir_info(recurse = 2, glob = "*.tex") %>% pull(path) walk(tex_file_names, tex_to_md) 🔗 bit.ly/openintro-useR2021
  8. conversion ๏ stringr and regular expressions for cleaning custom LaTeX!

    And when in doubt, add more //s! tex_file_clean <- tex_file %>% str_replace("\\\\qt\\{(.*?)(\n)?(\\s)*(.*?)\\\\label\\{.*?\\}\\}", "\\\\textbf{\\1 \\4}") %>% str_replace_all("begin\\{parts\\}", "begin{enumerate}\n\\\\def\\\\labelenumii{\\\\alph{enumii}.}") %>% str_replace_all("end\\{parts\\}", "end{enumerate}") %>% str_replace_all("\\\\footfullcite", " \\\\citep") %>% str_replace_all("\\.pdf", ".png") ๏ magick::image_convert() to convert PDFs to PNGs ๏ fs::file_delete() to remove fi les no longer needed ๏Challenge: Convert from LaTeX to R Markdown ๏Step 2: Post-processing 🔗 bit.ly/openintro-useR2021
  9. custom blocks ๏Challenge: Achieve similar looking custom blocks in HTML

    and PDF output ๏Solution: Fenced div blocks + Custom blocks chapter of the R Markdown Cookbook 🔗 bit.ly/openintro-useR2021 ::: {.guidedpractice data-latex=""} ::: Rmd .guidedpractice { padding: 1em 1em 1em 4em; margin-top: 30px; margin-bottom: 30px; background: #f8f8f8 5px center/3em no-repeat; border-top: 1px solid #569BBD; /* openintro::COL[1,1] */ border-bottom: 1px solid #569BBD; /* openintro::COL[1,1] */ background-image: url("../images/_icons/guided-practice.png"); background-position: 0.5em 1em; } .guidedpractice:before { content: "Guided practice"; clear: right; display: block; font-weight: bold; font-variant: small-caps; color: #569BBD; } CSS \newenvironment{guidedpractice}{ \vspace{4mm} \begin{mdframedwithfootGPWE} \begin{minipage}[t]{0.10\textwidth} {$\:$ \\ \setkeys{Gin}{width=2.5em,keepaspectratio} \includegraphics{images/_icons/guided-practice.png}} \end{minipage} \hfill \begin{minipage}[t]{0.90\textwidth} \vspace{-2mm} \setlength{\parskip}{1em} \noindent\textbf{\color{oiB}\small\fontfamily{phv} \selectfont{\MakeUppercase{Guided Practice}}} $\:$ \\ \\ } {\end{minipage} \end{mdframedwithfootGPWE} \vspace{4mm} } LaTeX
  10. plots ๏Challenge: A consistent and branded look for plots ๏Solution:

    De fi ne theme_openintro() and rede fi ne ggplot2 defaults 🔗 bit.ly/openintro-useR2021 ggplot2::update_geom_defaults("point", list(color = openintro::IMSCOL["blue","full"], fill = openintro::IMSCOL["blue","full"])) ggplot2::update_geom_defaults("bar", list(fill = openintro::IMSCOL["blue","full"], color = "#FFFFFF")) ggplot2::update_geom_defaults("col", list(fill = openintro::IMSCOL["blue","full"], color = "#FFFFFF")) ggplot2::update_geom_defaults("boxplot", list(color = openintro::IMSCOL["blue","full"])) ggplot2::update_geom_defaults("density", list(color = openintro::IMSCOL["blue","full"])) ggplot2::update_geom_defaults("line", list(color = openintro::IMSCOL["gray", "full"])) ggplot2::update_geom_defaults("smooth", list(color = openintro::IMSCOL["gray", "full"])) ggplot2::update_geom_defaults("dotplot", list(color = openintro::IMSCOL["blue","full"], fill = openintro::IMSCOL["blue","full"]))
  11. ๏Challenge: ๏ Source code for what’s in the printed book

    should be publicly available ๏ Source code for full solutions should not be publicly available ๏ Question, full solution, and short answer should all live in the same repo ๏Solution: ๏ Separate repo for exercises: 1 folder per exercise, containing 1 Rmd / question, short answer, full solution ๏ Programmatically generate a single Rmd for exercises for each chapter map(question_paths, read_lines) %>% map_chr(~ paste(.x, collapse = "\n")) %>% paste(collapse = "\n\n") %>% # fix figure path str_replace_all("images/", glue("exercises/images/")) %>% # write out write_lines(out_file_path) questions_odd <- tibble( question_no = 1:length(questions), question_label = questions ) %>% mutate(odd_even = if_else((question_no %% 2) != 0, "odd", "even")) %>% filter(odd_even == "odd") %>% pull(question_label) ๏ Programmatically generate a single Rmd for short solutions of odd numbered exercises for each chapter exercises 🔗 bit.ly/openintro-useR2021
  12. on: push name: Render Rmd jobs: render: name: Render Rmd

    runs-on: macOS-latest steps: - uses: actions/checkout@v2 - uses: r-lib/actions/setup-r@v1 - uses: r-lib/actions/setup-pandoc@v1 - uses: Install rmarkdown, remotes, and the local package run: | install.packages("remotes") remotes::install_cran(c("rmarkdown", "tidyverse", "openintro", "infer", "statsr", "GGally", "skimr")) shell: Rscript {0} - name: Render Lab 01 run: Rscript -e 'rmarkdown::render("01_intro_to_r/intro_to_r.Rmd")' … - name: Render Lab 09 run: Rscript -e 'rmarkdown::render("09_multiple_regression/multiple_regression.Rmd")' - name: Commit results run: | git config --local user.email "[email protected]" git config --local user.name "GitHub Actions" git commit -a -m 'Re-build Rmd' || echo "No changes to commit" git push origin || echo "No changes to commit" ๏Challenge: Maintenance ๏Solution: Use GitHub Actions (borrowed from r-lib/actions) to re-render Rmds with each push and PR merge 🔗 bit.ly/openintro-useR2021
  13. ๏Challenge: Keep main text software agnostic, but teach modern statistics!

    ๏Solution: Supplement with interactive R tutorials developed with learnr 🔗 bit.ly/openintro-useR2021
  14. ๏Challenge: Datasets blow up package size ๏Solution: Break into smaller

    packages, and bring them along automatically Depends: R (>= 2.10), airports, cherryblossom, usdata + 🔗 bit.ly/openintro-useR2021
  15. openintro.org/teachers/get_involved ๏Datasets: Data hunter ๏Exams: Exam contributor ๏Exercises: ๏ Exercise

    contributor ๏ MyOpenMath exercise conversion ๏Labs: ๏ Lab solution developer ๏ New software owner ๏ New lab developer ๏Modules: ๏ Module developer ๏ Module editor ๏Translation ๏Your ideas? 🔗 bit.ly/openintro-useR2021