diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..2d6d6748 Binary files /dev/null and b/.DS_Store differ diff --git a/.github/workflows/bookdown.yaml b/.github/workflows/bookdown.yaml index 30c0d5bf..49d727d3 100644 --- a/.github/workflows/bookdown.yaml +++ b/.github/workflows/bookdown.yaml @@ -16,11 +16,10 @@ name: bookdown jobs: bookdown: runs-on: ubuntu-latest - # Only restrict concurrency for non-PR jobs - concurrency: - group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} + env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/check-link-rot.yaml b/.github/workflows/check-link-rot.yaml new file mode 100644 index 00000000..5db14bd1 --- /dev/null +++ b/.github/workflows/check-link-rot.yaml @@ -0,0 +1,49 @@ +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + schedule: + # * is a special character in YAML so you have to quote this string + # Trigger once a week at 00:00 on Sunday + - cron: "0 0 * * SUN" + +name: check-link-rot + +jobs: + check-link-rot: + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-pandoc@v2 + with: + pandoc-version: "3.1.8" + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: "devel" + http-user-agent: "release" + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + pak-version: devel + dependencies: '"hard"' + extra-packages: | + any::rcmdcheck + any::urlchecker + needs: check + + - name: Run URL checker + run: | + options(crayon.enabled = TRUE) + rotten_links <- urlchecker::url_check(progress = FALSE) + print(rotten_links) + if (length(rotten_links$URL) > 0L) { + cli::cli_abort("Some URLs are outdated and need to be updated.") + } + shell: Rscript {0} diff --git a/.gitignore b/.gitignore index 476486f6..986cfda8 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ vignettes/*.pdf .Renviron rsconnect +.github/.DS_Store diff --git a/Advanced-R-exercises.rds b/Advanced-R-exercises.rds deleted file mode 100644 index cf73753d..00000000 Binary files a/Advanced-R-exercises.rds and /dev/null differ diff --git a/DESCRIPTION b/DESCRIPTION index a4d51e24..d5c08d7b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -12,7 +12,8 @@ Description: This presentation summarizes my understanding of best practices in naming things for software development. This is not a package, and the DESCRIPTION file is included to make it easier to install required packages in GitHub action workflows. -URL: https://github.com/IndrajeetPatil/Advanced-R-exercises +URL: https://github.com/IndrajeetPatil/Advanced-R-exercises, + https://indrajeetpatil.github.io/Advanced-R-exercises/ BugReports: https://github.com/IndrajeetPatil/Advanced-R-exercises/issues Depends: R (>= 4.1.0) diff --git a/_book/404.html b/_book/404.html deleted file mode 100644 index c3f00d26..00000000 --- a/_book/404.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - -Page not found | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

Page not found -

-

The page you requested cannot be found (perhaps it was moved or renamed).

-

You may want to try searching to find the page's new location, or use -the table of contents to find the page you are looking for.

-
-
-
-
-
- -
-
- - - - diff --git a/_book/Big-picture.md b/_book/Big-picture.md deleted file mode 100644 index 1636a9c9..00000000 --- a/_book/Big-picture.md +++ /dev/null @@ -1,3 +0,0 @@ -# Big Picture - -No exercises. diff --git a/_book/Conditions.md b/_book/Conditions.md deleted file mode 100644 index df9eae76..00000000 --- a/_book/Conditions.md +++ /dev/null @@ -1,756 +0,0 @@ -# Conditions - - - -Attaching the needed libraries: - - -```r -library(rlang, warn.conflicts = FALSE) -library(testthat, warn.conflicts = FALSE) -``` - -## Signalling conditions (Exercises 8.2.4) - ---- - -**Q1.** Write a wrapper around `file.remove()` that throws an error if the file to be deleted does not exist. - -**A1.** Let's first create a wrapper function around `file.remove()` that throws an error if the file to be deleted does not exist. - - -```r -fileRemove <- function(...) { - existing_files <- fs::file_exists(...) - - if (!all(existing_files)) { - stop( - cat( - "The following files to be deleted don't exist:", - names(existing_files[!existing_files]), - sep = "\n" - ), - call. = FALSE - ) - } - - file.remove(...) -} -``` - -Let's first create a file that we can delete immediately. - - -```r -fs::file_create("random.R") -``` - -The function should fail if there are any other files provided that don't exist: - - -```r -fileRemove(c("random.R", "XYZ.csv")) -#> The following files to be deleted don't exist: -#> XYZ.csv -#> Error: -``` - -But it does work as expected when the file exists: - - -```r -fileRemove("random.R") -#> [1] TRUE -``` - ---- - -**Q2.** What does the `appendLF` argument to `message()` do? How is it related to `cat()`? - -**A2.** As mentioned in the docs for `message()`, `appendLF` argument decides: - -> should messages given as a character string have a newline appended? - -- If `TRUE` (default value), a final newline is regarded as part of the message: - - -```r -foo <- function(appendLF) { - message("Beetle", appendLF = appendLF) - message("Juice", appendLF = appendLF) -} - -foo(appendLF = TRUE) -#> Beetle -#> Juice -``` - -- If `FALSE`, messages will be concatenated: - - -```r -foo <- function(appendLF) { - message("Beetle", appendLF = appendLF) - message("Juice", appendLF = appendLF) -} - -foo(appendLF = FALSE) -#> BeetleJuice -``` - -On the other hand, `cat()` converts its arguments to character vectors and concatenates them to a single character vector by default: - - -```r -foo <- function() { - cat("Beetle") - cat("Juice") -} - -foo() -#> BeetleJuice -``` - -In order to get `message()`-like default behavior for outputs, we can set `sep = "\n"`: - - -```r -foo <- function() { - cat("Beetle", sep = "\n") - cat("Juice", sep = "\n") -} - -foo() -#> Beetle -#> Juice -``` - ---- - -## Handling conditions (Exercises 8.4.5) - ---- - -**Q1.** What extra information does the condition generated by `abort()` contain compared to the condition generated by `stop()` i.e. what's the difference between these two objects? Read the help for `?abort` to learn more. - - -```r -catch_cnd(stop("An error")) -catch_cnd(abort("An error")) -``` - -**A1.** Compared to `base::stop()`, `rlang::abort()` contains two additional pieces of information: - -- `trace`: A traceback capturing the sequence of calls that lead to the current function -- `parent`: Information about another condition used as a parent to create a chained condition. - - -```r -library(rlang) - -stopInfo <- catch_cnd(stop("An error")) -abortInfo <- catch_cnd(abort("An error")) - -str(stopInfo) -#> List of 2 -#> $ message: chr "An error" -#> $ call : language force(expr) -#> - attr(*, "class")= chr [1:3] "simpleError" "error" "condition" - -str(abortInfo) -#> List of 4 -#> $ message: chr "An error" -#> $ trace :Classes 'rlang_trace', 'rlib_trace', 'tbl' and 'data.frame': 8 obs. of 6 variables: -#> ..$ call :List of 8 -#> .. ..$ : language catch_cnd(abort("An error")) -#> .. ..$ : language eval_bare(rlang::expr(tryCatch(!!!handlers, { force(expr) ... -#> .. ..$ : language tryCatch(condition = ``, { force(expr) ... -#> .. ..$ : language tryCatchList(expr, classes, parentenv, handlers) -#> .. ..$ : language tryCatchOne(expr, names, parentenv, handlers[[1L]]) -#> .. ..$ : language doTryCatch(return(expr), name, parentenv, handler) -#> .. ..$ : language force(expr) -#> .. ..$ : language abort("An error") -#> ..$ parent : int [1:8] 0 1 1 3 4 5 1 0 -#> ..$ visible : logi [1:8] FALSE FALSE FALSE FALSE FALSE FALSE ... -#> ..$ namespace : chr [1:8] "rlang" "rlang" "base" "base" ... -#> ..$ scope : chr [1:8] "::" "::" "::" "local" ... -#> ..$ error_frame: logi [1:8] FALSE FALSE FALSE FALSE FALSE FALSE ... -#> ..- attr(*, "version")= int 2 -#> $ parent : NULL -#> $ call : NULL -#> - attr(*, "class")= chr [1:3] "rlang_error" "error" "condition" -``` - ---- - -**Q2.** Predict the results of evaluating the following code - - -```r -show_condition <- function(code) { - tryCatch( - error = function(cnd) "error", - warning = function(cnd) "warning", - message = function(cnd) "message", - { - code - NULL - } - ) -} - -show_condition(stop("!")) -show_condition(10) -show_condition(warning("?!")) -show_condition({ - 10 - message("?") - warning("?!") -}) -``` - -**A2.** Correctly predicted πŸ˜‰ - -The first three pieces of code are straightforward: - - -```r -show_condition <- function(code) { - tryCatch( - error = function(cnd) "error", - warning = function(cnd) "warning", - message = function(cnd) "message", - { - code - NULL - } - ) -} - -show_condition(stop("!")) -#> [1] "error" -show_condition(10) -#> NULL -show_condition(warning("?!")) -#> [1] "warning" -``` - -The last piece of code is the challenging one and it illustrates how `tryCatch()` works. From its docs: - -> When several handlers are supplied in a single tryCatch then the first one is considered more recent than the second. - - -```r -show_condition({ - 10 - message("?") - warning("?!") -}) -#> [1] "message" -``` - ---- - -**Q3.** Explain the results of running this code: - - -```r -withCallingHandlers( - message = function(cnd) message("b"), - withCallingHandlers( - message = function(cnd) message("a"), - message("c") - ) -) -#> b -#> a -#> b -#> c -``` - -**A3.** The surprising part of this output is the `b` before the last `c`. - -This happens because the inner calling handler doesn't handle the message, so it bubbles up to the outer calling handler. - ---- - -**Q4.** Read the source code for `catch_cnd()` and explain how it works. - -**A4.** Let's look at the source code for `catch_cnd()`: - - -```r -rlang::catch_cnd -#> function (expr, classes = "condition") -#> { -#> stopifnot(is_character(classes)) -#> handlers <- rep_named(classes, list(identity)) -#> eval_bare(rlang::expr(tryCatch(!!!handlers, { -#> force(expr) -#> return(NULL) -#> }))) -#> } -#> -#> -``` - -As mentioned in the function docs: - -> This is a small wrapper around `tryCatch()` that captures any condition signalled while evaluating its argument. - -The `classes` argument allows a character vector of condition classes to catch, and the complex tidy evaluation generates the necessary condition (if there is any; otherwise `NULL`). - - -```r -catch_cnd(10) -#> NULL - -catch_cnd(abort(message = "an error", class = "class1")) -#> -#> Error: -#> ! an error -#> --- -#> Backtrace: -``` - ---- - -**Q5.** How could you rewrite `show_condition()` to use a single handler? - -**A5.** The source code for `rlang::catch_cond()` gives us a clue as to how we can do this. - -Conditions also have a `class` attribute, and we can use it to determine which handler will match the condition. - - -```r -show_condition2 <- function(code) { - tryCatch( - condition = function(cnd) { - if (inherits(cnd, "error")) { - return("error") - } - if (inherits(cnd, "warning")) { - return("warning") - } - if (inherits(cnd, "message")) { - return("message") - } - }, - { - code - NULL - } - ) -} -``` - -Let's try this new version with the examples used for the original version: - - -```r -show_condition2(stop("!")) -#> [1] "error" -show_condition2(10) -#> NULL -show_condition2(warning("?!")) -#> [1] "warning" -show_condition2({ - 10 - message("?") - warning("?!") -}) -#> [1] "message" -``` - ---- - -## Custom conditions (Exercises 8.5.4) - ---- - -**Q1.** Inside a package, it's occasionally useful to check that a package is installed before using it. Write a function that checks if a package is installed (with `requireNamespace("pkg", quietly = FALSE))` and if not, throws a custom condition that includes the package name in the metadata. - -**A1.** Here is the desired function: - - -```r -abort_missing_package <- function(pkg) { - msg <- glue::glue("Problem loading `{pkg}` package, which is missing and must be installed.") - - abort("error_missing_package", - message = msg, - pkg = pkg - ) -} - -check_if_pkg_installed <- function(pkg) { - if (!requireNamespace(pkg, quietly = TRUE)) { - abort_missing_package(pkg) - } - - TRUE -} - -check_if_pkg_installed("xyz123") -#> Error in `abort_missing_package()`: -#> ! Problem loading `xyz123` package, which is missing and must be installed. -check_if_pkg_installed("dplyr") -#> [1] TRUE -``` - -For a reference, also see the source code for following functions: - -- `rlang::is_installed()` -- `insight::check_if_installed()` - ---- - -**Q2.** Inside a package you often need to stop with an error when something is not right. Other packages that depend on your package might be tempted to check these errors in their unit tests. How could you help these packages to avoid relying on the error message which is part of the user interface rather than the API and might change without notice? - -**A2.** As an example, let's say that another package developer wanted to use the `check_if_pkg_installed()` function that we just wrote. - -So the developer using it in their own package can write a unit test like this: - - -```r -expect_error( - check_if_pkg_installed("xyz123"), - "Problem loading `xyz123` package, which is missing and must be installed." -) -``` - -To dissuade developers from having to rely on error messages to check for errors, we can instead provide a custom condition, which can be used for unit testing instead: - - -```r -e <- catch_cnd(check_if_pkg_installed("xyz123")) - -inherits(e, "error_missing_package") -#> [1] TRUE -``` - -So that the unit test could be: - - -```r -expect_s3_class(e, "error_missing_package") -``` - -This test wouldn't fail even if we decided to change the exact message. - ---- - -## Applications (Exercises 8.6.6) - ---- - -**Q1.** Create `suppressConditions()` that works like `suppressMessages()` and `suppressWarnings()` but suppresses everything. Think carefully about how you should handle errors. - -**A1.** To create the desired `suppressConditions()`, we just need to create an equivalent of `suppressWarnings()` and `suppressMessages()` for errors. To suppress the error message, we can handle errors within a `tryCatch()` and return the error object invisibly: - - -```r -suppressErrors <- function(expr) { - tryCatch( - error = function(cnd) invisible(cnd), - expr - ) -} - -suppressConditions <- function(expr) { - suppressErrors(suppressWarnings(suppressMessages(expr))) -} -``` - -Let's try out and see if this works as expected: - - -```r -suppressConditions(1) -#> [1] 1 - -suppressConditions({ - message("I'm messaging you") - warning("I'm warning you") -}) - -suppressConditions({ - stop("I'm stopping this") -}) -``` - -All condition messages are now suppressed, but note that if we assign error object to a variable, we can still extract useful information for debugging: - - -```r -e <- suppressConditions({ - stop("I'm stopping this") -}) - -e -#> -``` - - ---- - -**Q2.** Compare the following two implementations of `message2error()`. What is the main advantage of `withCallingHandlers()` in this scenario? (Hint: look carefully at the traceback.) - - -```r -message2error <- function(code) { - withCallingHandlers(code, message = function(e) stop(e)) -} -message2error <- function(code) { - tryCatch(code, message = function(e) stop(e)) -} -``` - -**A2.** With `withCallingHandlers()`, the condition handler is called from the signaling function itself, and, therefore, provides a more detailed call stack. - - -```r -message2error1 <- function(code) { - withCallingHandlers(code, message = function(e) stop("error")) -} - -message2error1({ - 1 - message("hidden error") - NULL -}) -#> Error in (function (e) : error - -traceback() -#> 9: stop("error") at #2 -#> 8: (function (e) -#> stop("error"))(list(message = "hidden error\n", -#> call = message("hidden error"))) -#> 7: signalCondition(cond) -#> 6: doWithOneRestart(return(expr), restart) -#> 5: withOneRestart(expr, restarts[[1L]]) -#> 4: withRestarts({ -#> signalCondition(cond) -#> defaultHandler(cond) -#> }, muffleMessage = function() NULL) -#> 3: message("hidden error") at #1 -#> 2: withCallingHandlers(code, -#> message = function(e) stop("error")) at #2 -#> 1: message2error1({ -#> 1 -#> message("hidden error") -#> NULL -#> }) -``` - -With `tryCatch()`, the signalling function terminates when a condition is raised, and so it doesn't provide as detailed call stack. - - -```r -message2error2 <- function(code) { - tryCatch(code, message = function(e) (stop("error"))) -} - -message2error2({ - 1 - stop("hidden error") - NULL -}) -#> Error in value[[3L]](cond) : error - -traceback() -#> 6: stop("error") at #2 -#> 5: value[[3L]](cond) -#> 4: tryCatchOne(expr, names, parentenv, handlers[[1L]]) -#> 3: tryCatchList(expr, classes, parentenv, handlers) -#> 2: tryCatch(code, message = function(e) (stop("error"))) at #2 -#> 1: message2error2({ -#> 1 -#> message("hidden error") -#> NULL -#> }) -``` - ---- - -**Q3.** How would you modify the `catch_cnds()` definition if you wanted to recreate the original intermingling of warnings and messages? - -**A3.** Actually, you won't have to modify anything about the function defined in the chapter, since it supports this out of the box. - -So nothing additional to do here^[The best kind of exercise there is!]! πŸ˜… - - -```r -catch_cnds <- function(expr) { - conds <- list() - add_cond <- function(cnd) { - conds <<- append(conds, list(cnd)) - cnd_muffle(cnd) - } - - withCallingHandlers( - message = add_cond, - warning = add_cond, - expr - ) - - conds -} - -catch_cnds({ - inform("a") - warn("b") - inform("c") -}) -#> [[1]] -#> -#> Message: -#> a -#> -#> [[2]] -#> -#> Warning: -#> b -#> -#> [[3]] -#> -#> Message: -#> c -``` - ---- - -**Q4.** Why is catching interrupts dangerous? Run this code to find out. - - -```r -bottles_of_beer <- function(i = 99) { - message( - "There are ", i, " bottles of beer on the wall, ", - i, " bottles of beer." - ) - while (i > 0) { - tryCatch( - Sys.sleep(1), - interrupt = function(err) { - i <<- i - 1 - if (i > 0) { - message( - "Take one down, pass it around, ", i, - " bottle", if (i > 1) "s", " of beer on the wall." - ) - } - } - ) - } - message( - "No more bottles of beer on the wall, ", - "no more bottles of beer." - ) -} -``` - -**A4.** Because this function catches the `interrupt` and there is no way to stop `bottles_of_beer()`, because the way you would usually stop it by using `interrupt`! - - -```r -bottles_of_beer() -#> There are 99 bottles of beer on the wall, 99 bottles of beer. -#> Take one down, pass it around, 98 bottles of beer on the wall. -#> Take one down, pass it around, 97 bottles of beer on the wall. -#> Take one down, pass it around, 96 bottles of beer on the wall. -#> Take one down, pass it around, 95 bottles of beer on the wall. -#> Take one down, pass it around, 94 bottles of beer on the wall. -#> Take one down, pass it around, 93 bottles of beer on the wall. -#> Take one down, pass it around, 92 bottles of beer on the wall. -#> Take one down, pass it around, 91 bottles of beer on the wall. -#> ... -``` - -In RStudio IDE, you can snap out of this loop by terminating the R session. - -This shows why catching `interrupt` is dangerous and can result in poor user experience. - ---- - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> brio 1.1.3 2021-11-30 [1] CRAN (R 4.2.0) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388) -#> desc 1.4.2 2022-09-08 [1] CRAN (R 4.2.1) -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> pkgload 1.3.1 2022-10-28 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rprojroot 2.0.3 2022-04-02 [1] CRAN (R 4.2.0) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> testthat * 3.1.5 2022-10-08 [1] CRAN (R 4.2.1) -#> tibble 3.1.8.9002 2022-10-16 [1] local -#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Control-flow.md b/_book/Control-flow.md deleted file mode 100644 index bcf7fe06..00000000 --- a/_book/Control-flow.md +++ /dev/null @@ -1,296 +0,0 @@ -# Control flow - - - -## Choices (Exercises 5.2.4) - -**Q1.** What type of vector does each of the following calls to `ifelse()` return? - - -```r -ifelse(TRUE, 1, "no") -ifelse(FALSE, 1, "no") -ifelse(NA, 1, "no") -``` - -Read the documentation and write down the rules in your own words. - -**A1.** Here are the rules about what a call to `ifelse()` might return: - -- It is type unstable, i.e. the type of return will depend on the type of which condition is true (`yes` or `no`, i.e.): - - -```r -ifelse(TRUE, 1, "no") # `numeric` returned -#> [1] 1 -ifelse(FALSE, 1, "no") # `character` returned -#> [1] "no" -``` - -- It works only for cases where `test` argument evaluates to a `logical` type: - - -```r -ifelse(NA_real_, 1, "no") -#> [1] NA -ifelse(NaN, 1, "no") -#> [1] NA -``` - -- If `test` is argument is of logical type, but `NA`, it will return `NA`: - - -```r -ifelse(NA, 1, "no") -#> [1] NA -``` - -- If the `test` argument doesn't resolve to `logical` type, it will try to coerce the output to a `logical` type: - - -```r -# will work -ifelse("TRUE", 1, "no") -#> [1] 1 -ifelse("false", 1, "no") -#> [1] "no" - -# won't work -ifelse("tRuE", 1, "no") -#> [1] NA -ifelse(NaN, 1, "no") -#> [1] NA -``` - -This is also clarified in the docs for this function: - -> A vector of the same length and attributes (including dimensions and `"class"`) as `test` and data values from the values of `yes` or `no`. The mode of the answer will be coerced from logical to accommodate first any values taken from yes and then any values taken from `no`. - -**Q2.** Why does the following code work? - - -```r -x <- 1:10 -if (length(x)) "not empty" else "empty" -#> [1] "not empty" - -x <- numeric() -if (length(x)) "not empty" else "empty" -#> [1] "empty" -``` - -**A2.** The code works because the conditional expressions in `if()` - even though of `numeric` type - can be successfully coerced to a `logical` type. - - -```r -as.logical(length(1:10)) -#> [1] TRUE - -as.logical(length(numeric())) -#> [1] FALSE -``` - -## Loops (Exercises 5.3.3) - -**Q1.** Why does this code succeed without errors or warnings? - - -```r -x <- numeric() -out <- vector("list", length(x)) -for (i in 1:length(x)) { - out[i] <- x[i]^2 -} -out -``` - -**A1.** This works because `1:length(x)` works in both positive and negative directions. - - -```r -1:2 -#> [1] 1 2 -1:0 -#> [1] 1 0 -1:-3 -#> [1] 1 0 -1 -2 -3 -``` - -In this case, since `x` is of length `0`, `i` will go from `1` to `0`. - -Additionally, since out-of-bound (OOB) value for atomic vectors is `NA`, all related operations with OOB values will also produce `NA`. - - -```r -x <- numeric() -out <- vector("list", length(x)) - -for (i in 1:length(x)) { - print(paste("i:", i, ", x[i]:", x[i], ", out[i]:", out[i])) - - out[i] <- x[i]^2 -} -#> [1] "i: 1 , x[i]: NA , out[i]: NULL" -#> [1] "i: 0 , x[i]: , out[i]: " - -out -#> [[1]] -#> [1] NA -``` - -A way to do avoid this unintended behavior is to use `seq_along()` instead: - - -```r -x <- numeric() -out <- vector("list", length(x)) - -for (i in seq_along(x)) { - out[i] <- x[i]^2 -} - -out -#> list() -``` - -**Q2.** When the following code is evaluated, what can you say about the vector being iterated? - - -```r -xs <- c(1, 2, 3) -for (x in xs) { - xs <- c(xs, x * 2) -} -xs -#> [1] 1 2 3 2 4 6 -``` - -**A2.** The iterator variable `x` initially takes all values of the vector `xs`. We can check this by printing `x` for each iteration: - - -```r -xs <- c(1, 2, 3) -for (x in xs) { - cat("x:", x, "\n") - xs <- c(xs, x * 2) - cat("xs:", paste(xs), "\n") -} -#> x: 1 -#> xs: 1 2 3 2 -#> x: 2 -#> xs: 1 2 3 2 4 -#> x: 3 -#> xs: 1 2 3 2 4 6 -``` - -It is worth noting that `x` is not updated *after* each iteration; otherwise, it will take increasingly bigger values of `xs`, and the loop will never end executing. - -**Q3.** What does the following code tell you about when the index is updated? - - -```r -for (i in 1:3) { - i <- i * 2 - print(i) -} -#> [1] 2 -#> [1] 4 -#> [1] 6 -``` - -**A3.** In a `for()` loop the index is updated in the **beginning** of each iteration. Otherwise, we will encounter an infinite loop. - - -```r -for (i in 1:3) { - cat("before: ", i, "\n") - i <- i * 2 - cat("after: ", i, "\n") -} -#> before: 1 -#> after: 2 -#> before: 2 -#> after: 4 -#> before: 3 -#> after: 6 -``` - -Also, worth contrasting the behavior of `for()` loop with that of `while()` loop: - - -```r -i <- 1 -while (i < 4) { - cat("before: ", i, "\n") - i <- i * 2 - cat("after: ", i, "\n") -} -#> before: 1 -#> after: 2 -#> before: 2 -#> after: 4 -``` - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> P utils * 4.2.2 2022-10-31 [1] local -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Debugging.md b/_book/Debugging.md deleted file mode 100644 index 6fb12943..00000000 --- a/_book/Debugging.md +++ /dev/null @@ -1,3 +0,0 @@ -# Debugging - -No exercises. diff --git a/_book/Environments.md b/_book/Environments.md deleted file mode 100644 index da0b22f3..00000000 --- a/_book/Environments.md +++ /dev/null @@ -1,627 +0,0 @@ -# Environments - - - - - - - -Loading the needed libraries: - - -```r -library(rlang, warn.conflicts = FALSE) -``` - -## Environment basics (Exercises 7.2.7) - -**Q1.** List three ways in which an environment differs from a list. - -**A1.** As mentioned in the book, here are a few ways in which environments differ from lists: - -| Property | List | Environment | -| :-----------------: | :-----: | :---------: | -| semantics | value | reference | -| data structure | linear | non-linear | -| duplicated names | allowed | not allowed | -| can have parents? | false | true | -| can contain itself? | false | true | - -**Q2.** Create an environment as illustrated by this picture. - - - -**A2.** Creating the environment illustrated in the picture: - - -```r -library(rlang) - -e <- env() -e$loop <- e -env_print(e) -#> -#> Parent: -#> Bindings: -#> β€’ loop: -``` - -The binding `loop` should have the same memory address as the environment `e`: - - -```r -lobstr::ref(e$loop) -#> β–ˆ [1:0x139a85110] -#> └─loop = [1:0x139a85110] -``` - -**Q3.** Create a pair of environments as illustrated by this picture. - - - -**A3.** Creating the specified environment: - - -```r -e1 <- env() -e2 <- env() - -e1$loop <- e2 -e2$deloop <- e1 - -# following should be the same -lobstr::obj_addrs(list(e1, e2$deloop)) -#> [1] "0x1083b4018" "0x1083b4018" -lobstr::obj_addrs(list(e2, e1$loop)) -#> [1] "0x108409438" "0x108409438" -``` - -**Q4.** Explain why `e[[1]]` and `e[c("a", "b")]` don't make sense when `e` is an environment. - -**A4.** An environment is a non-linear data structure, and has no concept of ordered elements. Therefore, indexing it (e.g. `e[[1]]`) doesn't make sense. - -Subsetting a list or a vector returns a subset of the underlying data structure. For example, subsetting a vector returns another vector. But it's unclear what subsetting an environment (e.g. `e[c("a", "b")]`) should return because there is no data structure to contain its returns. It can't be another environment since environments have reference semantics. - -**Q5.** Create a version of `env_poke()` that will only bind new names, never re-bind old names. Some programming languages only do this, and are known as [single assignment languages](https://en.wikipedia.org/wiki/Assignment_(computer_science)#Single_assignment). - -**A5.** Create a version of `env_poke()` that doesn't allow re-binding old names: - - -```r -env_poke2 <- function(env, nm, value) { - if (env_has(env, nm)) { - abort("Can't re-bind existing names.") - } - - env_poke(env, nm, value) -} -``` - -Making sure that it behaves as expected: - - -```r -e <- env(a = 1, b = 2, c = 3) - -# re-binding old names not allowed -env_poke2(e, "b", 4) -#> Error in `env_poke2()`: -#> ! Can't re-bind existing names. - -# binding new names allowed -env_poke2(e, "d", 8) -e$d -#> [1] 8 -``` - -Contrast this behavior with the following: - - -```r -e <- env(a = 1, b = 2, c = 3) - -e$b -#> [1] 2 - -# re-binding old names allowed -env_poke(e, "b", 4) -e$b -#> [1] 4 -``` - -**Q6.** What does this function do? How does it differ from `<<-` and why might you prefer it? - - -```r -rebind <- function(name, value, env = caller_env()) { - if (identical(env, empty_env())) { - stop("Can't find `", name, "`", call. = FALSE) - } else if (env_has(env, name)) { - env_poke(env, name, value) - } else { - rebind(name, value, env_parent(env)) - } -} -rebind("a", 10) -#> Error: Can't find `a` -a <- 5 -rebind("a", 10) -a -#> [1] 10 -``` - -**A6.** The downside of `<<-` is that it will create a new binding if it doesn't exist in the given environment, which is something that we may not wish: - - -```r -# `x` doesn't exist -exists("x") -#> [1] FALSE - -# so `<<-` will create one for us -{ - x <<- 5 -} - -# in the global environment -env_has(global_env(), "x") -#> x -#> TRUE -x -#> [1] 5 -``` - -But `rebind()` function will let us know if the binding doesn't exist, which is much safer: - - -```r -rebind <- function(name, value, env = caller_env()) { - if (identical(env, empty_env())) { - stop("Can't find `", name, "`", call. = FALSE) - } else if (env_has(env, name)) { - env_poke(env, name, value) - } else { - rebind(name, value, env_parent(env)) - } -} - -# doesn't exist -exists("abc") -#> [1] FALSE - -# so function will produce an error instead of creating it for us -rebind("abc", 10) -#> Error: Can't find `abc` - -# but it will work as expected when the variable already exists -abc <- 5 -rebind("abc", 10) -abc -#> [1] 10 -``` - -## Recursing over environments (Exercises 7.3.1) - -**Q1.** Modify `where()` to return _all_ environments that contain a binding for `name`. Carefully think through what type of object the function will need to return. - -**A1.** Here is a modified version of `where()` that returns _all_ environments that contain a binding for `name`. - -Since we anticipate more than one environment, we dynamically update a list each time an environment with the specified binding is found. It is important to initialize to an empty list since that signifies that given binding is not found in any of the environments. - - -```r -where <- function(name, env = caller_env()) { - env_list <- list() - - while (!identical(env, empty_env())) { - if (env_has(env, name)) { - env_list <- append(env_list, env) - } - - env <- env_parent(env) - } - - return(env_list) -} -``` - -Let's try it out: - - -```r -where("yyy") -#> list() - -x <- 5 -where("x") -#> [[1]] -#> - -where("mean") -#> [[1]] -#> - -library(dplyr, warn.conflicts = FALSE) -where("filter") -#> [[1]] -#> -#> attr(,"name") -#> [1] "package:dplyr" -#> attr(,"path") -#> [1] "/Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library/dplyr" -#> -#> [[2]] -#> -#> attr(,"name") -#> [1] "package:stats" -#> attr(,"path") -#> [1] "/Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library/stats" -detach("package:dplyr") -``` - -**Q2.** Write a function called `fget()` that finds only function objects. It should have two arguments, `name` and `env`, and should obey the regular scoping rules for functions: if there's an object with a matching name that's not a function, look in the parent. For an added challenge, also add an `inherits` argument which controls whether the function recurses up the parents or only looks in one environment. - -**A2.** Here is a function that recursively looks for function objects: - - -```r -fget <- function(name, env = caller_env(), inherits = FALSE) { - # we need only function objects - f_value <- mget(name, - envir = env, - mode = "function", - inherits = FALSE, # since we have our custom argument - ifnotfound = list(NULL) - ) - - if (!is.null(f_value[[1]])) { - # success case - f_value[[1]] - } else { - if (inherits && !identical(env, empty_env())) { - # recursive case - env <- env_parent(env) - fget(name, env, inherits = TRUE) - } else { - # base case - stop("No function objects with matching name was found.", call. = FALSE) - } - } -} -``` - -Let's try it out: - - -```r -fget("mean", inherits = FALSE) -#> Error: No function objects with matching name was found. - -fget("mean", inherits = TRUE) -#> function (x, ...) -#> UseMethod("mean") -#> -#> - -mean <- 5 -fget("mean", inherits = FALSE) -#> Error: No function objects with matching name was found. - -mean <- function() NULL -fget("mean", inherits = FALSE) -#> function() NULL -rm("mean") -``` - -## Special environments (Exercises 7.4.5) - -**Q1.** How is `search_envs()` different from `env_parents(global_env())`? - -**A1.** The `search_envs()` lists a chain of environments currently attached to the search path and contains exported functions from these packages. The search path always ends at the `{base}` package environment. The search path also includes the global environment. - - -```r -search_envs() -#> [[1]] $ -#> [[2]] $ -#> [[3]] $ -#> [[4]] $ -#> [[5]] $ -#> [[6]] $ -#> [[7]] $ -#> [[8]] $ -#> [[9]] $ -#> [[10]] $ -#> [[11]] $ -``` - -The `env_parents()` lists all parent environments up until the empty environment. Of course, the global environment itself is not included in this list. - - -```r -env_parents(global_env()) -#> [[1]] $ -#> [[2]] $ -#> [[3]] $ -#> [[4]] $ -#> [[5]] $ -#> [[6]] $ -#> [[7]] $ -#> [[8]] $ -#> [[9]] $ -#> [[10]] $ -#> [[11]] $ -``` - -**Q2.** Draw a diagram that shows the enclosing environments of this function: - - -```r -f1 <- function(x1) { - f2 <- function(x2) { - f3 <- function(x3) { - x1 + x2 + x3 - } - f3(3) - } - f2(2) -} -f1(1) -``` - -**A2.** I don't have access to the graphics software used to create diagrams in the book, so I am linking the diagram from the [official solutions manual](https://advanced-r-solutions.rbind.io/environments.html#special-environments), where you will also find a more detailed description for the figure: - - - -**Q3.** Write an enhanced version of `str()` that provides more information about functions. Show where the function was found and what environment it was defined in. - -**A3.** To write the required function, we can first re-purpose the `fget()` function we wrote above to return the environment in which it was found and its enclosing environment: - - -```r -fget2 <- function(name, env = caller_env()) { - # we need only function objects - f_value <- mget(name, - envir = env, - mode = "function", - inherits = FALSE, - ifnotfound = list(NULL) - ) - - if (!is.null(f_value[[1]])) { - # success case - list( - "where" = env, - "enclosing" = fn_env(f_value[[1]]) - ) - } else { - if (!identical(env, empty_env())) { - # recursive case - env <- env_parent(env) - fget2(name, env) - } else { - # base case - stop("No function objects with matching name was found.", call. = FALSE) - } - } -} -``` - -Let's try it out: - - -```r -fget2("mean") -#> $where -#> -#> -#> $enclosing -#> - -mean <- function() NULL -fget2("mean") -#> $where -#> -#> -#> $enclosing -#> -rm("mean") -``` - -We can now write the new version of `str()` as a wrapper around this function. We only need to foresee that the users might enter the function name either as a symbol or a string. - - -```r -str_function <- function(.f) { - fget2(as_string(ensym(.f))) -} -``` - -Let's first try it with `base::mean()`: - - -```r -str_function(mean) -#> $where -#> -#> -#> $enclosing -#> - -str_function("mean") -#> $where -#> -#> -#> $enclosing -#> -``` - -And then with our variant present in the global environment: - - -```r -mean <- function() NULL - -str_function(mean) -#> $where -#> -#> -#> $enclosing -#> - -str_function("mean") -#> $where -#> -#> -#> $enclosing -#> - -rm("mean") -``` - -## Call stacks (Exercises 7.5.5) - -**Q1.** Write a function that lists all the variables defined in the environment in which it was called. It should return the same results as `ls()`. - -**A1.** Here is a function that lists all the variables defined in the environment in which it was called: - - -```r -# let's first remove everything that exists in the global environment right now -# to test with only newly defined objects -rm(list = ls()) -rm(.Random.seed, envir = globalenv()) - -ls_env <- function(env = rlang::caller_env()) { - sort(rlang::env_names(env)) -} -``` - -The workhorse here is `rlang::caller_env()`, so let's also have a look at its definition: - - -```r -rlang::caller_env -#> function (n = 1) -#> { -#> parent.frame(n + 1) -#> } -#> -#> -``` - -Let's try it out: - -- In global environment: - - -```r -x <- "a" -y <- 1 - -ls_env() -#> [1] "ls_env" "x" "y" - -ls() -#> [1] "ls_env" "x" "y" -``` - -- In function environment: - - -```r -foo <- function() { - a <- "x" - b <- 2 - - print(ls_env()) - - print(ls()) -} - -foo() -#> [1] "a" "b" -#> [1] "a" "b" -``` - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1) -#> P datasets * 4.2.2 2022-10-31 [1] local -#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388) -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0) -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> lobstr 1.1.2 2022-06-22 [1] CRAN (R 4.2.0) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> png 0.1-7 2013-12-03 [1] CRAN (R 4.2.0) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble 3.1.8.9002 2022-10-16 [1] local -#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` - diff --git a/_book/Evaluation.md b/_book/Evaluation.md deleted file mode 100644 index 662ba05e..00000000 --- a/_book/Evaluation.md +++ /dev/null @@ -1,727 +0,0 @@ -# Evaluation - - - -Attaching the needed libraries: - - -```r -library(rlang) -``` - -## Evaluation basics (Exercises 20.2.4) - ---- - -**Q1.** Carefully read the documentation for `source()`. What environment does it use by default? What if you supply `local = TRUE`? How do you provide a custom environment? - -**A1.** The parameter `local` for `source()` decides the environment in which the parsed expressions are evaluated. - -By default `local = FALSE`, this corresponds to the user's workspace (the global environment, i.e.). - - -```r -withr::with_tempdir( - code = { - f <- tempfile() - writeLines("rlang::env_print()", f) - foo <- function() source(f, local = FALSE) - foo() - } -) -#> -#> Parent: -#> Bindings: -#> β€’ .Random.seed: -#> β€’ foo: -#> β€’ f: -``` - -If `local = TRUE`, then the environment from which `source()` is called will be used. - - -```r -withr::with_tempdir( - code = { - f <- tempfile() - writeLines("rlang::env_print()", f) - foo <- function() source(f, local = TRUE) - foo() - } -) -#> -#> Parent: -``` - -To specify a custom environment, the `sys.source()` function can be used, which provides an `envir` parameter. - ---- - -**Q2.** Predict the results of the following lines of code: - - -```r -eval(expr(eval(expr(eval(expr(2 + 2)))))) -eval(eval(expr(eval(expr(eval(expr(2 + 2))))))) -expr(eval(expr(eval(expr(eval(expr(2 + 2))))))) -``` - -**A2.** Correctly predicted πŸ˜‰ - - -```r -eval(expr(eval(expr(eval(expr(2 + 2)))))) -#> [1] 4 - -eval(eval(expr(eval(expr(eval(expr(2 + 2))))))) -#> [1] 4 - -expr(eval(expr(eval(expr(eval(expr(2 + 2))))))) -#> eval(expr(eval(expr(eval(expr(2 + 2)))))) -``` - ---- - -**Q3.** Fill in the function bodies below to re-implement `get()` using `sym()` and `eval()`, and `assign()` using `sym()`, `expr()`, and `eval()`. Don't worry about the multiple ways of choosing an environment that `get()` and `assign()` support; assume that the user supplies it explicitly. - - -```r -# name is a string -get2 <- function(name, env) {} -assign2 <- function(name, value, env) {} -``` - -**A3.** Here are the required re-implementations: - -- `get()` - - -```r -get2 <- function(name, env = caller_env()) { - name <- sym(name) - eval(name, env) -} - -x <- 2 - -get2("x") -#> [1] 2 -get("x") -#> [1] 2 - -y <- 1:4 -assign("y[1]", 2) - -get2("y[1]") -#> [1] 2 -get("y[1]") -#> [1] 2 -``` - -- `assign()` - - -```r -assign2 <- function(name, value, env = caller_env()) { - name <- sym(name) - eval(expr(!!name <- !!value), env) -} - -assign("y1", 4) -y1 -#> [1] 4 - -assign2("y2", 4) -y2 -#> [1] 4 -``` - ---- - -**Q4.** Modify `source2()` so it returns the result of *every* expression, not just the last one. Can you eliminate the for loop? - -**A4.** We can use `purrr::map()` to iterate over every expression and return result of every expression: - - -```r -source2 <- function(path, env = caller_env()) { - file <- paste(readLines(path, warn = FALSE), collapse = "\n") - exprs <- parse_exprs(file) - purrr::map(exprs, ~ eval(.x, env)) -} - -withr::with_tempdir( - code = { - f <- tempfile(fileext = ".R") - writeLines("1 + 1; 2 + 4", f) - source2(f) - } -) -#> [[1]] -#> [1] 2 -#> -#> [[2]] -#> [1] 6 -``` - ---- - -**Q5.** We can make `base::local()` slightly easier to understand by spreading out over multiple lines: - - -```r -local3 <- function(expr, envir = new.env()) { - call <- substitute(eval(quote(expr), envir)) - eval(call, envir = parent.frame()) -} -``` - -Explain how `local()` works in words. (Hint: you might want to `print(call)` to help understand what `substitute()` is doing, and read the documentation to remind yourself what environment `new.env()` will inherit from.) - -**A5.** In order to figure out how this function works, let's add the suggested `print(call)`: - - -```r -local3 <- function(expr, envir = new.env()) { - call <- substitute(eval(quote(expr), envir)) - print(call) - - eval(call, envir = parent.frame()) -} - -local3({ - x <- 10 - y <- 200 - x + y -}) -#> eval(quote({ -#> x <- 10 -#> y <- 200 -#> x + y -#> }), new.env()) -#> [1] 210 -``` - -As docs for `substitute()` mention: - -> Substituting and quoting often cause confusion when the argument is expression(...). The result is a call to the expression constructor function and needs to be evaluated with eval to give the actual expression object. - -Thus, to get the actual expression object, quoted expression needs to be evaluated using `eval()`: - - -```r -is_expression(eval(quote({ - x <- 10 - y <- 200 - x + y -}), new.env())) -#> [1] TRUE -``` - -Finally, the generated `call` is evaluated in the caller environment. So the final function call looks like the following: - - -```r -# outer environment -eval( - # inner environment - eval(quote({ - x <- 10 - y <- 200 - x + y - }), new.env()), - envir = parent.frame() -) -``` - -Note here that the bindings for `x` and `y` are found in the inner environment, while bindings for functions `eval()`, `quote()`, etc. are found in the outer environment. - ---- - -## Quosures (Exercises 20.3.6) - ---- - -**Q1.** Predict what each of the following quosures will return if evaluated. - - -```r -q1 <- new_quosure(expr(x), env(x = 1)) -q1 -#> -#> expr: ^x -#> env: 0x103b6fd78 -q2 <- new_quosure(expr(x + !!q1), env(x = 10)) -q2 -#> -#> expr: ^x + (^x) -#> env: 0x14af4ada0 -q3 <- new_quosure(expr(x + !!q2), env(x = 100)) -q3 -#> -#> expr: ^x + (^x + (^x)) -#> env: 0x103f88b78 -``` - -**A1.** Correctly predicted πŸ˜‰ - - -```r -q1 <- new_quosure(expr(x), env(x = 1)) -eval_tidy(q1) -#> [1] 1 - -q2 <- new_quosure(expr(x + !!q1), env(x = 10)) -eval_tidy(q2) -#> [1] 11 - -q3 <- new_quosure(expr(x + !!q2), env(x = 100)) -eval_tidy(q3) -#> [1] 111 -``` - ---- - -**Q2.** Write an `enenv()` function that captures the environment associated with an argument. (Hint: this should only require two function calls.) - -**A2.** We can make use of the `get_env()` helper to get the environment associated with an argument: - - -```r -enenv <- function(x) { - x <- enquo(x) - get_env(x) -} - -enenv(x) -#> - -foo <- function(x) enenv(x) -foo() -#> -``` - ---- - -## Data masks (Exercises 20.4.6) - ---- - -**Q1.** Why did I use a `for` loop in `transform2()` instead of `map()`? Consider `transform2(df, x = x * 2, x = x * 2)`. - -**A1.** To see why `map()` is not appropriate for this function, let's create a version of the function with `map()` and see what happens. - - -```r -transform2 <- function(.data, ...) { - dots <- enquos(...) - - for (i in seq_along(dots)) { - name <- names(dots)[[i]] - dot <- dots[[i]] - - .data[[name]] <- eval_tidy(dot, .data) - } - - .data -} - -transform3 <- function(.data, ...) { - dots <- enquos(...) - - purrr::map(dots, function(x, .data = .data) { - name <- names(x) - dot <- x - - .data[[name]] <- eval_tidy(dot, .data) - - .data - }) -} -``` - -When we use a `for()` loop, in each iteration, we are updating the `x` column with the current expression under evaluation. That is, repeatedly modifying the same column works. - - -```r -df <- data.frame(x = 1:3) -transform2(df, x = x * 2, x = x * 2) -#> x -#> 1 4 -#> 2 8 -#> 3 12 -``` - -If we use `map()` instead, we are trying to evaluate all expressions at the same time; i.e., the same column is being attempted to modify on using multiple expressions. - - -```r -df <- data.frame(x = 1:3) -transform3(df, x = x * 2, x = x * 2) -#> Error in eval_tidy(dot, .data): promise already under evaluation: recursive default argument reference or earlier problems? -``` - ---- - -**Q2.** Here's an alternative implementation of `subset2()`: - - -```r -subset3 <- function(data, rows) { - rows <- enquo(rows) - eval_tidy(expr(data[!!rows, , drop = FALSE]), data = data) -} -df <- data.frame(x = 1:3) -subset3(df, x == 1) -``` - -Compare and contrast `subset3()` to `subset2()`. What are its advantages and disadvantages? - -**A2.** Let's first juxtapose these functions and their outputs so that we can compare them better. - - -```r -subset2 <- function(data, rows) { - rows <- enquo(rows) - rows_val <- eval_tidy(rows, data) - stopifnot(is.logical(rows_val)) - - data[rows_val, , drop = FALSE] -} - -df <- data.frame(x = 1:3) -subset2(df, x == 1) -#> x -#> 1 1 -``` - - -```r -subset3 <- function(data, rows) { - rows <- enquo(rows) - eval_tidy(expr(data[!!rows, , drop = FALSE]), data = data) -} - -subset3(df, x == 1) -#> x -#> 1 1 -``` - -**Disadvantages of `subset3()` over `subset2()`** - -When the filtering conditions specified in `rows` don't evaluate to a logical, the function doesn't fail informatively. Indeed, it silently returns incorrect result. - - -```r -rm("x") -exists("x") -#> [1] FALSE - -subset2(df, x + 1) -#> Error in subset2(df, x + 1): is.logical(rows_val) is not TRUE - -subset3(df, x + 1) -#> x -#> 2 2 -#> 3 3 -#> NA NA -``` - -**Advantages of `subset3()` over `subset2()`** - -Some might argue that the function being shorter is an advantage, but this is very much a subjective preference. - ---- - -**Q3.** The following function implements the basics of `dplyr::arrange()`. Annotate each line with a comment explaining what it does. Can you explain why `!!.na.last` is strictly correct, but omitting the `!!` is unlikely to cause problems? - - -```r -arrange2 <- function(.df, ..., .na.last = TRUE) { - args <- enquos(...) - order_call <- expr(order(!!!args, na.last = !!.na.last)) - ord <- eval_tidy(order_call, .df) - stopifnot(length(ord) == nrow(.df)) - .df[ord, , drop = FALSE] -} -``` - -**A3.** Annotated version of the function: - - -```r -arrange2 <- function(.df, ..., .na.last = TRUE) { - # capture user-supplied expressions (and corresponding environments) as quosures - args <- enquos(...) - - # create a call object by splicing a list of quosures - order_call <- expr(order(!!!args, na.last = !!.na.last)) - - # and evaluate the constructed call in the data frame - ord <- eval_tidy(order_call, .df) - - # sanity check - stopifnot(length(ord) == nrow(.df)) - - .df[ord, , drop = FALSE] -} -``` - -To see why it doesn't matter whether whether we unquote the `.na.last` argument or not, let's have a look at this smaller example: - - -```r -x <- TRUE -eval(expr(c(x = !!x))) -#> x -#> TRUE -eval(expr(c(x = x))) -#> x -#> TRUE -``` - -As can be seen: - -- without unquoting, `.na.last` is found in the function environment -- with unquoting, `.na.last` is included in the `order` call object itself - ---- - -## Using tidy evaluation (Exercises 20.5.4) - ---- - -**Q1.** I've included an alternative implementation of `threshold_var()` below. What makes it different to the approach I used above? What makes it harder? - - -```r -threshold_var <- function(df, var, val) { - var <- ensym(var) - subset2(df, `$`(.data, !!var) >= !!val) -} -``` - -**A1.** First, let's compare the two definitions for the same function and make sure that they produce the same output: - - -```r -threshold_var_old <- function(df, var, val) { - var <- as_string(ensym(var)) - subset2(df, .data[[var]] >= !!val) -} - -threshold_var_new <- threshold_var - -df <- data.frame(x = 1:10) - -identical( - threshold_var(df, x, 8), - threshold_var(df, x, 8) -) -#> [1] TRUE -``` - -The key difference is in the subsetting operator used: - -- The old version uses non-quoting `[[` operator. Thus, `var` argument first needs to be converted to a string. -- The new version uses quoting `$` operator. Thus, `var` argument is first quoted and then unquoted (using `!!`). - ---- - -## Base evaluation (Exercises 20.6.3) - ---- - -**Q1.** Why does this function fail? - - -```r -lm3a <- function(formula, data) { - formula <- enexpr(formula) - lm_call <- expr(lm(!!formula, data = data)) - eval(lm_call, caller_env()) -} - -lm3a(mpg ~ disp, mtcars)$call -#> Error in as.data.frame.default(data, optional = TRUE): -#> cannot coerce class β€˜"function"’ to a data.frame -``` - -**A1.** This doesn't work because when `lm_call` call is evaluated in `caller_env()`, it finds a binding for `base::data()` function, and not `data` from execution environment. - -To make it work, we need to unquote `data` into the expression: - - -```r -lm3a <- function(formula, data) { - formula <- enexpr(formula) - lm_call <- expr(lm(!!formula, data = !!data)) - eval(lm_call, caller_env()) -} - -is_call(lm3a(mpg ~ disp, mtcars)$call) -#> [1] TRUE -``` - ---- - -**Q2.** When model building, typically the response and data are relatively constant while you rapidly experiment with different predictors. Write a small wrapper that allows you to reduce duplication in the code below. - - -```r -lm(mpg ~ disp, data = mtcars) -lm(mpg ~ I(1 / disp), data = mtcars) -lm(mpg ~ disp * cyl, data = mtcars) -``` - -**A2.** Here is a small wrapper that allows you to enter only the predictors: - - -```r -lm_custom <- function(data = mtcars, x, y = mpg) { - x <- enexpr(x) - y <- enexpr(y) - data <- enexpr(data) - - lm_call <- expr(lm(formula = !!y ~ !!x, data = !!data)) - - eval(lm_call, caller_env()) -} - -identical( - lm_custom(x = disp), - lm(mpg ~ disp, data = mtcars) -) -#> [1] TRUE - -identical( - lm_custom(x = I(1 / disp)), - lm(mpg ~ I(1 / disp), data = mtcars) -) -#> [1] TRUE - -identical( - lm_custom(x = disp * cyl), - lm(mpg ~ disp * cyl, data = mtcars) -) -#> [1] TRUE -``` - -But the function is flexible enough to also allow changing both the data and the dependent variable: - - -```r -lm_custom(data = iris, x = Sepal.Length, y = Petal.Width) -#> -#> Call: -#> lm(formula = Petal.Width ~ Sepal.Length, data = iris) -#> -#> Coefficients: -#> (Intercept) Sepal.Length -#> -3.2002 0.7529 -``` - ---- - -**Q3.** Another way to write `resample_lm()` would be to include the resample expression (`data[sample(nrow(data), replace = TRUE), , drop = FALSE]`) in the data argument. Implement that approach. What are the advantages? What are the disadvantages? - -**A3.** In this variant of `resample_lm()`, we are providing the resampled data as an argument. - - -```r -resample_lm3 <- function(formula, - data, - resample_data = data[sample(nrow(data), replace = TRUE), , drop = FALSE], - env = current_env()) { - formula <- enexpr(formula) - lm_call <- expr(lm(!!formula, data = resample_data)) - expr_print(lm_call) - eval(lm_call, env) -} - -df <- data.frame(x = 1:10, y = 5 + 3 * (1:10) + round(rnorm(10), 2)) -resample_lm3(y ~ x, data = df) -#> lm(y ~ x, data = resample_data) -#> -#> Call: -#> lm(formula = y ~ x, data = resample_data) -#> -#> Coefficients: -#> (Intercept) x -#> 2.654 3.420 -``` - -This makes use of R's lazy evaluation of function arguments. That is, `resample_data` argument will be evaluated only when it is needed in the function. - ---- - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Expressions.md b/_book/Expressions.md deleted file mode 100644 index 65788237..00000000 --- a/_book/Expressions.md +++ /dev/null @@ -1,1051 +0,0 @@ -# Expressions - - - -Attaching the needed libraries: - - -```r -library(rlang, warn.conflicts = FALSE) -library(lobstr, warn.conflicts = FALSE) -``` - -## Abstract syntax trees (Exercises 18.2.4) - -**Q1.** Reconstruct the code represented by the trees below: - - -``` -#> β–ˆβ”€f -#> β””β”€β–ˆβ”€g -#> β””β”€β–ˆβ”€h -#> β–ˆβ”€`+` -#> β”œβ”€β–ˆβ”€`+` -#> β”‚ β”œβ”€1 -#> β”‚ └─2 -#> └─3 -#> β–ˆβ”€`*` -#> β”œβ”€β–ˆβ”€`(` -#> β”‚ β””β”€β–ˆβ”€`+` -#> β”‚ β”œβ”€x -#> β”‚ └─y -#> └─z -``` - -**A1.** Below is the reconstructed code. - - -```r -f(g(h())) -1 + 2 + 3 -(x + y) * z -``` - -We can confirm it by drawing ASTs for them: - - -```r -ast(f(g(h()))) -#> β–ˆβ”€f -#> β””β”€β–ˆβ”€g -#> β””β”€β–ˆβ”€h - -ast(1 + 2 + 3) -#> β–ˆβ”€`+` -#> β”œβ”€β–ˆβ”€`+` -#> β”‚ β”œβ”€1 -#> β”‚ └─2 -#> └─3 - -ast((x + y) * z) -#> β–ˆβ”€`*` -#> β”œβ”€β–ˆβ”€`(` -#> β”‚ β””β”€β–ˆβ”€`+` -#> β”‚ β”œβ”€x -#> β”‚ └─y -#> └─z -``` - -**Q2.** Draw the following trees by hand and then check your answers with `ast()`. - - -```r -f(g(h(i(1, 2, 3)))) -f(1, g(2, h(3, i()))) -f(g(1, 2), h(3, i(4, 5))) -``` - -**A2.** Successfully drawn by hand. Checking using `ast()`: - - -```r -ast(f(g(h(i(1, 2, 3))))) -#> β–ˆβ”€f -#> β””β”€β–ˆβ”€g -#> β””β”€β–ˆβ”€h -#> β””β”€β–ˆβ”€i -#> β”œβ”€1 -#> β”œβ”€2 -#> └─3 - -ast(f(1, g(2, h(3, i())))) -#> β–ˆβ”€f -#> β”œβ”€1 -#> β””β”€β–ˆβ”€g -#> β”œβ”€2 -#> β””β”€β–ˆβ”€h -#> β”œβ”€3 -#> β””β”€β–ˆβ”€i - -ast(f(g(1, 2), h(3, i(4, 5)))) -#> β–ˆβ”€f -#> β”œβ”€β–ˆβ”€g -#> β”‚ β”œβ”€1 -#> β”‚ └─2 -#> β””β”€β–ˆβ”€h -#> β”œβ”€3 -#> β””β”€β–ˆβ”€i -#> β”œβ”€4 -#> └─5 -``` - -**Q3.** What's happening with the ASTs below? (Hint: carefully read `?"^"`.) - - -```r -ast(`x` + `y`) -#> β–ˆβ”€`+` -#> β”œβ”€x -#> └─y -ast(x**y) -#> β–ˆβ”€`^` -#> β”œβ”€x -#> └─y -ast(1 -> x) -#> β–ˆβ”€`<-` -#> β”œβ”€x -#> └─1 -``` - -**A3.** The `str2expression()` helps make sense of these ASTs. - -The non-syntactic names are parsed to names. Thus, backticks have been removed in the AST. - - -```r -str2expression("`x` + `y`") -#> expression(x + y) -``` - -As mentioned in the docs for `^`: - -> \*\* is translated in the parser to \^ - - -```r -str2expression("x**y") -#> expression(x^y) -``` - -The rightward assignment is parsed to leftward assignment: - - -```r -str2expression("1 -> x") -#> expression(x <- 1) -``` - -**Q4.** What is special about the AST below? - - -```r -ast(function(x = 1, y = 2) {}) -#> β–ˆβ”€`function` -#> β”œβ”€β–ˆβ”€x = 1 -#> β”‚ └─y = 2 -#> β”œβ”€β–ˆβ”€`{` -#> └─ -``` - -**A4.** As mentioned in [this](https://adv-r.hadley.nz/functions.html#fun-components) section: - -> Like all objects in R, functions can also possess any number of additional `attributes()`. One attribute used by base R is `srcref`, short for source reference. It points to the source code used to create the function. The `srcref` is used for printing because, unlike `body()`, it contains code comments and other formatting. - -Therefore, the last leaf in this AST, although not specified in the function call, represents source reference attribute. - -**Q5.** What does the call tree of an `if` statement with multiple `else if` conditions look like? Why? - -**A5.** There is nothing special about this tree. It just shows the nested loop structure inherent to code with `if` and multiple `else if` statements. - - -```r -ast(if (FALSE) 1 else if (FALSE) 2 else if (FALSE) 3 else 4) -#> β–ˆβ”€`if` -#> β”œβ”€FALSE -#> β”œβ”€1 -#> β””β”€β–ˆβ”€`if` -#> β”œβ”€FALSE -#> β”œβ”€2 -#> β””β”€β–ˆβ”€`if` -#> β”œβ”€FALSE -#> β”œβ”€3 -#> └─4 -``` - -## Expressions (Exercises 18.3.5) - -**Q1.** Which two of the six types of atomic vector can't appear in an expression? Why? Similarly, why can't you create an expression that contains an atomic vector of length greater than one? - -**A1.** Out of the six types of atomic vectors, the two that can't appear in an expression are: complex and raw. - -Complex numbers are created via a **function call** (using `+`), as can be seen by its AST: - - -```r -x_complex <- expr(1 + 1i) -typeof(x_complex) -#> [1] "language" - -ast(1 + 1i) -#> β–ˆβ”€`+` -#> β”œβ”€1 -#> └─1i -``` - -Similarly, for raw vectors (using `raw()`): - - -```r -x_raw <- expr(raw(2)) -typeof(x_raw) -#> [1] "language" - -ast(raw(2)) -#> β–ˆβ”€raw -#> └─2 -``` - -Contrast this with other atomic vectors: - - -```r -x_int <- expr(2L) -typeof(x_int) -#> [1] "integer" - -ast(2L) -#> 2L -``` - -For the same reason, you can't you create an expression that contains an atomic vector of length greater than one since that itself is a function call that uses `c()` function: - - -```r -x_vec <- expr(c(1, 2)) -typeof(x_vec) -#> [1] "language" - -ast(c(1, 2)) -#> β–ˆβ”€c -#> β”œβ”€1 -#> └─2 -``` - -**Q2.** What happens when you subset a call object to remove the first element? e.g. `expr(read.csv("foo.csv", header = TRUE))[-1]`. Why? - -**A2.** A captured function call like the following creates a call object: - - -```r -expr(read.csv("foo.csv", header = TRUE)) -#> read.csv("foo.csv", header = TRUE) - -typeof(expr(read.csv("foo.csv", header = TRUE))) -#> [1] "language" -``` - -As mentioned in the [respective section](https://adv-r.hadley.nz/expressions.html#function-position): - -> The first element of the call object is the function position. - -Therefore, when the first element in the call object is removed, the next one moves in the function position, and we get the observed output: - - -```r -expr(read.csv("foo.csv", header = TRUE))[-1] -#> "foo.csv"(header = TRUE) -``` - -**Q3.** Describe the differences between the following call objects. - - -```r -x <- 1:10 -call2(median, x, na.rm = TRUE) -call2(expr(median), x, na.rm = TRUE) -call2(median, expr(x), na.rm = TRUE) -call2(expr(median), expr(x), na.rm = TRUE) -``` - -**A4.** The differences in the constructed call objects are due to the different *type* of arguments supplied to first two parameters in the `call2()` function. - -Types of arguments supplied to `.fn`: - - -```r -typeof(median) -#> [1] "closure" -typeof(expr(median)) -#> [1] "symbol" -``` - -Types of arguments supplied to the dynamic dots: - - -```r -x <- 1:10 -typeof(x) -#> [1] "integer" -typeof(expr(x)) -#> [1] "symbol" -``` - -The following outputs can be understood using the following properties: - -- when `.fn` argument is a `closure`, that function is inlined in the constructed function call -- when `x` is not a symbol, its value is passed to the function call - - -```r -x <- 1:10 - -call2(median, x, na.rm = TRUE) -#> (function (x, na.rm = FALSE, ...) -#> UseMethod("median"))(1:10, na.rm = TRUE) - -call2(expr(median), x, na.rm = TRUE) -#> median(1:10, na.rm = TRUE) - -call2(median, expr(x), na.rm = TRUE) -#> (function (x, na.rm = FALSE, ...) -#> UseMethod("median"))(x, na.rm = TRUE) - -call2(expr(median), expr(x), na.rm = TRUE) -#> median(x, na.rm = TRUE) -``` - -Importantly, all of the constructed call objects evaluate to give the same result: - - -```r -x <- 1:10 - -eval(call2(median, x, na.rm = TRUE)) -#> [1] 5.5 - -eval(call2(expr(median), x, na.rm = TRUE)) -#> [1] 5.5 - -eval(call2(median, expr(x), na.rm = TRUE)) -#> [1] 5.5 - -eval(call2(expr(median), expr(x), na.rm = TRUE)) -#> [1] 5.5 -``` - -**Q4.** `call_standardise()` doesn't work so well for the following calls. Why? What makes `mean()` special? - - -```r -call_standardise(quote(mean(1:10, na.rm = TRUE))) -#> mean(x = 1:10, na.rm = TRUE) -call_standardise(quote(mean(n = T, 1:10))) -#> mean(x = 1:10, n = T) -call_standardise(quote(mean(x = 1:10, , TRUE))) -#> mean(x = 1:10, , TRUE) -``` - -**A4.** This is because of the ellipsis in `mean()` function signature: - - -```r -mean -#> function (x, ...) -#> UseMethod("mean") -#> -#> -``` - -As mentioned in the respective [section](If the function uses ... it’s not possible to standardise all arguments.): - -> If the function uses `...` it’s not possible to standardise all arguments. - -`mean()` is an S3 generic and the dots are passed to underlying S3 methods. - -So, the output can be improved using a specific method. For example: - - -```r -call_standardise(quote(mean.default(n = T, 1:10))) -#> mean.default(x = 1:10, na.rm = T) -``` - -**Q5.** Why does this code not make sense? - - -```r -x <- expr(foo(x = 1)) -names(x) <- c("x", "y") -``` - -**A5.** This doesn't make sense because the first position in a call object is reserved for function (function position), and so assigning names to this element will just be ignored by R: - - -```r -x <- expr(foo(x = 1)) -x -#> foo(x = 1) - -names(x) <- c("x", "y") -x -#> foo(y = 1) -``` - -**Q6.** Construct the expression `if(x > 1) "a" else "b"` using multiple calls to `call2()`. How does the code structure reflect the structure of the AST? - -**A6.** Using multiple calls to construct the required expression: - - -```r -x <- 5 -call_obj1 <- call2(">", expr(x), 1) -call_obj1 -#> x > 1 - -call_obj2 <- call2("if", cond = call_obj1, cons.expr = "a", alt.expr = "b") -call_obj2 -#> if (x > 1) "a" else "b" -``` - -This construction follows from the prefix form of this expression, revealed by its AST: - - -```r -ast(if (x > 1) "a" else "b") -#> β–ˆβ”€`if` -#> β”œβ”€β–ˆβ”€`>` -#> β”‚ β”œβ”€x -#> β”‚ └─1 -#> β”œβ”€"a" -#> └─"b" -``` - -## Parsing and grammar (Exercises 18.4.4) - -**Q1.** R uses parentheses in two slightly different ways as illustrated by these two calls: - - -```r -f((1)) -`(`(1 + 1) -``` - -Compare and contrast the two uses by referencing the AST. - -**A1.** Let's first have a look at the AST: - - -```r -ast(f((1))) -#> β–ˆβ”€f -#> β””β”€β–ˆβ”€`(` -#> └─1 -ast(`(`(1 + 1)) -#> β–ˆβ”€`(` -#> β””β”€β–ˆβ”€`+` -#> β”œβ”€1 -#> └─1 -``` - -As, you can see `(` is being used in two separate ways: - -- As a function in its own right ``"`(`"`` -- As part of the prefix syntax (`f()`) - -This is why, in the AST for `f((1))`, we see only one ``"`(`"`` (the first use case), and not for `f()`, which is part of the function syntax (the second use case). - -**Q2.** `=` can also be used in two ways. Construct a simple example that shows both uses. - -**A2.** Here is a simple example illustrating how `=` can also be used in two ways: - -- for assignment -- for named arguments in function calls - - -```r -m <- mean(x = 1) -``` - -We can also have a look at its AST: - - -```r -ast({ - m <- mean(x = 1) -}) -#> β–ˆβ”€`{` -#> β””β”€β–ˆβ”€`<-` -#> β”œβ”€m -#> β””β”€β–ˆβ”€mean -#> └─x = 1 -``` - -**Q3.** Does `-2^2` yield 4 or -4? Why? - -**A3.** The expression `-2^2` evaluates to -4 because the operator `^` has higher precedence than the unary `-` operator: - - -```r --2^2 -#> [1] -4 -``` - -The same can also be seen by its AST: - - -```r -ast(-2^2) -#> β–ˆβ”€`-` -#> β””β”€β–ˆβ”€`^` -#> β”œβ”€2 -#> └─2 -``` - -A less confusing way to write this would be: - - -```r --(2^2) -#> [1] -4 -``` - -**Q4.** What does `!1 + !1` return? Why? - -**A3.** The expression `!1 + !1` evaluates to FALSE. - -This is because the `!` operator has higher precedence than the unary `+` operator. Thus, `!1` evaluates to `FALSE`, which is added to `1 + FALSE`, which evaluates to `1`, and then logically negated to `!1`, or `FALSE`. - -This can be easily seen by its AST: - - -```r -ast(!1 + !1) -#> β–ˆβ”€`!` -#> β””β”€β–ˆβ”€`+` -#> β”œβ”€1 -#> β””β”€β–ˆβ”€`!` -#> └─1 -``` - -**Q5.** Why does `x1 <- x2 <- x3 <- 0` work? Describe the two reasons. - -**A5.** There are two reasons why the following works as expected: - - -```r -x1 <- x2 <- x3 <- 0 -``` - -- The `<-` operator is right associative. - -Therefore, the order of assignment here is: - -```r -(x3 <- 0) -(x2 <- x3) -(x1 <- x2) -``` - -- The `<-` operator invisibly returns the assigned value. - - -```r -(x <- 1) -#> [1] 1 -``` - -This is easy to surmise from its AST: - - -```r -ast(x1 <- x2 <- x3 <- 0) -#> β–ˆβ”€`<-` -#> β”œβ”€x1 -#> β””β”€β–ˆβ”€`<-` -#> β”œβ”€x2 -#> β””β”€β–ˆβ”€`<-` -#> β”œβ”€x3 -#> └─0 -``` - -**Q6.** Compare the ASTs of `x + y %+% z` and `x ^ y %+% z`. What have you learned about the precedence of custom infix functions? - -**A6.** Looking at the ASTs for these expressions, - - -```r -ast(x + y %+% z) -#> β–ˆβ”€`+` -#> β”œβ”€x -#> β””β”€β–ˆβ”€`%+%` -#> β”œβ”€y -#> └─z - -ast(x^y %+% z) -#> β–ˆβ”€`%+%` -#> β”œβ”€β–ˆβ”€`^` -#> β”‚ β”œβ”€x -#> β”‚ └─y -#> └─z -``` - -we can say that the custom infix operator `%+%` has: - -- higher precedence than the `+` operator -- lower precedence than the `^` operator - -**Q7.** What happens if you call `parse_expr()` with a string that generates multiple expressions? e.g. `parse_expr("x + 1; y + 1")` - -**A7.** It produced an error: - - -```r -parse_expr("x + 1; y + 1") -#> Error in `parse_expr()`: -#> ! `x` must contain exactly 1 expression, not 2. -``` - -This is expected based on the docs: - -> parse_expr() returns one expression. If the text contains more than one expression (separated by semicolons or new lines), an error is issued. - -We instead need to use `parse_exprs()`: - - -```r -parse_exprs("x + 1; y + 1") -#> [[1]] -#> x + 1 -#> -#> [[2]] -#> y + 1 -``` - -**Q8.** What happens if you attempt to parse an invalid expression? e.g. `"a +"` or `"f())"`. - -**A8.** An invalid expression produces an error: - - -```r -parse_expr("a +") -#> Error in parse(text = elt): :2:0: unexpected end of input -#> 1: a + -#> ^ - -parse_expr("f())") -#> Error in parse(text = elt): :1:4: unexpected ')' -#> 1: f()) -#> ^ -``` - -Since the underlying `parse()` function produces an error: - - -```r -parse(text = "a +") -#> Error in parse(text = "a +"): :2:0: unexpected end of input -#> 1: a + -#> ^ - -parse(text = "f())") -#> Error in parse(text = "f())"): :1:4: unexpected ')' -#> 1: f()) -#> ^ -``` - -**Q9.** `deparse()` produces vectors when the input is long. For example, the following call produces a vector of length two: - - -```r -expr <- expr(g(a + b + c + d + e + f + g + h + i + j + k + l + - m + n + o + p + q + r + s + t + u + v + w + x + y + z)) -deparse(expr) -``` - -What does `expr_text()` do instead? - -**A9.** The only difference between `deparse()` and `expr_text()` is that the latter turns the (possibly multi-line) expression into a single string. - - -```r -expr <- expr(g(a + b + c + d + e + f + g + h + i + j + k + l + - m + n + o + p + q + r + s + t + u + v + w + x + y + z)) - -deparse(expr) -#> [1] "g(a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + " -#> [2] " p + q + r + s + t + u + v + w + x + y + z)" - -expr_text(expr) -#> [1] "g(a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + \n p + q + r + s + t + u + v + w + x + y + z)" -``` - -**Q10.** `pairwise.t.test()` assumes that `deparse()` always returns a length one character vector. Can you construct an input that violates this expectation? What happens? - -**A10** Since R 4.0, it is not possible to violate this expectation since the new implementation produces a single string no matter the input: - -> New function `deparse1()` produces one string, wrapping `deparse()`, to be used typically in `deparse1(substitute(*))` - -## Walking AST with recursive functions (Exercises 18.5.3) - -**Q1.** `logical_abbr()` returns `TRUE` for `T(1, 2, 3)`. How could you modify `logical_abbr_rec()` so that it ignores function calls that use `T` or `F`? - -**A1.** To avoid function calls that use `T` or `F`, we just need to ignore the function position in call objects: - - - -Let's try it out: - - -```r -logical_abbr_rec(expr(T(1, 2, 3))) -#> [1] FALSE - -logical_abbr_rec(expr(F(1, 2, 3))) -#> [1] FALSE - -logical_abbr_rec(expr(T)) -#> [1] TRUE - -logical_abbr_rec(expr(F)) -#> [1] TRUE -``` - -**Q2.** `logical_abbr()` works with expressions. It currently fails when you give it a function. Why? How could you modify `logical_abbr()` to make it work? What components of a function will you need to recurse over? - - -```r -logical_abbr(function(x = TRUE) { - g(x + T) -}) -``` - -**A2.** Surprisingly, `logical_abbr()` currently doesn't fail with closures: - - - -To see why, let's see what type of object is produced when we capture user provided closure: - - -```r -print_enexpr <- function(.f) { - print(typeof(enexpr(.f))) - print(is.call(enexpr(.f))) -} - -print_enexpr(function(x = TRUE) { - g(x + T) -}) -#> [1] "language" -#> [1] TRUE -``` - -Given that closures are converted to `call` objects, it is not a surprise that the function works: - - -```r -logical_abbr(function(x = TRUE) { - g(x + T) -}) -#> [1] TRUE -``` - -The function only fails if it can't find any negative case. For example, instead of returning `FALSE`, this produces an error for reasons that remain (as of yet) elusive to me: - - - - -```r -logical_abbr(function(x = TRUE) { - g(x + TRUE) -}) -#> Error: Don't know how to handle type integer -``` - -**Q3.** Modify `find_assign` to also detect assignment using replacement functions, i.e. `names(x) <- y`. - -**A3.** Although both simple assignment (`x <- y`) and assignment using replacement functions (`names(x) <- y`) have `<-` operator in their call, in the latter case, `names(x)` will be a call object and not a symbol: - - -```r -expr1 <- expr(names(x) <- y) -as.list(expr1) -#> [[1]] -#> `<-` -#> -#> [[2]] -#> names(x) -#> -#> [[3]] -#> y -typeof(expr1[[2]]) -#> [1] "language" - -expr2 <- expr(x <- y) -as.list(expr2) -#> [[1]] -#> `<-` -#> -#> [[2]] -#> x -#> -#> [[3]] -#> y -typeof(expr2[[2]]) -#> [1] "symbol" -``` - -That's how we can detect this kind of assignment by checking if the second element of the expression is a `symbol` or `language` type object. - - -```r -expr_type <- function(x) { - if (is_syntactic_literal(x)) { - "constant" - } else if (is.symbol(x)) { - "symbol" - } else if (is.call(x)) { - "call" - } else if (is.pairlist(x)) { - "pairlist" - } else { - typeof(x) - } -} - -switch_expr <- function(x, ...) { - switch(expr_type(x), - ..., - stop("Don't know how to handle type ", typeof(x), call. = FALSE) - ) -} - -flat_map_chr <- function(.x, .f, ...) { - purrr::flatten_chr(purrr::map(.x, .f, ...)) -} - -extract_symbol <- function(x) { - if (is_symbol(x[[2]])) { - as_string(x[[2]]) - } else { - extract_symbol(as.list(x[[2]])) - } -} - -find_assign_call <- function(x) { - if (is_call(x, "<-") && is_symbol(x[[2]])) { - lhs <- as_string(x[[2]]) - children <- as.list(x)[-1] - } else if (is_call(x, "<-") && is_call(x[[2]])) { - lhs <- extract_symbol(as.list(x[[2]])) - children <- as.list(x)[-1] - } else { - lhs <- character() - children <- as.list(x) - } - - c(lhs, flat_map_chr(children, find_assign_rec)) -} - -find_assign_rec <- function(x) { - switch_expr(x, - # Base cases - constant = , - symbol = character(), - - # Recursive cases - pairlist = flat_map_chr(x, find_assign_rec), - call = find_assign_call(x) - ) -} - -find_assign <- function(x) find_assign_rec(enexpr(x)) -``` - -Let's try it out: - - -```r -find_assign(names(x)) -#> character(0) - -find_assign(names(x) <- y) -#> [1] "x" - -find_assign(names(f(x)) <- y) -#> [1] "x" - -find_assign(names(x) <- y <- z <- NULL) -#> [1] "x" "y" "z" - -find_assign(a <- b <- c <- 1) -#> [1] "a" "b" "c" - -find_assign(system.time(x <- print(y <- 5))) -#> [1] "x" "y" -``` - -**Q4.** Write a function that extracts all calls to a specified function. - -**A4.** Here is a function that extracts all calls to a specified function: - - -```r -find_function_call <- function(x, .f) { - if (is_call(x)) { - if (is_call(x, .f)) { - list(x) - } else { - purrr::map(as.list(x), ~ find_function_call(.x, .f)) %>% - purrr::compact() %>% - unlist(use.names = FALSE) - } - } -} - -# example-1: with infix operator `:` -find_function_call(expr(mean(1:2)), ":") -#> [[1]] -#> 1:2 - -find_function_call(expr(sum(mean(1:2))), ":") -#> [[1]] -#> 1:2 - -find_function_call(expr(list(1:5, 4:6, 3:9)), ":") -#> [[1]] -#> 1:5 -#> -#> [[2]] -#> 4:6 -#> -#> [[3]] -#> 3:9 - -find_function_call(expr(list(1:5, sum(4:6), mean(3:9))), ":") -#> [[1]] -#> 1:5 -#> -#> [[2]] -#> 4:6 -#> -#> [[3]] -#> 3:9 - -# example-2: with assignment operator `<-` -find_function_call(expr(names(x)), "<-") -#> NULL - -find_function_call(expr(names(x) <- y), "<-") -#> [[1]] -#> names(x) <- y - -find_function_call(expr(names(f(x)) <- y), "<-") -#> [[1]] -#> names(f(x)) <- y - -find_function_call(expr(names(x) <- y <- z <- NULL), "<-") -#> [[1]] -#> names(x) <- y <- z <- NULL - -find_function_call(expr(a <- b <- c <- 1), "<-") -#> [[1]] -#> a <- b <- c <- 1 - -find_function_call(expr(system.time(x <- print(y <- 5))), "<-") -#> [[1]] -#> x <- print(y <- 5) -``` - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1) -#> P datasets * 4.2.2 2022-10-31 [1] local -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> lobstr * 1.1.2 2022-06-22 [1] CRAN (R 4.2.0) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` - diff --git a/_book/Function-factories.md b/_book/Function-factories.md deleted file mode 100644 index f29608ab..00000000 --- a/_book/Function-factories.md +++ /dev/null @@ -1,725 +0,0 @@ -# Function factories - - - -Attaching the needed libraries: - - -```r -library(rlang, warn.conflicts = FALSE) -library(ggplot2, warn.conflicts = FALSE) -``` - -## Factory fundamentals (Exercises 10.2.6) - ---- - -**Q1.** The definition of `force()` is simple: - - -```r -force -#> function (x) -#> x -#> -#> -``` - -Why is it better to `force(x)` instead of just `x`? - -**A1.** Due to lazy evaluation, argument to a function won't be evaluated until its value is needed. But sometimes we may want to have eager evaluation, and using `force()` makes this intent clearer. - ---- - -**Q2.** Base R contains two function factories, `approxfun()` and `ecdf()`. Read their documentation and experiment to figure out what the functions do and what they return. - -**A2.** About the two function factories- - -- `approxfun()` - -This function factory returns a function performing the linear (or constant) interpolation. - - -```r -x <- 1:10 -y <- rnorm(10) -f <- approxfun(x, y) -f -#> function (v) -#> .approxfun(x, y, v, method, yleft, yright, f, na.rm) -#> -#> -f(x) -#> [1] -0.7786629 -0.3894764 -2.0337983 -0.9823731 0.2478901 -#> [6] -2.1038646 -0.3814180 2.0749198 1.0271384 0.4730142 -curve(f(x), 0, 11) -``` - - - -- `ecdf()` - -This function factory computes an empirical cumulative distribution function. - - -```r -x <- rnorm(12) -f <- ecdf(x) -f -#> Empirical CDF -#> Call: ecdf(x) -#> x[1:12] = -1.8793, -1.3221, -1.2392, ..., 1.1604, 1.7956 -f(seq(-2, 2, by = 0.1)) -#> [1] 0.00000000 0.00000000 0.08333333 0.08333333 0.08333333 -#> [6] 0.08333333 0.08333333 0.16666667 0.25000000 0.25000000 -#> [11] 0.33333333 0.33333333 0.33333333 0.41666667 0.41666667 -#> [16] 0.41666667 0.41666667 0.50000000 0.58333333 0.58333333 -#> [21] 0.66666667 0.75000000 0.75000000 0.75000000 0.75000000 -#> [26] 0.75000000 0.75000000 0.75000000 0.75000000 0.83333333 -#> [31] 0.83333333 0.83333333 0.91666667 0.91666667 0.91666667 -#> [36] 0.91666667 0.91666667 0.91666667 1.00000000 1.00000000 -#> [41] 1.00000000 -``` - ---- - -**Q3.** Create a function `pick()` that takes an index, `i`, as an argument and returns a function with an argument `x` that subsets `x` with `i`. - - -```r -pick(1)(x) -# should be equivalent to -x[[1]] - -lapply(mtcars, pick(5)) -# should be equivalent to -lapply(mtcars, function(x) x[[5]]) -``` - -**A3.** To write desired function, we just need to make sure that the argument `i` is eagerly evaluated. - - -```r -pick <- function(i) { - force(i) - function(x) x[[i]] -} -``` - -Testing it with specified test cases: - - -```r -x <- list("a", "b", "c") -identical(x[[1]], pick(1)(x)) -#> [1] TRUE - -identical( - lapply(mtcars, pick(5)), - lapply(mtcars, function(x) x[[5]]) -) -#> [1] TRUE -``` - ---- - -**Q4.** Create a function that creates functions that compute the i^th^ [central moment](http://en.wikipedia.org/wiki/Central_moment) of a numeric vector. You can test it by running the following code: - - -```r -m1 <- moment(1) -m2 <- moment(2) -x <- runif(100) -stopifnot(all.equal(m1(x), 0)) -stopifnot(all.equal(m2(x), var(x) * 99 / 100)) -``` - -**A4.** The following function satisfied the specified requirements: - - -```r -moment <- function(k) { - force(k) - - function(x) (sum((x - mean(x))^k)) / length(x) -} -``` - -Testing it with specified test cases: - - -```r -m1 <- moment(1) -m2 <- moment(2) -x <- runif(100) - -stopifnot(all.equal(m1(x), 0)) -stopifnot(all.equal(m2(x), var(x) * 99 / 100)) -``` - ---- - -**Q5.** What happens if you don't use a closure? Make predictions, then verify with the code below. - - -```r -i <- 0 -new_counter2 <- function() { - i <<- i + 1 - i -} -``` - -**A5.** In case closures are not used in this context, the counts are stored in a global variable, which can be modified by other processes or even deleted. - - -```r -new_counter2() -#> [1] 1 - -new_counter2() -#> [1] 2 - -new_counter2() -#> [1] 3 - -i <- 20 -new_counter2() -#> [1] 21 -``` - ---- - -**Q6.** What happens if you use `<-` instead of `<<-`? Make predictions, then verify with the code below. - - -```r -new_counter3 <- function() { - i <- 0 - function() { - i <- i + 1 - i - } -} -``` - -**A6.** In this case, the function will always return `1`. - - -```r -new_counter3() -#> function() { -#> i <- i + 1 -#> i -#> } -#> - -new_counter3() -#> function() { -#> i <- i + 1 -#> i -#> } -#> -#> -``` - ---- - -## Graphical factories (Exercises 10.3.4) - ---- - -**Q1.** Compare and contrast `ggplot2::label_bquote()` with `scales::number_format()`. - -**A1.** To compare and contrast, let's first look at the source code for these functions: - -- `ggplot2::label_bquote()` - - -```r -ggplot2::label_bquote -#> function (rows = NULL, cols = NULL, default) -#> { -#> cols_quoted <- substitute(cols) -#> rows_quoted <- substitute(rows) -#> call_env <- env_parent() -#> fun <- function(labels) { -#> quoted <- resolve_labeller(rows_quoted, cols_quoted, -#> labels) -#> if (is.null(quoted)) { -#> return(label_value(labels)) -#> } -#> evaluate <- function(...) { -#> params <- list(...) -#> params <- as_environment(params, call_env) -#> eval(substitute(bquote(expr, params), list(expr = quoted))) -#> } -#> list(inject(mapply(evaluate, !!!labels, SIMPLIFY = FALSE))) -#> } -#> structure(fun, class = "labeller") -#> } -#> -#> -``` - -- `scales::number_format()` - - -```r -scales::number_format -#> function (accuracy = NULL, scale = 1, prefix = "", suffix = "", -#> big.mark = " ", decimal.mark = ".", style_positive = c("none", -#> "plus"), style_negative = c("hyphen", "minus", "parens"), -#> scale_cut = NULL, trim = TRUE, ...) -#> { -#> force_all(accuracy, scale, prefix, suffix, big.mark, decimal.mark, -#> style_positive, style_negative, scale_cut, trim, ...) -#> function(x) { -#> number(x, accuracy = accuracy, scale = scale, prefix = prefix, -#> suffix = suffix, big.mark = big.mark, decimal.mark = decimal.mark, -#> style_positive = style_positive, style_negative = style_negative, -#> scale_cut = scale_cut, trim = trim, ...) -#> } -#> } -#> -#> -``` - -Both of these functions return formatting functions used to style the facets labels and other labels to have the desired format in `{ggplot2}` plots. - -For example, using plotmath expression in the facet label: - - -```r -library(ggplot2) - -p <- ggplot(mtcars, aes(wt, mpg)) + - geom_point() -p + facet_grid(. ~ vs, labeller = label_bquote(cols = alpha^.(vs))) -``` - - - -Or to display axes labels in the desired format: - - -```r -library(scales) - -ggplot(mtcars, aes(wt, mpg)) + - geom_point() + - scale_y_continuous(labels = number_format(accuracy = 0.01, decimal.mark = ",")) -``` - - - -The `ggplot2::label_bquote()` adds an additional class to the returned function. - -The `scales::number_format()` function is a simple pass-through method that forces evaluation of all its parameters and passes them on to the underlying `scales::number()` function. - ---- - -## Statistical factories (Exercises 10.4.4) - ---- - -**Q1.** In `boot_model()`, why don't I need to force the evaluation of `df` or `model`? - -**A1.** We don’t need to force the evaluation of `df` or `model` because these arguments are automatically evaluated by `lm()`: - - -```r -boot_model <- function(df, formula) { - mod <- lm(formula, data = df) - fitted <- unname(fitted(mod)) - resid <- unname(resid(mod)) - rm(mod) - - function() { - fitted + sample(resid) - } -} -``` - ---- - -**Q2.** Why might you formulate the Box-Cox transformation like this? - - -```r -boxcox3 <- function(x) { - function(lambda) { - if (lambda == 0) { - log(x) - } else { - (x^lambda - 1) / lambda - } - } -} -``` - -**A2.** To see why we formulate this transformation like above, we can compare it to the one mentioned in the book: - - -```r -boxcox2 <- function(lambda) { - if (lambda == 0) { - function(x) log(x) - } else { - function(x) (x^lambda - 1) / lambda - } -} -``` - -Let's have a look at one example with each: - - -```r -boxcox2(1) -#> function(x) (x^lambda - 1) / lambda -#> - -boxcox3(mtcars$wt) -#> function(lambda) { -#> if (lambda == 0) { -#> log(x) -#> } else { -#> (x^lambda - 1) / lambda -#> } -#> } -#> -``` - -As can be seen: - -- in `boxcox2()`, we can vary `x` for the same value of `lambda`, while -- in `boxcox3()`, we can vary `lambda` for the same vector. - -Thus, `boxcox3()` can be handy while exploring different transformations across inputs. - ---- - -**Q3.** Why don't you need to worry that `boot_permute()` stores a copy of the data inside the function that it generates? - -**A3.** If we look at the source code generated by the function factory, we notice that the exact data frame (`mtcars`) is not referenced: - - -```r -boot_permute <- function(df, var) { - n <- nrow(df) - force(var) - - function() { - col <- df[[var]] - col[sample(n, replace = TRUE)] - } -} - -boot_permute(mtcars, "mpg") -#> function() { -#> col <- df[[var]] -#> col[sample(n, replace = TRUE)] -#> } -#> -``` - -This is why we don't need to worry about a copy being made because the `df` in the function environment points to the memory address of the data frame. We can confirm this by comparing their memory addresses: - - -```r -boot_permute_env <- rlang::fn_env(boot_permute(mtcars, "mpg")) -rlang::env_print(boot_permute_env) -#> -#> Parent: -#> Bindings: -#> β€’ n: -#> β€’ df: -#> β€’ var: - -identical( - lobstr::obj_addr(boot_permute_env$df), - lobstr::obj_addr(mtcars) -) -#> [1] TRUE -``` - -We can also check that the values of these bindings are the same as what we entered into the function factory: - - -```r -identical(boot_permute_env$df, mtcars) -#> [1] TRUE -identical(boot_permute_env$var, "mpg") -#> [1] TRUE -``` - ---- - -**Q4.** How much time does `ll_poisson2()` save compared to `ll_poisson1()`? Use `bench::mark()` to see how much faster the optimisation occurs. How does changing the length of `x` change the results? - -**A4.** Let's first compare the performance of these functions with the example in the book: - - -```r -ll_poisson1 <- function(x) { - n <- length(x) - - function(lambda) { - log(lambda) * sum(x) - n * lambda - sum(lfactorial(x)) - } -} - -ll_poisson2 <- function(x) { - n <- length(x) - sum_x <- sum(x) - c <- sum(lfactorial(x)) - - function(lambda) { - log(lambda) * sum_x - n * lambda - c - } -} - -x1 <- c(41, 30, 31, 38, 29, 24, 30, 29, 31, 38) - -bench::mark( - "LL1" = optimise(ll_poisson1(x1), c(0, 100), maximum = TRUE), - "LL2" = optimise(ll_poisson2(x1), c(0, 100), maximum = TRUE) -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc `gc/sec` -#> -#> 1 LL1 15.79Β΅s 34.8Β΅s 20353. 12.8KB 30.5 -#> 2 LL2 8.49Β΅s 10.5Β΅s 56011. 0B 39.2 -``` - -As can be seen, the second version is much faster than the first version. - -We can also vary the length of the vector and confirm that across a wide range of vector lengths, this performance advantage is observed. - - -```r -generate_ll_benches <- function(n) { - x_vec <- sample.int(n, n) - - bench::mark( - "LL1" = optimise(ll_poisson1(x_vec), c(0, 100), maximum = TRUE), - "LL2" = optimise(ll_poisson2(x_vec), c(0, 100), maximum = TRUE) - )[1:4] %>% - dplyr::mutate(length = n, .before = expression) -} - -(df_bench <- purrr::map_dfr( - .x = c(10, 20, 50, 100, 1000), - .f = ~ generate_ll_benches(n = .x) -)) -#> # A tibble: 10 Γ— 5 -#> length expression min median `itr/sec` -#> -#> 1 10 LL1 25.1Β΅s 38.2Β΅s 15958. -#> 2 10 LL2 10.4Β΅s 14.2Β΅s 46876. -#> 3 20 LL1 27.4Β΅s 37.9Β΅s 19378. -#> 4 20 LL2 10.1Β΅s 13.8Β΅s 52872. -#> 5 50 LL1 32.8Β΅s 46.2Β΅s 9780. -#> 6 50 LL2 10Β΅s 15Β΅s 46956. -#> 7 100 LL1 45.6Β΅s 62.6Β΅s 11691. -#> 8 100 LL2 11Β΅s 14.8Β΅s 44373. -#> 9 1000 LL1 633.4Β΅s 924.7Β΅s 823. -#> 10 1000 LL2 36.6Β΅s 50.2Β΅s 14557. - -ggplot( - df_bench, - aes( - x = as.numeric(length), - y = median, - group = as.character(expression), - color = as.character(expression) - ) -) + - geom_point() + - geom_line() + - labs( - x = "Vector length", - y = "Median Execution Time", - colour = "Function used" - ) -``` - - - ---- - -## Function factories + functionals (Exercises 10.5.1) - -**Q1.** Which of the following commands is equivalent to `with(x, f(z))`? - - (a) `x$f(x$z)`. - (b) `f(x$z)`. - (c) `x$f(z)`. - (d) `f(z)`. - (e) It depends. - -**A1.** It depends on whether `with()` is used with a data frame or a list. - - -```r -f <- mean -z <- 1 -x <- list(f = mean, z = 1) - -identical(with(x, f(z)), x$f(x$z)) -#> [1] TRUE - -identical(with(x, f(z)), f(x$z)) -#> [1] TRUE - -identical(with(x, f(z)), x$f(z)) -#> [1] TRUE - -identical(with(x, f(z)), f(z)) -#> [1] TRUE -``` - ---- - -**Q2.** Compare and contrast the effects of `env_bind()` vs. `attach()` for the following code. - -**A2.** Let's compare and contrast the effects of `env_bind()` vs. `attach()`. - -- `attach()` adds `funs` to the search path. Since these functions have the same names as functions in `{base}` package, the attached names mask the ones in the `{base}` package. - - -```r -funs <- list( - mean = function(x) mean(x, na.rm = TRUE), - sum = function(x) sum(x, na.rm = TRUE) -) - -attach(funs) -#> The following objects are masked from package:base: -#> -#> mean, sum - -mean -#> function(x) mean(x, na.rm = TRUE) -head(search()) -#> [1] ".GlobalEnv" "funs" "package:scales" -#> [4] "package:ggplot2" "package:rlang" "package:magrittr" - -mean <- function(x) stop("Hi!") -mean -#> function(x) stop("Hi!") -head(search()) -#> [1] ".GlobalEnv" "funs" "package:scales" -#> [4] "package:ggplot2" "package:rlang" "package:magrittr" - -detach(funs) -``` - -- `env_bind()` adds the functions in `funs` to the global environment, instead of masking the names in the `{base}` package. - - -```r -env_bind(globalenv(), !!!funs) -mean -#> function(x) mean(x, na.rm = TRUE) - -mean <- function(x) stop("Hi!") -mean -#> function(x) stop("Hi!") -env_unbind(globalenv(), names(funs)) -``` - -Note that there is no `"funs"` in this output. - ---- - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bench 1.1.2 2021-11-30 [1] CRAN (R 4.2.0) -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388) -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> farver 2.1.1 2022-07-06 [1] CRAN (R 4.2.1) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.2) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> P grid 4.2.2 2022-10-31 [1] local -#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.1) -#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0) -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> labeling 0.4.2 2020-10-20 [1] CRAN (R 4.2.0) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> lobstr 1.1.2 2022-06-22 [1] CRAN (R 4.2.0) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0) -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> profmem 0.6.0 2020-12-13 [1] CRAN (R 4.2.0) -#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> scales * 1.2.1 2022-08-20 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble 3.1.8.9002 2022-10-16 [1] local -#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` - diff --git a/_book/Function-factories_files/figure-html/Function-factories-18-1.png b/_book/Function-factories_files/figure-html/Function-factories-18-1.png deleted file mode 100644 index 1e696e5e..00000000 Binary files a/_book/Function-factories_files/figure-html/Function-factories-18-1.png and /dev/null differ diff --git a/_book/Function-factories_files/figure-html/Function-factories-19-1.png b/_book/Function-factories_files/figure-html/Function-factories-19-1.png deleted file mode 100644 index 9cb4b9e3..00000000 Binary files a/_book/Function-factories_files/figure-html/Function-factories-19-1.png and /dev/null differ diff --git a/_book/Function-factories_files/figure-html/Function-factories-28-1.png b/_book/Function-factories_files/figure-html/Function-factories-28-1.png deleted file mode 100644 index 04c05abc..00000000 Binary files a/_book/Function-factories_files/figure-html/Function-factories-28-1.png and /dev/null differ diff --git a/_book/Function-factories_files/figure-html/Function-factories-4-1.png b/_book/Function-factories_files/figure-html/Function-factories-4-1.png deleted file mode 100644 index 0c37642d..00000000 Binary files a/_book/Function-factories_files/figure-html/Function-factories-4-1.png and /dev/null differ diff --git a/_book/Function-operators.md b/_book/Function-operators.md deleted file mode 100644 index e685bfd9..00000000 --- a/_book/Function-operators.md +++ /dev/null @@ -1,398 +0,0 @@ -# Function operators - - - -Attaching the needed libraries: - - -```r -library(purrr, warn.conflicts = FALSE) -``` - -## Existing function operators (Exercises 11.2.3) - ---- - -**Q1.** Base R provides a function operator in the form of `Vectorize()`. What does it do? When might you use it? - -**A1.** `Vectorize()` function creates a function that vectorizes the action of the provided function over specified arguments (i.e., it acts on each element of the vector). We will see its utility by solving a problem that otherwise would be difficult to solve. - -The problem is to find indices of matching numeric values for the given threshold by creating a hybrid of the following functions: - -- `%in%` (which doesn't provide any way to provide tolerance when comparing numeric values), -- `dplyr::near()` (which is vectorized element-wise and thus expects two vectors of equal length) - - -```r -which_near <- function(x, y, tolerance) { - # Vectorize `dplyr::near()` function only over the `y` argument. - # `Vectorize()` is a function operator and will return a function. - customNear <- Vectorize(dplyr::near, vectorize.args = c("y"), SIMPLIFY = FALSE) - - # Apply the vectorized function to vector arguments and then check where the - # comparisons are equal (i.e. `TRUE`) using `which()`. - # - # Use `compact()` to remove empty elements from the resulting list. - index_list <- purrr::compact(purrr::map(customNear(x, y, tol = tolerance), which)) - - # If there are any matches, return the indices as an atomic vector of integers. - if (length(index_list) > 0L) { - index_vector <- purrr::simplify(index_list, "integer") - return(index_vector) - } - - # If there are no matches - return(integer(0L)) -} -``` - -Let's use it: - - -```r -x1 <- c(2.1, 3.3, 8.45, 8, 6) -x2 <- c(6, 8.40, 3) - -which_near(x1, x2, tolerance = 0.1) -#> [1] 5 3 -``` - -Note that we needed to create a new function for this because neither of the existing functions do what we want. - - -```r -which(x1 %in% x2) -#> [1] 5 - -which(dplyr::near(x1, x2, tol = 0.1)) -#> Warning in x - y: longer object length is not a multiple of -#> shorter object length -#> integer(0) -``` - -We solved a complex task here using the `Vectorize()` function! - ---- - -**Q2.** Read the source code for `possibly()`. How does it work? - -**A2.** Let's have a look at the source code for this function: - - -```r -possibly -#> function (.f, otherwise, quiet = TRUE) -#> { -#> .f <- as_mapper(.f) -#> force(otherwise) -#> function(...) { -#> tryCatch(.f(...), error = function(e) { -#> if (!quiet) -#> message("Error: ", e$message) -#> otherwise -#> }, interrupt = function(e) { -#> stop("Terminated by user", call. = FALSE) -#> }) -#> } -#> } -#> -#> -``` - -Looking at this code, we can see that `possibly()`: - -- uses `tryCatch()` for error handling -- has a parameter `otherwise` to specify default value in case an error occurs -- has a parameter `quiet` to suppress error message (if needed) - ---- - -**Q3.** Read the source code for `safely()`. How does it work? - -**A3.** Let's have a look at the source code for this function: - - -```r -safely -#> function (.f, otherwise = NULL, quiet = TRUE) -#> { -#> .f <- as_mapper(.f) -#> function(...) capture_error(.f(...), otherwise, quiet) -#> } -#> -#> - -purrr:::capture_error -#> function (code, otherwise = NULL, quiet = TRUE) -#> { -#> tryCatch(list(result = code, error = NULL), error = function(e) { -#> if (!quiet) -#> message("Error: ", e$message) -#> list(result = otherwise, error = e) -#> }, interrupt = function(e) { -#> stop("Terminated by user", call. = FALSE) -#> }) -#> } -#> -#> -``` - -Looking at this code, we can see that `safely()`: - -- uses a list to save both the results (if the function executes successfully) and the error (if it fails) -- uses `tryCatch()` for error handling -- has a parameter `otherwise` to specify default value in case an error occurs -- has a parameter `quiet` to suppress error message (if needed) - ---- - -## Case study: Creating your own function operators (Exercises 11.3.1) - ---- - -**Q1.** Weigh the pros and cons of `download.file %>% dot_every(10) %>% delay_by(0.1)` versus `download.file %>% delay_by(0.1) %>% dot_every(10)`. - -**A1.** Although both of these chains of piped operations produce the same number of dots and would need the same amount of time, there is a subtle difference in how they do this. - -- `download.file %>% dot_every(10) %>% delay_by(0.1)` - -Here, the printing of the dot is also delayed, and the first dot is printed when the 10th URL download starts. - -- `download.file %>% delay_by(0.1) %>% dot_every(10)` - -Here, the first dot is printed after the 9th download is finished, and the 10th download starts after a short delay. - ---- - -**Q2.** Should you memoise `download.file()`? Why or why not? - -**A2.** Since `download.file()` is meant to download files from the Internet, memoising it is not recommended for the following reasons: - -- Memoization is helpful when giving the same input the function returns the same output. This is not necessarily the case for webpages since they constantly change, and you may continue to "download" an outdated version of the webpage. - -- Memoization works by caching results, which can take up a significant amount of memory. - ---- - -**Q3.** Create a function operator that reports whenever a file is created or deleted in the working directory, using `dir()` and `setdiff()`. What other global function effects might you want to track? - -**A3.** First, let's create helper functions to compare and print added or removed filenames: - - -```r -print_multiple_entries <- function(header, entries) { - message(paste0(header, ":\n"), paste0(entries, collapse = "\n")) -} - -file_comparator <- function(old, new) { - if (setequal(old, new)) { - return() - } - - removed <- setdiff(old, new) - added <- setdiff(new, old) - - if (length(removed) > 0L) print_multiple_entries("- File removed", removed) - if (length(added) > 0L) print_multiple_entries("- File added", added) -} -``` - -We can then write a function operator and use it to create functions that will do the necessary tracking: - - -```r -dir_tracker <- function(f) { - force(f) - function(...) { - old_files <- dir() - on.exit(file_comparator(old_files, dir()), add = TRUE) - - f(...) - } -} - -file_creation_tracker <- dir_tracker(file.create) -file_deletion_tracker <- dir_tracker(file.remove) -``` - -Let's try it out: - - -```r -file_creation_tracker(c("a.txt", "b.txt")) -#> - File added: -#> a.txt -#> b.txt -#> [1] TRUE TRUE - -file_deletion_tracker(c("a.txt", "b.txt")) -#> - File removed: -#> a.txt -#> b.txt -#> [1] TRUE TRUE -``` - -Other global function effects we might want to track: - -- working directory -- environment variables -- connections -- library paths -- graphics devices -- [etc.](https://withr.r-lib.org/reference/index.html) - ---- - -**Q4.** Write a function operator that logs a timestamp and message to a file every time a function is run. - -**A4.** The following function operator logs a timestamp and message to a file every time a function is run: - - -```r -# helper function to write to a file connection -write_line <- function(filepath, ...) { - cat(..., "\n", sep = "", file = filepath, append = TRUE) -} - -# function operator -logger <- function(f, filepath) { - force(f) - force(filepath) - - write_line(filepath, "Function created at: ", as.character(Sys.time())) - - function(...) { - write_line(filepath, "Function called at: ", as.character(Sys.time())) - f(...) - } -} - -# check that the function works as expected with a tempfile -withr::with_tempfile("logfile", code = { - logged_runif <- logger(runif, logfile) - - Sys.sleep(sample.int(10, 1)) - logged_runif(1) - - Sys.sleep(sample.int(10, 1)) - logged_runif(2) - - Sys.sleep(sample.int(10, 1)) - logged_runif(3) - - cat(readLines(logfile), sep = "\n") -}) -#> Function created at: 2022-11-12 11:49:04 -#> Function called at: 2022-11-12 11:49:09 -#> Function called at: 2022-11-12 11:49:14 -#> Function called at: 2022-11-12 11:49:22 -``` - ---- - -**Q5.** Modify `delay_by()` so that instead of delaying by a fixed amount of time, it ensures that a certain amount of time has elapsed since the function was last called. That is, if you called `g <- delay_by(1, f); g(); Sys.sleep(2); g()` there shouldn't be an extra delay. - -**A5.** Modified version of the function meeting the specified requirements: - - -```r -delay_by_atleast <- function(f, amount) { - force(f) - force(amount) - - # the last time the function was run - last_time <- NULL - - function(...) { - if (!is.null(last_time)) { - wait <- (last_time - Sys.time()) + amount - if (wait > 0) Sys.sleep(wait) - } - - # update the time in the parent frame for the next run when the function finishes - on.exit(last_time <<- Sys.time()) - - f(...) - } -} -``` - ---- - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388) -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble 3.1.8.9002 2022-10-16 [1] local -#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Functionals.md b/_book/Functionals.md deleted file mode 100644 index c6bf3ae4..00000000 --- a/_book/Functionals.md +++ /dev/null @@ -1,1083 +0,0 @@ -# Functionals - - - -Attaching the needed libraries: - - -```r -library(purrr, warn.conflicts = FALSE) -``` - -## My first functional: `map()` (Exercises 9.2.6) - ---- - -**Q1.** Use `as_mapper()` to explore how `{purrr}` generates anonymous functions for the integer, character, and list helpers. What helper allows you to extract attributes? Read the documentation to find out. - -**A1.** Let's handle the two parts of the question separately. - -- `as_mapper()` and `{purrr}`-generated anonymous functions: - -Looking at the experimentation below with `map()` and `as_mapper()`, we can see that, depending on the type of the input, `as_mapper()` creates an extractor function using `pluck()`. - - -```r -# mapping by position ----------------------- - -x <- list(1, list(2, 3, list(1, 2))) - -map(x, 1) -#> [[1]] -#> [1] 1 -#> -#> [[2]] -#> [1] 2 -as_mapper(1) -#> function (x, ...) -#> pluck(x, 1, .default = NULL) -#> - -map(x, list(2, 1)) -#> [[1]] -#> NULL -#> -#> [[2]] -#> [1] 3 -as_mapper(list(2, 1)) -#> function (x, ...) -#> pluck(x, 2, 1, .default = NULL) -#> - -# mapping by name ----------------------- - -y <- list( - list(m = "a", list(1, m = "mo")), - list(n = "b", list(2, n = "no")) -) - -map(y, "m") -#> [[1]] -#> [1] "a" -#> -#> [[2]] -#> NULL -as_mapper("m") -#> function (x, ...) -#> pluck(x, "m", .default = NULL) -#> - -# mixing position and name -map(y, list(2, "m")) -#> [[1]] -#> [1] "mo" -#> -#> [[2]] -#> NULL -as_mapper(list(2, "m")) -#> function (x, ...) -#> pluck(x, 2, "m", .default = NULL) -#> - -# compact functions ---------------------------- - -map(y, ~ length(.x)) -#> [[1]] -#> [1] 2 -#> -#> [[2]] -#> [1] 2 -as_mapper(~ length(.x)) -#> -#> function (..., .x = ..1, .y = ..2, . = ..1) -#> length(.x) -#> attr(,"class") -#> [1] "rlang_lambda_function" "function" -``` - -- You can extract attributes using `purrr::attr_getter()`: - - -```r -pluck(Titanic, attr_getter("class")) -#> [1] "table" -``` - ---- - -**Q2.** `map(1:3, ~ runif(2))` is a useful pattern for generating random numbers, but `map(1:3, runif(2))` is not. Why not? Can you explain why it returns the result that it does? - -**A2.** As shown by `as_mapper()` outputs below, the second call is not appropriate for generating random numbers because it translates to `pluck()` function where the indices for plucking are taken to be randomly generated numbers, and these are not valid accessors and so we get `NULL`s in return. - - -```r -map(1:3, ~ runif(2)) -#> [[1]] -#> [1] 0.2180892 0.9876342 -#> -#> [[2]] -#> [1] 0.3484619 0.3810470 -#> -#> [[3]] -#> [1] 0.02098596 0.74972687 -as_mapper(~ runif(2)) -#> -#> function (..., .x = ..1, .y = ..2, . = ..1) -#> runif(2) -#> attr(,"class") -#> [1] "rlang_lambda_function" "function" - -map(1:3, runif(2)) -#> [[1]] -#> NULL -#> -#> [[2]] -#> NULL -#> -#> [[3]] -#> NULL -as_mapper(runif(2)) -#> function (x, ...) -#> pluck(x, 0.597890264587477, 0.587997315218672, .default = NULL) -#> -``` - ---- - -**Q3.** Use the appropriate `map()` function to: - - a) Compute the standard deviation of every column in a numeric data frame. - - a) Compute the standard deviation of every numeric column in a mixed data frame. (Hint: you'll need to do it in two steps.) - - a) Compute the number of levels for every factor in a data frame. - -**A3.** Using the appropriate `map()` function to: - -- Compute the standard deviation of every column in a numeric data frame: - - -```r -map_dbl(mtcars, sd) -#> mpg cyl disp hp drat -#> 6.0269481 1.7859216 123.9386938 68.5628685 0.5346787 -#> wt qsec vs am gear -#> 0.9784574 1.7869432 0.5040161 0.4989909 0.7378041 -#> carb -#> 1.6152000 -``` - -- Compute the standard deviation of every numeric column in a mixed data frame: - - -```r -keep(iris, is.numeric) %>% - map_dbl(sd) -#> Sepal.Length Sepal.Width Petal.Length Petal.Width -#> 0.8280661 0.4358663 1.7652982 0.7622377 -``` - -- Compute the number of levels for every factor in a data frame: - - -```r -modify_if(dplyr::starwars, is.character, as.factor) %>% - keep(is.factor) %>% - map_int(~ length(levels(.))) -#> name hair_color skin_color eye_color sex -#> 87 12 31 15 4 -#> gender homeworld species -#> 2 48 37 -``` - ---- - -**Q4.** The following code simulates the performance of a *t*-test for non-normal data. Extract the *p*-value from each test, then visualise. - - -```r -trials <- map(1:100, ~ t.test(rpois(10, 10), rpois(7, 10))) -``` - -**A4.** - -- Extract the *p*-value from each test: - - -```r -trials <- map(1:100, ~ t.test(rpois(10, 10), rpois(7, 10))) - -(p <- map_dbl(trials, "p.value")) -#> [1] 0.81695628 0.53177360 0.94750819 0.41026769 0.34655294 -#> [6] 0.05300287 0.56479901 0.85936864 0.77517391 0.64321161 -#> [11] 0.84462914 0.54144946 0.63070476 0.20325827 0.39824435 -#> [16] 0.67052432 0.39932663 0.44437632 0.51645941 0.96578745 -#> [21] 0.70219557 0.69931716 0.23946786 0.55100566 0.76028958 -#> [26] 0.38105366 0.64544126 0.15379307 0.86945196 0.09965658 -#> [31] 0.96425489 0.54239108 0.38985789 0.59019282 0.96247907 -#> [36] 0.54997487 0.66111391 0.30961551 0.10897334 0.55049635 -#> [41] 0.93882405 0.14836866 0.44307287 0.61583610 0.37284284 -#> [46] 0.38559622 0.42935767 0.26059293 0.07831619 0.93768396 -#> [51] 0.48459268 0.73571291 0.30288560 0.68521609 0.06374636 -#> [56] 0.11007808 0.98758443 0.17831882 0.94471538 0.19711729 -#> [61] 0.02094185 0.12370745 0.23247837 0.93842382 0.19160550 -#> [66] 0.49005550 0.98146240 0.09034183 0.94912080 0.55857523 -#> [71] 0.24692070 0.63658206 0.14290966 0.10309770 0.89516449 -#> [76] 0.25660092 0.16943034 0.41199780 0.82721280 0.74017418 -#> [81] 0.43724631 0.55944024 0.93615100 0.68788872 0.01416627 -#> [86] 0.60120497 0.54125910 0.91581929 0.78949327 0.57887371 -#> [91] 0.83217542 0.90108906 0.97474727 0.99129282 0.54436155 -#> [96] 0.74159859 0.06534957 0.10834529 0.19737786 0.93750342 -``` - -- Visualise the extracted *p*-values: - - -```r -plot(p) -``` - - - -```r - -hist(p) -``` - - - ---- - -**Q5.** The following code uses a map nested inside another map to apply a function to every element of a nested list. Why does it fail, and what do you need to do to make it work? - - -```r -x <- list( - list(1, c(3, 9)), - list(c(3, 6), 7, c(4, 7, 6)) -) - -triple <- function(x) x * 3 -map(x, map, .f = triple) -#> Error in .f(.x[[i]], ...): unused argument (function (.x, .f, ...) -#> { -#> .f <- as_mapper(.f, ...) -#> .Call(map_impl, environment(), ".x", ".f", "list") -#> }) -``` - -**A5.** This function fails because this call effectively evaluates to the following: - - -```r -map(.x = x, .f = ~ triple(x = .x, map)) -``` - -But `triple()` has only one parameter (`x`), and so the execution fails. - -Here is the fixed version: - - -```r -x <- list( - list(1, c(3, 9)), - list(c(3, 6), 7, c(4, 7, 6)) -) - -triple <- function(x) x * 3 -map(x, .f = ~ map(.x, ~ triple(.x))) -#> [[1]] -#> [[1]][[1]] -#> [1] 3 -#> -#> [[1]][[2]] -#> [1] 9 27 -#> -#> -#> [[2]] -#> [[2]][[1]] -#> [1] 9 18 -#> -#> [[2]][[2]] -#> [1] 21 -#> -#> [[2]][[3]] -#> [1] 12 21 18 -``` - ---- - -**Q6.** Use `map()` to fit linear models to the `mtcars` dataset using the formulas stored in this list: - - -```r -formulas <- list( - mpg ~ disp, - mpg ~ I(1 / disp), - mpg ~ disp + wt, - mpg ~ I(1 / disp) + wt -) -``` - -**A6.** Fitting linear models to the `mtcars` dataset using the provided formulas: - - -```r -formulas <- list( - mpg ~ disp, - mpg ~ I(1 / disp), - mpg ~ disp + wt, - mpg ~ I(1 / disp) + wt -) - -map(formulas, ~ lm(formula = ., data = mtcars)) -#> [[1]] -#> -#> Call: -#> lm(formula = ., data = mtcars) -#> -#> Coefficients: -#> (Intercept) disp -#> 29.59985 -0.04122 -#> -#> -#> [[2]] -#> -#> Call: -#> lm(formula = ., data = mtcars) -#> -#> Coefficients: -#> (Intercept) I(1/disp) -#> 10.75 1557.67 -#> -#> -#> [[3]] -#> -#> Call: -#> lm(formula = ., data = mtcars) -#> -#> Coefficients: -#> (Intercept) disp wt -#> 34.96055 -0.01772 -3.35083 -#> -#> -#> [[4]] -#> -#> Call: -#> lm(formula = ., data = mtcars) -#> -#> Coefficients: -#> (Intercept) I(1/disp) wt -#> 19.024 1142.560 -1.798 -``` - ---- - -**Q7.** Fit the model `mpg ~ disp` to each of the bootstrap replicates of `mtcars` in the list below, then extract the $R^2$ of the model fit (Hint: you can compute the $R^2$ with `summary()`.) - - -```r -bootstrap <- function(df) { - df[sample(nrow(df), replace = TRUE), , drop = FALSE] -} - -bootstraps <- map(1:10, ~ bootstrap(mtcars)) -``` - -**A7.** This can be done using `map_dbl()`: - - -```r -bootstrap <- function(df) { - df[sample(nrow(df), replace = TRUE), , drop = FALSE] -} - -bootstraps <- map(1:10, ~ bootstrap(mtcars)) - -bootstraps %>% - map(~ lm(mpg ~ disp, data = .x)) %>% - map(summary) %>% - map_dbl("r.squared") -#> [1] 0.7864562 0.8110818 0.7956331 0.7632399 0.7967824 -#> [6] 0.7364226 0.7203027 0.6653252 0.7732780 0.6753329 -``` - ---- - -## Map variants (Exercises 9.4.6) - ---- - -**Q1.** Explain the results of `modify(mtcars, 1)`. - -**A1.** `modify()` returns the object of type same as the input. Since the input here is a data frame of certain dimensions and `.f = 1` translates to plucking the first element in each column, it returns a data frame with the same dimensions with the plucked element recycled across rows. - - -```r -head(modify(mtcars, 1)) -#> mpg cyl disp hp drat wt qsec vs am -#> Mazda RX4 21 6 160 110 3.9 2.62 16.46 0 1 -#> Mazda RX4 Wag 21 6 160 110 3.9 2.62 16.46 0 1 -#> Datsun 710 21 6 160 110 3.9 2.62 16.46 0 1 -#> Hornet 4 Drive 21 6 160 110 3.9 2.62 16.46 0 1 -#> Hornet Sportabout 21 6 160 110 3.9 2.62 16.46 0 1 -#> Valiant 21 6 160 110 3.9 2.62 16.46 0 1 -#> gear carb -#> Mazda RX4 4 4 -#> Mazda RX4 Wag 4 4 -#> Datsun 710 4 4 -#> Hornet 4 Drive 4 4 -#> Hornet Sportabout 4 4 -#> Valiant 4 4 -``` - ---- - -**Q2.** Rewrite the following code to use `iwalk()` instead of `walk2()`. What are the advantages and disadvantages? - - -```r -cyls <- split(mtcars, mtcars$cyl) -paths <- file.path(temp, paste0("cyl-", names(cyls), ".csv")) -walk2(cyls, paths, write.csv) -``` - -**A2.** Let's first rewrite provided code using `iwalk()`: - - -```r -cyls <- split(mtcars, mtcars$cyl) -names(cyls) <- file.path(temp, paste0("cyl-", names(cyls), ".csv")) -iwalk(cyls, ~ write.csv(.x, .y)) -``` - -The advantage of using `iwalk()` is that we need to now deal with only a single variable (`cyls`) instead of two (`cyls` and `paths`). - -The disadvantage is that the code is difficult to reason about: -In `walk2()`, it's explicit what `.x` (`= cyls`) and `.y` (`= paths`) correspond to, while this is not so for `iwalk()` (i.e., `.x = cyls` and `.y = names(cyls)`) with the `.y` argument being "invisible". - ---- - -**Q3.** Explain how the following code transforms a data frame using functions stored in a list. - - -```r -trans <- list( - disp = function(x) x * 0.0163871, - am = function(x) factor(x, labels = c("auto", "manual")) -) - -nm <- names(trans) -mtcars[nm] <- map2(trans, mtcars[nm], function(f, var) f(var)) -``` - -Compare and contrast the `map2()` approach to this `map()` approach: - - -```r -mtcars[nm] <- map(nm, ~ trans[[.x]](mtcars[[.x]])) -``` - -**A3.** `map2()` supplies the functions stored in `trans` as anonymous functions via placeholder `f`, while the names of the columns specified in `mtcars[nm]` are supplied as `var` argument to the anonymous function. Note that the function is iterating over indices for vectors of transformations and column names. - - -```r -trans <- list( - disp = function(x) x * 0.0163871, - am = function(x) factor(x, labels = c("auto", "manual")) -) - -nm <- names(trans) -mtcars[nm] <- map2(trans, mtcars[nm], function(f, var) f(var)) -``` - -In the `map()` approach, the function is iterating over indices for vectors of column names. - - -```r -mtcars[nm] <- map(nm, ~ trans[[.x]](mtcars[[.x]])) -``` - -The latter approach can't afford passing arguments to placeholders in an anonymous function. - ---- - -**Q4.** What does `write.csv()` return, i.e. what happens if you use it with `map2()` instead of `walk2()`? - -**A4.** If we use `map2()`, it will work, but it will print `NULL`s to the console for every list element. - - -```r -withr::with_tempdir( - code = { - ls <- split(mtcars, mtcars$cyl) - nm <- names(ls) - map2(ls, nm, write.csv) - } -) -#> $`4` -#> NULL -#> -#> $`6` -#> NULL -#> -#> $`8` -#> NULL -``` - ---- - -## Predicate functionals (Exercises 9.6.3) - ---- - -**Q1.** Why isn't `is.na()` a predicate function? What base R function is closest to being a predicate version of `is.na()`? - -**A1.** As mentioned in the docs: - -> A predicate is a function that returns a **single** `TRUE` or `FALSE`. - -The `is.na()` function does not return a `logical` scalar, but instead returns a vector and thus isn't a predicate function. - - -```r -# contrast the following behavior of predicate functions -is.character(c("x", 2)) -#> [1] TRUE -is.null(c(3, NULL)) -#> [1] FALSE - -# with this behavior -is.na(c(NA, 1)) -#> [1] TRUE FALSE -``` - -The closest equivalent of a predicate function in base-R is `anyNA()` function. - - -```r -anyNA(c(NA, 1)) -#> [1] TRUE -``` - ---- - -**Q2.** `simple_reduce()` has a problem when `x` is length 0 or length 1. Describe the source of the problem and how you might go about fixing it. - - -```r -simple_reduce <- function(x, f) { - out <- x[[1]] - for (i in seq(2, length(x))) { - out <- f(out, x[[i]]) - } - out -} -``` - -**A2.** The supplied function struggles with inputs of length 0 and 1 because function tries to subscript out-of-bound values. - - -```r -simple_reduce(numeric(), sum) -#> Error in x[[1]]: subscript out of bounds -simple_reduce(1, sum) -#> Error in x[[i]]: subscript out of bounds -simple_reduce(1:3, sum) -#> [1] 6 -``` - -This problem can be solved by adding `init` argument, which supplies the default or initial value: - - -```r -simple_reduce2 <- function(x, f, init = 0) { - # initializer will become the first value - if (length(x) == 0L) { - return(init) - } - - if (length(x) == 1L) { - return(x[[1L]]) - } - - out <- x[[1]] - - for (i in seq(2, length(x))) { - out <- f(out, x[[i]]) - } - - out -} -``` - -Let's try it out: - - -```r -simple_reduce2(numeric(), sum) -#> [1] 0 -simple_reduce2(1, sum) -#> [1] 1 -simple_reduce2(1:3, sum) -#> [1] 6 -``` - -Depending on the function, we can provide a different `init` argument: - - -```r -simple_reduce2(numeric(), `*`, init = 1) -#> [1] 1 -simple_reduce2(1, `*`, init = 1) -#> [1] 1 -simple_reduce2(1:3, `*`, init = 1) -#> [1] 6 -``` - ---- - -**Q3.** Implement the `span()` function from Haskell: given a list `x` and a predicate function `f`, `span(x, f)` returns the location of the longest sequential run of elements where the predicate is true. (Hint: you might find `rle()` helpful.) - -**A3.** Implementation of `span()`: - - -```r -span <- function(x, f) { - running_lengths <- purrr::map_lgl(x, ~ f(.x)) %>% rle() - - df <- dplyr::tibble( - "lengths" = running_lengths$lengths, - "values" = running_lengths$values - ) %>% - dplyr::mutate(rowid = dplyr::row_number()) %>% - dplyr::filter(values) - - # no sequence where condition is `TRUE` - if (nrow(df) == 0L) { - return(integer()) - } - - # only single sequence where condition is `TRUE` - if (nrow(df) == 1L) { - return((df$rowid):(df$lengths - 1 + df$rowid)) - } - - # multiple sequences where condition is `TRUE`; select max one - if (nrow(df) > 1L) { - df <- dplyr::filter(df, lengths == max(lengths)) - return((df$rowid):(df$lengths - 1 + df$rowid)) - } -} -``` - -Testing it once: - - -```r -span(c(0, 0, 0, 0, 0), is.na) -#> integer(0) -span(c(NA, 0, NA, NA, NA), is.na) -#> [1] 3 4 5 -span(c(NA, 0, 0, 0, 0), is.na) -#> [1] 1 -span(c(NA, NA, 0, 0, 0), is.na) -#> [1] 1 2 -``` - -Testing it twice: - - -```r -span(c(3, 1, 2, 4, 5, 6), function(x) x > 3) -#> [1] 2 3 4 -span(c(3, 1, 2, 4, 5, 6), function(x) x > 9) -#> integer(0) -span(c(3, 1, 2, 4, 5, 6), function(x) x == 3) -#> [1] 1 -span(c(3, 1, 2, 4, 5, 6), function(x) x %in% c(2, 4)) -#> [1] 2 3 -``` - ---- - -**Q4.** Implement `arg_max()`. It should take a function and a vector of inputs, and return the elements of the input where the function returns the highest value. For example, `arg_max(-10:5, function(x) x ^ 2)` should return -10. `arg_max(-5:5, function(x) x ^ 2)` should return `c(-5, 5)`. Also implement the matching `arg_min()` function. - -**A4.** Here are implementations for the specified functions: - -- Implementing `arg_max()` - - -```r -arg_max <- function(.x, .f) { - df <- dplyr::tibble( - original = .x, - transformed = purrr::map_dbl(.x, .f) - ) - - dplyr::filter(df, transformed == max(transformed))[["original"]] -} - -arg_max(-10:5, function(x) x^2) -#> [1] -10 -arg_max(-5:5, function(x) x^2) -#> [1] -5 5 -``` - -- Implementing `arg_min()` - - -```r -arg_min <- function(.x, .f) { - df <- dplyr::tibble( - original = .x, - transformed = purrr::map_dbl(.x, .f) - ) - - dplyr::filter(df, transformed == min(transformed))[["original"]] -} - -arg_min(-10:5, function(x) x^2) -#> [1] 0 -arg_min(-5:5, function(x) x^2) -#> [1] 0 -``` - ---- - -**Q5.** The function below scales a vector so it falls in the range [0, 1]. How would you apply it to every column of a data frame? How would you apply it to every numeric column in a data frame? - - -```r -scale01 <- function(x) { - rng <- range(x, na.rm = TRUE) - (x - rng[1]) / (rng[2] - rng[1]) -} -``` - -**A5.** We will use `{purrr}` package to apply this function. Key thing to keep in mind is that a data frame is a list of atomic vectors of equal length. - -- Applying function to every column in a data frame: We will use `anscombe` as example since it has all numeric columns. - - -```r -purrr::map_df(head(anscombe), .f = scale01) -#> # A tibble: 6 Γ— 8 -#> x1 x2 x3 x4 y1 y2 y3 y4 -#> -#> 1 0.333 0.333 0.333 NaN 0.362 0.897 0.116 0.266 -#> 2 0 0 0 NaN 0 0.0345 0 0 -#> 3 0.833 0.833 0.833 NaN 0.209 0.552 1 0.633 -#> 4 0.167 0.167 0.167 NaN 0.618 0.578 0.0570 1 -#> 5 0.5 0.5 0.5 NaN 0.458 1 0.174 0.880 -#> 6 1 1 1 NaN 1 0 0.347 0.416 -``` - -- Applying function to every numeric column in a data frame: We will use `iris` as example since not all of its columns are of numeric type. - - -```r -purrr::modify_if(head(iris), .p = is.numeric, .f = scale01) -#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species -#> 1 0.625 0.5555556 0.25 0 setosa -#> 2 0.375 0.0000000 0.25 0 setosa -#> 3 0.125 0.2222222 0.00 0 setosa -#> 4 0.000 0.1111111 0.50 0 setosa -#> 5 0.500 0.6666667 0.25 0 setosa -#> 6 1.000 1.0000000 1.00 1 setosa -``` - ---- - -## Base functionals (Exercises 9.7.3) - ---- - -**Q1.** How does `apply()` arrange the output? Read the documentation and perform some experiments. - -**A1.** Let's prepare an array and apply a function over different margins: - - -```r -(m <- as.array(table(mtcars$cyl, mtcars$am, mtcars$vs))) -#> , , = 0 -#> -#> -#> auto manual -#> 4 0 1 -#> 6 0 3 -#> 8 12 2 -#> -#> , , = 1 -#> -#> -#> auto manual -#> 4 3 7 -#> 6 4 0 -#> 8 0 0 - -# rows -apply(m, 1, function(x) x^2) -#> -#> 4 6 8 -#> [1,] 0 0 144 -#> [2,] 1 9 4 -#> [3,] 9 16 0 -#> [4,] 49 0 0 - -# columns -apply(m, 2, function(x) x^2) -#> -#> auto manual -#> [1,] 0 1 -#> [2,] 0 9 -#> [3,] 144 4 -#> [4,] 9 49 -#> [5,] 16 0 -#> [6,] 0 0 - -# rows and columns -apply(m, c(1, 2), function(x) x^2) -#> , , = auto -#> -#> -#> 4 6 8 -#> 0 0 0 144 -#> 1 9 16 0 -#> -#> , , = manual -#> -#> -#> 4 6 8 -#> 0 1 9 4 -#> 1 49 0 0 -``` - -As can be seen, `apply()` returns outputs organised first by the margins being operated over, and only then the results. - ---- - -**Q2.** What do `eapply()` and `rapply()` do? Does purrr have equivalents? - -**A2.** Let's consider them one-by-one. - -- `eapply()` - -As mentioned in its documentation: - -> `eapply()` applies FUN to the named values from an environment and returns the results as a list. - -Here is an example: - - -```r -library(rlang) -#> -#> Attaching package: 'rlang' -#> The following objects are masked from 'package:purrr': -#> -#> %@%, as_function, flatten, flatten_chr, -#> flatten_dbl, flatten_int, flatten_lgl, -#> flatten_raw, invoke, splice -#> The following object is masked from 'package:magrittr': -#> -#> set_names - -e <- env("x" = 1, "y" = 2) -rlang::env_print(e) -#> -#> Parent: -#> Bindings: -#> β€’ x: -#> β€’ y: - -eapply(e, as.character) -#> $x -#> [1] "1" -#> -#> $y -#> [1] "2" -``` - -`{purrr}` doesn't have any function to iterate over environments. - -- `rapply()` - -> `rapply()` is a recursive version of lapply with flexibility in how the result is structured (how = ".."). - -Here is an example: - - -```r -X <- list(list(a = TRUE, b = list(c = c(4L, 3.2))), d = 9.0) - -rapply(X, as.character, classes = "numeric", how = "replace") -#> [[1]] -#> [[1]]$a -#> [1] TRUE -#> -#> [[1]]$b -#> [[1]]$b$c -#> [1] "4" "3.2" -#> -#> -#> -#> $d -#> [1] "9" -``` - -`{purrr}` has something similar in `modify_depth()`. - - -```r -X <- list(list(a = TRUE, b = list(c = c(4L, 3.2))), d = 9.0) - -purrr::modify_depth(X, .depth = 2L, .f = length) -#> [[1]] -#> [[1]]$a -#> [1] 1 -#> -#> [[1]]$b -#> [1] 1 -#> -#> -#> $d -#> [1] 1 -``` - ---- - -**Q3.** Challenge: read about the [fixed point algorithm](https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-12.html#%25_idx_1096). Complete the exercises using R. - -**A3.** As mentioned in the suggested reading material: - -> A number $x$ is called a fixed point of a function $f$ if $x$ satisfies the equation $f(x) = x$. For some functions $f$ we can locate a fixed point by beginning with an initial guess and applying $f$ repeatedly, $f(x), f(f(x)), f(f(f(x))), ...$ until the value does not change very much. Using this idea, we can devise a procedure fixed-point that takes as inputs a function and an initial guess and produces an approximation to a fixed point of the function. - -Let's first implement a fixed-point algorithm: - - -```r -close_enough <- function(x1, x2, tolerance = 0.001) { - if (abs(x1 - x2) < tolerance) { - return(TRUE) - } else { - return(FALSE) - } -} - -find_fixed_point <- function(.f, .guess, tolerance = 0.001) { - .next <- .f(.guess) - is_close_enough <- close_enough(.next, .guess, tol = tolerance) - - if (is_close_enough) { - return(.next) - } else { - find_fixed_point(.f, .next, tolerance) - } -} -``` - -Let's check if it works as expected: - - -```r -find_fixed_point(cos, 1.0) -#> [1] 0.7387603 - -# cos(x) = x -cos(find_fixed_point(cos, 1.0)) -#> [1] 0.7393039 -``` - -We will solve only one exercise from the reading material. Rest are beyond the scope of this solution manual. - -> Show that the golden ratio $\phi$ is a fixed point of the transformation $x \mapsto 1 + 1/x$, and use this fact to compute $\phi$ by means of the fixed-point procedure. - - -```r -golden_ratio_f <- function(x) 1 + (1 / x) - -find_fixed_point(golden_ratio_f, 1.0) -#> [1] 1.618182 -``` - ---- - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388) -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0) -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble 3.1.8.9002 2022-10-16 [1] local -#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Functionals_files/figure-html/Functionals-11-1.png b/_book/Functionals_files/figure-html/Functionals-11-1.png deleted file mode 100644 index 8d7f70fd..00000000 Binary files a/_book/Functionals_files/figure-html/Functionals-11-1.png and /dev/null differ diff --git a/_book/Functionals_files/figure-html/Functionals-11-2.png b/_book/Functionals_files/figure-html/Functionals-11-2.png deleted file mode 100644 index f8b39a86..00000000 Binary files a/_book/Functionals_files/figure-html/Functionals-11-2.png and /dev/null differ diff --git a/_book/Functions.md b/_book/Functions.md deleted file mode 100644 index 9b54205d..00000000 --- a/_book/Functions.md +++ /dev/null @@ -1,1394 +0,0 @@ -# Functions - - - -Attaching the needed libraries: - - -```r -library(tidyverse, warn.conflicts = FALSE) -``` - -## Function fundamentals (Exercises 6.2.5) - -**Q1.** Given a name, like `"mean"`, `match.fun()` lets you find a function. Given a function, can you find its name? Why doesn't that make sense in R? - -**A1.** Given a name, `match.fun()` lets you find a function. - - -```r -match.fun("mean") -#> function (x, ...) -#> UseMethod("mean") -#> -#> -``` - -But, given a function, it doesn't make sense to find its name because there can be multiple names bound to the same function. - - -```r -f1 <- function(x) mean(x) -f2 <- f1 - -match.fun("f1") -#> function(x) mean(x) - -match.fun("f2") -#> function(x) mean(x) -``` - -**Q2.** It's possible (although typically not useful) to call an anonymous function. Which of the two approaches below is correct? Why? - - -```r -function(x) 3() -#> function(x) 3() -(function(x) 3)() -#> [1] 3 -``` - -**A2.** The first expression is not correct since the function will evaluate `3()`, which is syntactically not allowed since literals can't be treated like functions. - - -```r -f <- (function(x) 3()) -f -#> function(x) 3() -f() -#> Error in f(): attempt to apply non-function - -rlang::is_syntactic_literal(3) -#> [1] TRUE -``` - -This is the correct way to call an anonymous function. - - -```r -g <- (function(x) 3) -g -#> function(x) 3 -g() -#> [1] 3 -``` - -**Q3.** A good rule of thumb is that an anonymous function should fit on one line and shouldn't need to use `{}`. Review your code. Where could you have used an anonymous function instead of a named function? Where should you have used a named function instead of an anonymous function? - -**A3.** Self activity. - -**Q4.** What function allows you to tell if an object is a function? What function allows you to tell if a function is a primitive function? - -**A4.** Use `is.function()` to check if an *object* is a *function*: - - -```r -# these are functions -f <- function(x) 3 -is.function(mean) -#> [1] TRUE -is.function(f) -#> [1] TRUE - -# these aren't -is.function("x") -#> [1] FALSE -is.function(new.env()) -#> [1] FALSE -``` - -Use `is.primitive()` to check if a *function* is *primitive*: - - -```r -# primitive -is.primitive(sum) -#> [1] TRUE -is.primitive(`+`) -#> [1] TRUE - -# not primitive -is.primitive(mean) -#> [1] FALSE -is.primitive(read.csv) -#> [1] FALSE -``` - -**Q5.** This code makes a list of all functions in the base package. - - -```r -objs <- mget(ls("package:base", all = TRUE), inherits = TRUE) -funs <- Filter(is.function, objs) -``` - -Use it to answer the following questions: - -a. Which base function has the most arguments? - -b. How many base functions have no arguments? What's special about those functions? - -c. How could you adapt the code to find all primitive functions? - -**A5.** The provided code is the following: - - -```r -objs <- mget(ls("package:base", all = TRUE), inherits = TRUE) -funs <- Filter(is.function, objs) -``` - -a. Which base function has the most arguments? - -We can use `formals()` to extract number of arguments, but because this function returns `NULL` for primitive functions. - - -```r -formals("!") -#> NULL - -length(formals("!")) -#> [1] 0 -``` - -Therefore, we will focus only on non-primitive functions. - - -```r -funs <- purrr::discard(funs, is.primitive) -``` - -`scan()` function has the most arguments. - - -```r -df_formals <- purrr::map_df(funs, ~ length(formals(.))) %>% - tidyr::pivot_longer( - cols = dplyr::everything(), - names_to = "function", - values_to = "argumentCount" - ) %>% - dplyr::arrange(desc(argumentCount)) - -df_formals -#> # A tibble: 1,125 Γ— 2 -#> `function` argumentCount -#> -#> 1 scan 22 -#> 2 format.default 16 -#> 3 source 16 -#> 4 formatC 15 -#> 5 library 13 -#> 6 merge.data.frame 13 -#> 7 prettyNum 13 -#> 8 system2 11 -#> 9 print.default 10 -#> 10 save 10 -#> # … with 1,115 more rows -``` - -b. How many base functions have no arguments? What’s special about those functions? - - - -At the time of writing, 47 base (non-primitive) functions have no arguments. - - -```r -dplyr::filter(df_formals, argumentCount == 0) -#> # A tibble: 47 Γ— 2 -#> `function` argumentCount -#> -#> 1 .First.sys 0 -#> 2 .NotYetImplemented 0 -#> 3 .OptRequireMethods 0 -#> 4 .standard_regexps 0 -#> 5 .tryResumeInterrupt 0 -#> 6 closeAllConnections 0 -#> 7 contributors 0 -#> 8 Cstack_info 0 -#> 9 date 0 -#> 10 default.stringsAsFactors 0 -#> # … with 37 more rows -``` - -c. How could you adapt the code to find all primitive functions? - - -```r -objs <- mget(ls("package:base", all = TRUE), inherits = TRUE) -funs <- Filter(is.function, objs) -primitives <- Filter(is.primitive, funs) - -length(primitives) -#> [1] 204 - -names(primitives) -#> [1] "-" ":" -#> [3] "::" ":::" -#> [5] "!" "!=" -#> [7] "...elt" "...length" -#> [9] "...names" ".C" -#> [11] ".cache_class" ".Call" -#> [13] ".Call.graphics" ".class2" -#> [15] ".External" ".External.graphics" -#> [17] ".External2" ".Fortran" -#> [19] ".Internal" ".isMethodsDispatchOn" -#> [21] ".Primitive" ".primTrace" -#> [23] ".primUntrace" ".subset" -#> [25] ".subset2" "(" -#> [27] "[" "[[" -#> [29] "[[<-" "[<-" -#> [31] "{" "@" -#> [33] "@<-" "*" -#> [35] "/" "&" -#> [37] "&&" "%*%" -#> [39] "%/%" "%%" -#> [41] "^" "+" -#> [43] "<" "<-" -#> [45] "<<-" "<=" -#> [47] "=" "==" -#> [49] ">" ">=" -#> [51] "|" "||" -#> [53] "~" "$" -#> [55] "$<-" "abs" -#> [57] "acos" "acosh" -#> [59] "all" "any" -#> [61] "anyNA" "Arg" -#> [63] "as.call" "as.character" -#> [65] "as.complex" "as.double" -#> [67] "as.environment" "as.integer" -#> [69] "as.logical" "as.numeric" -#> [71] "as.raw" "asin" -#> [73] "asinh" "atan" -#> [75] "atanh" "attr" -#> [77] "attr<-" "attributes" -#> [79] "attributes<-" "baseenv" -#> [81] "break" "browser" -#> [83] "c" "call" -#> [85] "ceiling" "class" -#> [87] "class<-" "Conj" -#> [89] "cos" "cosh" -#> [91] "cospi" "cummax" -#> [93] "cummin" "cumprod" -#> [95] "cumsum" "digamma" -#> [97] "dim" "dim<-" -#> [99] "dimnames" "dimnames<-" -#> [101] "emptyenv" "enc2native" -#> [103] "enc2utf8" "environment<-" -#> [105] "exp" "expm1" -#> [107] "expression" "floor" -#> [109] "for" "forceAndCall" -#> [111] "function" "gamma" -#> [113] "gc.time" "globalenv" -#> [115] "if" "Im" -#> [117] "interactive" "invisible" -#> [119] "is.array" "is.atomic" -#> [121] "is.call" "is.character" -#> [123] "is.complex" "is.double" -#> [125] "is.environment" "is.expression" -#> [127] "is.finite" "is.function" -#> [129] "is.infinite" "is.integer" -#> [131] "is.language" "is.list" -#> [133] "is.logical" "is.matrix" -#> [135] "is.na" "is.name" -#> [137] "is.nan" "is.null" -#> [139] "is.numeric" "is.object" -#> [141] "is.pairlist" "is.raw" -#> [143] "is.recursive" "is.single" -#> [145] "is.symbol" "isS4" -#> [147] "lazyLoadDBfetch" "length" -#> [149] "length<-" "levels<-" -#> [151] "lgamma" "list" -#> [153] "log" "log10" -#> [155] "log1p" "log2" -#> [157] "max" "min" -#> [159] "missing" "Mod" -#> [161] "names" "names<-" -#> [163] "nargs" "next" -#> [165] "nzchar" "oldClass" -#> [167] "oldClass<-" "on.exit" -#> [169] "pos.to.env" "proc.time" -#> [171] "prod" "quote" -#> [173] "range" "Re" -#> [175] "rep" "repeat" -#> [177] "retracemem" "return" -#> [179] "round" "seq_along" -#> [181] "seq_len" "seq.int" -#> [183] "sign" "signif" -#> [185] "sin" "sinh" -#> [187] "sinpi" "sqrt" -#> [189] "standardGeneric" "storage.mode<-" -#> [191] "substitute" "sum" -#> [193] "switch" "tan" -#> [195] "tanh" "tanpi" -#> [197] "tracemem" "trigamma" -#> [199] "trunc" "unclass" -#> [201] "untracemem" "UseMethod" -#> [203] "while" "xtfrm" -``` - -**Q6.** What are the three important components of a function? - -**A6.** Except for primitive functions, all functions have 3 important components: - -* `formals()` -* `body()` -* `environment()` - -**Q7.** When does printing a function not show the environment it was created in? - -**A7.** All package functions print their environment: - - -```r -# base -mean -#> function (x, ...) -#> UseMethod("mean") -#> -#> - -# other package function -purrr::map -#> function (.x, .f, ...) -#> { -#> .f <- as_mapper(.f, ...) -#> .Call(map_impl, environment(), ".x", ".f", "list") -#> } -#> -#> -``` - -There are two exceptions where the enclosing environment won't be printed: - -- primitive functions - - -```r -sum -#> function (..., na.rm = FALSE) .Primitive("sum") -``` - -- functions created in the global environment - - -```r -f <- function(x) mean(x) -f -#> function(x) mean(x) -``` - -## Lexical scoping (Exercises 6.4.5) - -**Q1.** What does the following code return? Why? Describe how each of the three `c`'s is interpreted. - - -```r -c <- 10 -c(c = c) -``` - -**A1.** In `c(c = c)`: - -* first *c* is interpreted as a function call `c()` -* second *c* as a name for the vector element -* third *c* as a variable with value `10` - - -```r -c <- 10 -c(c = c) -#> c -#> 10 -``` - -You can also see this in the lexical analysis of this expression: - - -```r -p_expr <- parse(text = "c(c = c)", keep.source = TRUE) -getParseData(p_expr) %>% select(token, text) -#> token text -#> 12 expr -#> 1 SYMBOL_FUNCTION_CALL c -#> 3 expr -#> 2 '(' ( -#> 4 SYMBOL_SUB c -#> 5 EQ_SUB = -#> 6 SYMBOL c -#> 8 expr -#> 7 ')' ) -``` - -**Q2.** What are the four principles that govern how R looks for values? - -**A2.** Principles that govern how R looks for values: - -1. Name masking (names defined inside a function mask names defined outside a function) - -1. Functions vs. variables (the rule above also applies to function names) - -1. A fresh start (every time a function is called, a new environment is created to host its execution) - -1. Dynamic look-up (R looks for values when the function is run, not when the function is created) - -**Q3.** What does the following function return? Make a prediction before running the code yourself. - - -```r -f <- function(x) { - f <- function(x) { - f <- function() { - x^2 - } - f() + 1 - } - f(x) * 2 -} -f(10) -``` - -**A3.** Correctly predicted πŸ˜‰ - - -```r -f <- function(x) { - f <- function(x) { - f <- function() { - x^2 - } - f() + 1 - } - f(x) * 2 -} - -f(10) -#> [1] 202 -``` - -Although there are multiple `f()` functions, the order of evaluation goes from inside to outside with `x^2` evaluated first and `f(x) * 2` evaluated last. This results in 202 (= `((10 ^ 2) + 1) * 2`). - -## Lazy evaluation (Exercises 6.5.4) - -**Q1.** What important property of `&&` makes `x_ok()` work? - - -```r -x_ok <- function(x) { - !is.null(x) && length(x) == 1 && x > 0 -} - -x_ok(NULL) -x_ok(1) -x_ok(1:3) -``` - -What is different with this code? Why is this behaviour undesirable here? - - -```r -x_ok <- function(x) { - !is.null(x) & length(x) == 1 & x > 0 -} - -x_ok(NULL) -x_ok(1) -x_ok(1:3) -``` - -**A1.** `&&` evaluates left to right and has short-circuit evaluation, i.e., if the first operand is `TRUE`, R will short-circuit and not even look at the second operand. - - -```r -x_ok <- function(x) { - !is.null(x) && length(x) == 1 && x > 0 -} - -x_ok(NULL) -#> [1] FALSE - -x_ok(1) -#> [1] TRUE - -x_ok(1:3) -#> [1] FALSE -``` - -Replacing `&&` with `&` is undesirable because it performs element-wise logical comparisons and returns a vector of values that is not always useful for a decision (`TRUE`, `FALSE`, or `NA`). - - -```r -x_ok <- function(x) { - !is.null(x) & length(x) == 1 & x > 0 -} - -x_ok(NULL) -#> logical(0) - -x_ok(1) -#> [1] TRUE - -x_ok(1:3) -#> [1] FALSE FALSE FALSE -``` - -**Q2.** What does this function return? Why? Which principle does it illustrate? - - -```r -f2 <- function(x = z) { - z <- 100 - x -} -f2() -``` - -**A2.** The function returns `100` due to lazy evaluation. - -When function execution environment encounters `x`, it evaluates argument `x = z` and since the name `z` is already bound to the value 100 in this environment, `x` is also bound to the same value. - -We can check this by looking at the memory addresses: - - -```r -f2 <- function(x = z) { - z <- 100 - print(lobstr::obj_addrs(list(x, z))) - x -} - -f2() -#> [1] "0x114d77808" "0x114d77808" -#> [1] 100 -``` - -**Q3.** What does this function return? Why? Which principle does it illustrate? - - -```r -y <- 10 -f1 <- function(x = - { - y <- 1 - 2 - }, - y = 0) { - c(x, y) -} -f1() -y -``` - -**A3.** Let's first look at what the function returns: - - -```r -y <- 10 -f1 <- function(x = - { - y <- 1 - 2 - }, - y = 0) { - c(x, y) -} -f1() -#> [1] 2 1 -y -#> [1] 10 -``` - -This is because of name masking. In the function call `c(x, y)`, when `x` is accessed in the function environment, the following promise is evaluated in the function environment: - - -```r -x <- { - y <- 1 - 2 -} -``` - -And, thus `y` gets assigned to `1`, and `x` to `2`, since its the last value in that scope. - -Therefore, neither the promise `y = 0` nor global assignment `y <- 10` is ever consulted to find the value for `y`. - -**Q4.** In `hist()`, the default value of `xlim` is `range(breaks)`, the default value for `breaks` is `"Sturges"`, and - - -```r -range("Sturges") -#> [1] "Sturges" "Sturges" -``` - -Explain how `hist()` works to get a correct `xlim` value. - -**A4.** The `xlim` defines the range of the histogram's `x`-axis. - - -```r -hist(mtcars$wt, xlim = c(1, 6)) -``` - - - -The default `xlim = range(breaks)` and `breaks = "Sturges"` arguments reveal that the function uses Sturges' algorithm to compute the number of breaks. - - -```r -nclass.Sturges(mtcars$wt) -#> [1] 6 -``` - -To see the implementation, run `sloop::s3_get_method("hist.default")`. - -`hist()` ensures that the chosen algorithm returns a numeric vector containing at least two unique elements before `xlim` is computed. - -**Q5.** Explain why this function works. Why is it confusing? - - -```r -show_time <- function(x = stop("Error!")) { - stop <- function(...) Sys.time() - print(x) -} - -show_time() -#> [1] "2022-11-12 11:48:38 CET" -``` - -**A5.** Let's take this step-by-step. - -The function argument `x` is missing in the function call. This means that `stop("Error!")` is evaluated in the function environment, and not global environment. - -But, due to lazy evaluation, the promise `stop("Error!")` is evaluated only when `x` is accessed. This happens only when `print(x)` is called. - -`print(x)` leads to `x` being evaluated, which evaluates `stop` in the function environment. But, in function environment, the `base::stop()` is masked by a locally defined `stop()` function, which returns `Sys.time()` output. - -**Q6.** How many arguments are required when calling `library()`? - -**A6.** Going solely by its signature, - - -```r -formals(library) -#> $package -#> -#> -#> $help -#> -#> -#> $pos -#> [1] 2 -#> -#> $lib.loc -#> NULL -#> -#> $character.only -#> [1] FALSE -#> -#> $logical.return -#> [1] FALSE -#> -#> $warn.conflicts -#> -#> -#> $quietly -#> [1] FALSE -#> -#> $verbose -#> getOption("verbose") -#> -#> $mask.ok -#> -#> -#> $exclude -#> -#> -#> $include.only -#> -#> -#> $attach.required -#> missing(include.only) -``` - -it looks like the following arguments are required: - - -```r -formals(library) %>% - purrr::discard(is.null) %>% - purrr::map_lgl(~ .x == "") %>% - purrr::keep(~ isTRUE(.x)) %>% - names() -#> [1] "package" "help" "warn.conflicts" -#> [4] "mask.ok" "exclude" "include.only" -``` - -But, in reality, only one argument is required: `package`. The function internally checks if the other arguments are missing and adjusts accordingly. - -It would have been better if there arguments were `NULL` instead of missing; that would avoid this confusion. - -## `...` (dot-dot-dot) (Exercises 6.6.1) - -**Q1.** Explain the following results: - - -```r -sum(1, 2, 3) -#> [1] 6 -mean(1, 2, 3) -#> [1] 1 - -sum(1, 2, 3, na.omit = TRUE) -#> [1] 7 -mean(1, 2, 3, na.omit = TRUE) -#> [1] 1 -``` - -**A1.** Let's look at arguments for these functions: - - -```r -str(sum) -#> function (..., na.rm = FALSE) -str(mean) -#> function (x, ...) -``` - -As can be seen, `sum()` function doesn't have `na.omit` argument. So, the input `na.omit = TRUE` is treated as `1` (logical implicitly coerced to numeric), and thus the results. So, the expression evaluates to `sum(1, 2, 3, 1)`. - -For `mean()` function, there is only one parameter (`x`) and it's matched by the first argument (`1`). So, the expression evaluates to `mean(1)`. - -**Q2.** Explain how to find the documentation for the named arguments in the following function call: - - -```r -plot(1:10, col = "red", pch = 20, xlab = "x", col.lab = "blue") -``` - - - -**A2.** Typing `?plot` in the console, we see its documentation, which also shows its signature: - - -``` -#> function (x, y, ...) -``` - -Since `...` are passed to `par()`, we can look at `?par` docs: - - -``` -#> function (..., no.readonly = FALSE) -``` - -And so on. - -The docs for all parameters of interest [reside there](https://rdrr.io/r/graphics/par.html). - -**Q3.** Why does `plot(1:10, col = "red")` only colour the points, not the axes or labels? Read the source code of `plot.default()` to find out. - -**A3.** Source code can be found [here](https://github.com/wch/r-source/blob/79e73dba5259b25ec30118d45fea64aeac0f41dc/src/library/graphics/R/plot.R#L51-L84). - -`plot.default()` passes `...` to `localTitle()`, which passes it to `title()`. - -`title()` has four parts: `main`, `sub`, `xlab`, `ylab`. - -So having a single argument `col` would not work as it will be ambiguous as to which element to apply this argument to. - - -```r -localTitle <- function(..., col, bg, pch, cex, lty, lwd) title(...) - -title <- function(main = NULL, sub = NULL, xlab = NULL, ylab = NULL, - line = NA, outer = FALSE, ...) { - main <- as.graphicsAnnot(main) - sub <- as.graphicsAnnot(sub) - xlab <- as.graphicsAnnot(xlab) - ylab <- as.graphicsAnnot(ylab) - .External.graphics(C_title, main, sub, xlab, ylab, line, outer, ...) - invisible() -} -``` - -## Exiting a function (Exercises 6.7.5) - -**Q1.** What does `load()` return? Why don't you normally see these values? - -**A1.** The `load()` function reloads datasets that were saved using the `save()` function: - - -```r -save(iris, file = "my_iris.rda") -load("my_iris.rda") -``` - -We normally don't see any value because the function loads the datasets invisibly. - -We can change this by setting `verbose = TRUE`: - - -```r -load("my_iris.rda", verbose = TRUE) -#> Loading objects: -#> iris - -# cleanup -unlink("my_iris.rda") -``` - -**Q2.** What does `write.table()` return? What would be more useful? - -**A2.** The `write.table()` writes a data frame to a file and returns a `NULL` invisibly. - - -```r -write.table(BOD, file = "BOD.csv") -``` - -It would have been more helpful if the function invisibly returned the actual object being written to the file, which could then be further used. - - -```r -# cleanup -unlink("BOD.csv") -``` - -**Q3.** How does the `chdir` parameter of `source()` compare to `with_dir()`? Why might you prefer one to the other? - -**A3.** The `chdir` parameter of `source()` is described as: - -> if `TRUE` and `file` is a pathname, the `R` working directory is temporarily changed to the directory containing file for evaluating - -That is, `chdir` allows changing working directory temporarily but *only* to the directory containing file being sourced: - -While `withr::with_dir()` temporarily changes the current working directory: - - -```r -withr::with_dir -#> function (new, code) -#> { -#> old <- setwd(dir = new) -#> on.exit(setwd(old)) -#> force(code) -#> } -#> -#> -``` - -More importantly, its parameters `dir` allows temporarily changing working directory to *any* directory. - -**Q4.** Write a function that opens a graphics device, runs the supplied code, and closes the graphics device (always, regardless of whether or not the plotting code works). - -**A4.** Here is a function that opens a graphics device, runs the supplied code, and closes the graphics device: - - -```r -with_png_device <- function(filename, code, ...) { - grDevices::png(filename = filename, ...) - on.exit(grDevices::dev.off(), add = TRUE) - - force(code) -} -``` - -**Q5.** We can use `on.exit()` to implement a simple version of `capture.output()`. - - -```r -capture.output2 <- function(code) { - temp <- tempfile() - on.exit(file.remove(temp), add = TRUE, after = TRUE) - - sink(temp) - on.exit(sink(), add = TRUE, after = TRUE) - - force(code) - readLines(temp) -} - -capture.output2(cat("a", "b", "c", sep = "\n")) -#> [1] "a" "b" "c" -``` - -Compare `capture.output()` to `capture.output2()`. How do the functions differ? What features have I removed to make the key ideas easier to see? How have I rewritten the key ideas so they're easier to understand? - -**A5.** The `capture.output()` is significantly more complex, as can be seen by its definition: - - -```r -capture.output -#> function (..., file = NULL, append = FALSE, type = c("output", -#> "message"), split = FALSE) -#> { -#> type <- match.arg(type) -#> rval <- NULL -#> closeit <- TRUE -#> if (is.null(file)) -#> file <- textConnection("rval", "w", local = TRUE) -#> else if (is.character(file)) -#> file <- file(file, if (append) -#> "a" -#> else "w") -#> else if (inherits(file, "connection")) { -#> if (!isOpen(file)) -#> open(file, if (append) -#> "a" -#> else "w") -#> else closeit <- FALSE -#> } -#> else stop("'file' must be NULL, a character string or a connection") -#> sink(file, type = type, split = split) -#> on.exit({ -#> sink(type = type, split = split) -#> if (closeit) close(file) -#> }) -#> for (i in seq_len(...length())) { -#> out <- withVisible(...elt(i)) -#> if (out$visible) -#> print(out$value) -#> } -#> on.exit() -#> sink(type = type, split = split) -#> if (closeit) -#> close(file) -#> if (is.null(rval)) -#> invisible(NULL) -#> else rval -#> } -#> -#> -``` - -Here are few key differences: - -- `capture.output()` uses `print()` function to print to console: - - -```r -capture.output(1) -#> [1] "[1] 1" - -capture.output2(1) -#> character(0) -``` - -- `capture.output()` can capture messages as well: - - -```r -capture.output(message("Hi there!"), "a", type = "message") -#> Hi there! -#> [1] "a" -#> character(0) -``` - -- `capture.output()` takes into account visibility of the expression: - - -```r -capture.output(1, invisible(2), 3) -#> [1] "[1] 1" "[1] 3" -``` - -## Function forms (Exercises 6.8.6) - -**Q1.** Rewrite the following code snippets into prefix form: - - -```r -1 + 2 + 3 - -1 + (2 + 3) - -if (length(x) <= 5) x[[5]] else x[[n]] -``` - -**A1.** Prefix forms for code snippets: - - -```r -# The binary `+` operator has left to right associative property. -`+`(`+`(1, 2), 3) - -`+`(1, `(`(`+`(2, 3))) - -`if`(cond = `<=`(length(x), 5), cons.expr = `[[`(x, 5), alt.expr = `[[`(x, n)) -``` - -**Q2.** Clarify the following list of odd function calls: - - -```r -x <- sample(replace = TRUE, 20, x = c(1:10, NA)) -y <- runif(min = 0, max = 1, 20) -cor(m = "k", y = y, u = "p", x = x) -``` - -**A2.** These functions don't have dots (`...`) as parameters, so the argument matching takes place in the following steps: - -- exact matching for named arguments -- partial matching -- position-based - -**Q3.** Explain why the following code fails: - - -```r -modify(get("x"), 1) <- 10 -#> Error: target of assignment expands to non-language object -``` - -**A3.** As provided in the book, the replacement function is defined as: - - -```r -`modify<-` <- function(x, position, value) { - x[position] <- value - x -} -``` - -Let's re-write the provided code in prefix format to understand why it doesn't work: - - -```r -get("x") <- `modify<-`(x = get("x"), position = 1, value = 10) -``` - -Although this works: - - -```r -x <- 5 -`modify<-`(x = get("x"), position = 1, value = 10) -#> [1] 10 -``` - -The following doesn't because the code above evaluates to: - - -```r -`get<-`("x", 10) -#> Error in `get<-`("x", 10): could not find function "get<-" -``` - -And there is no `get<-` function in R. - -**Q4.** Create a replacement function that modifies a random location in a vector. - -**A4.** A replacement function that modifies a random location in a vector: - - -```r -`random_modify<-` <- function(x, value) { - random_index <- sample(seq_along(x), size = 1) - x[random_index] <- value - return(x) -} -``` - -Let's try it out: - - -```r -x1 <- rep("a", 10) -random_modify(x1) <- "X" -x1 -#> [1] "a" "a" "a" "a" "X" "a" "a" "a" "a" "a" - -x2 <- rep("a", 10) -random_modify(x2) <- "Y" -x2 -#> [1] "a" "a" "a" "a" "a" "Y" "a" "a" "a" "a" - -x3 <- rep(0, 15) -random_modify(x3) <- -4 -x3 -#> [1] 0 0 0 0 -4 0 0 0 0 0 0 0 0 0 0 - -x4 <- rep(0, 15) -random_modify(x4) <- -1 -x4 -#> [1] 0 0 0 0 0 0 0 0 0 0 0 0 -1 0 0 -``` - -**Q5.** Write your own version of `+` that pastes its inputs together if they are character vectors but behaves as usual otherwise. In other words, make this code work: - - -```r -1 + 2 -#> [1] 3 - -"a" + "b" -#> [1] "ab" -``` - -**A5.** Infix operator to re-create the desired output: - - -```r -`+` <- function(x, y) { - if (is.character(x) || is.character(y)) { - paste0(x, y) - } else { - base::`+`(x, y) - } -} - -1 + 2 -#> [1] 3 - -"a" + "b" -#> [1] "ab" - -rm("+", envir = .GlobalEnv) -``` - -**Q6.** Create a list of all the replacement functions found in the base package. Which ones are primitive functions? (Hint: use `apropos()`.) - -**A6.** Replacement functions always have `<-` at the end of their names. - -So, using `apropos()`, we can find all replacement functions in search paths and the filter out the ones that don't belong to `{base}` package: - - -```r -ls_replacement <- apropos("<-", where = TRUE, mode = "function") - -base_index <- which(grepl("base", searchpaths())) - -ls_replacement <- ls_replacement[which(names(ls_replacement) == as.character(base_index))] - -unname(ls_replacement) -#> [1] ".rowNamesDF<-" "[[<-" -#> [3] "[[<-.data.frame" "[[<-.factor" -#> [5] "[[<-.numeric_version" "[[<-.POSIXlt" -#> [7] "[<-" "[<-.data.frame" -#> [9] "[<-.Date" "[<-.difftime" -#> [11] "[<-.factor" "[<-.numeric_version" -#> [13] "[<-.POSIXct" "[<-.POSIXlt" -#> [15] "@<-" "<-" -#> [17] "<<-" "$<-" -#> [19] "$<-.data.frame" "attr<-" -#> [21] "attributes<-" "body<-" -#> [23] "class<-" "colnames<-" -#> [25] "comment<-" "diag<-" -#> [27] "dim<-" "dimnames<-" -#> [29] "dimnames<-.data.frame" "Encoding<-" -#> [31] "environment<-" "formals<-" -#> [33] "is.na<-" "is.na<-.default" -#> [35] "is.na<-.factor" "is.na<-.numeric_version" -#> [37] "length<-" "length<-.Date" -#> [39] "length<-.difftime" "length<-.factor" -#> [41] "length<-.POSIXct" "length<-.POSIXlt" -#> [43] "levels<-" "levels<-.factor" -#> [45] "mode<-" "mostattributes<-" -#> [47] "names<-" "names<-.POSIXlt" -#> [49] "oldClass<-" "parent.env<-" -#> [51] "regmatches<-" "row.names<-" -#> [53] "row.names<-.data.frame" "row.names<-.default" -#> [55] "rownames<-" "split<-" -#> [57] "split<-.data.frame" "split<-.default" -#> [59] "storage.mode<-" "substr<-" -#> [61] "substring<-" "units<-" -#> [63] "units<-.difftime" -``` - -The primitive replacement functions can be listed using `is.primitive()`: - - -```r -mget(ls_replacement, envir = baseenv()) %>% - purrr::keep(is.primitive) %>% - names() -#> [1] "[[<-" "[<-" "@<-" -#> [4] "<-" "<<-" "$<-" -#> [7] "attr<-" "attributes<-" "class<-" -#> [10] "dim<-" "dimnames<-" "environment<-" -#> [13] "length<-" "levels<-" "names<-" -#> [16] "oldClass<-" "storage.mode<-" -``` - -**Q7.** What are valid names for user-created infix functions? - -**A7.** As mentioned in the respective [section](https://adv-r.hadley.nz/functions.html#infix-functions) of the book: - -> The names of infix functions are more flexible than regular R functions: they can contain any sequence of characters except for `%`. - -**Q8.** Create an infix `xor()` operator. - -**A8.** Exclusive OR is a logical operation that is `TRUE` if and only if its arguments differ (one is `TRUE`, the other is `FALSE`). - - -```r -lv1 <- c(TRUE, FALSE, TRUE, FALSE) -lv2 <- c(TRUE, TRUE, FALSE, FALSE) - -xor(lv1, lv2) -#> [1] FALSE TRUE TRUE FALSE -``` - -We can create infix operator for exclusive OR like so: - - -```r -`%xor%` <- function(x, y) { - !((x & y) | !(x | y)) -} - -lv1 %xor% lv2 -#> [1] FALSE TRUE TRUE FALSE - -TRUE %xor% TRUE -#> [1] FALSE -``` - -The function is vectorized over its inputs because the underlying logical operators themselves are vectorized. - -**Q9.** Create infix versions of the set functions `intersect()`, `union()`, and `setdiff()`. You might call them `%n%`, `%u%`, and `%/%` to match conventions from mathematics. - -**A9.** The required infix operators can be created as following: - - -```r -`%n%` <- function(x, y) { - intersect(x, y) -} - -`%u%` <- function(x, y) { - union(x, y) -} - -`%/%` <- function(x, y) { - setdiff(x, y) -} -``` - -We can check that the outputs agree with the underlying functions: - - -```r -(x <- c(sort(sample(1:20, 9)), NA)) -#> [1] 4 7 8 9 11 13 15 16 20 NA -(y <- c(sort(sample(3:23, 7)), NA)) -#> [1] 9 10 13 15 17 19 20 NA - -identical(intersect(x, y), x %n% y) -#> [1] TRUE -identical(union(x, y), x %u% y) -#> [1] TRUE -identical(setdiff(x, y), x %/% y) -#> [1] TRUE -``` - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> backports 1.4.1 2021-12-13 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> broom 1.0.1 2022-08-29 [1] CRAN (R 4.2.0) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cellranger 1.1.0 2016-07-27 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1) -#> P datasets * 4.2.2 2022-10-31 [1] local -#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388) -#> dbplyr 2.2.1 2022-06-27 [1] CRAN (R 4.2.0) -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.1) -#> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.2.0) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> forcats * 0.5.2 2022-08-19 [1] CRAN (R 4.2.1) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> gargle 1.2.1 2022-09-08 [1] CRAN (R 4.2.1) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.2) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> googledrive 2.0.0 2021-07-08 [1] CRAN (R 4.2.0) -#> googlesheets4 1.0.1 2022-08-13 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> P grid 4.2.2 2022-10-31 [1] local -#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.1) -#> haven 2.5.1 2022-08-22 [1] CRAN (R 4.2.0) -#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0) -#> hms 1.1.2 2022-08-19 [1] CRAN (R 4.2.0) -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> httr 1.4.4 2022-08-17 [1] CRAN (R 4.2.0) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> lobstr 1.1.2 2022-06-22 [1] CRAN (R 4.2.0) -#> lubridate 1.9.0 2022-11-06 [1] CRAN (R 4.2.2) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> modelr 0.1.10 2022-11-11 [1] CRAN (R 4.2.2) -#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0) -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> readr * 2.1.3 2022-10-01 [1] CRAN (R 4.2.1) -#> readxl 1.4.1 2022-08-17 [1] CRAN (R 4.2.0) -#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.1) -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> rvest 1.0.3 2022-08-19 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr * 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble * 3.1.8.9002 2022-10-16 [1] local -#> tidyr * 1.2.1 2022-09-08 [1] CRAN (R 4.2.1) -#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1) -#> tidyverse * 1.3.2 2022-07-18 [1] CRAN (R 4.2.0) -#> timechange 0.1.1 2022-11-04 [1] CRAN (R 4.2.2) -#> P tools 4.2.2 2022-10-31 [1] local -#> tzdb 0.3.0 2022-03-28 [1] CRAN (R 4.2.0) -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` - diff --git a/_book/Functions_files/figure-html/Functions-36-1.png b/_book/Functions_files/figure-html/Functions-36-1.png deleted file mode 100644 index 2c2a55ff..00000000 Binary files a/_book/Functions_files/figure-html/Functions-36-1.png and /dev/null differ diff --git a/_book/Functions_files/figure-html/Functions-43-1.png b/_book/Functions_files/figure-html/Functions-43-1.png deleted file mode 100644 index 0950e226..00000000 Binary files a/_book/Functions_files/figure-html/Functions-43-1.png and /dev/null differ diff --git a/_book/Introduction.md b/_book/Introduction.md deleted file mode 100644 index f296ac22..00000000 --- a/_book/Introduction.md +++ /dev/null @@ -1,4 +0,0 @@ -# Introduction - -No exercises. - diff --git a/_book/Names-values.md b/_book/Names-values.md deleted file mode 100644 index c5b6c270..00000000 --- a/_book/Names-values.md +++ /dev/null @@ -1,652 +0,0 @@ -# Names and values - - - -Loading the needed libraries: - - -```r -library(lobstr) -``` - -## Binding basics (Exercise 2.2.2) - ---- - -**Q1.** Explain the relationship between `a`, `b`, `c` and `d` in the following code: - - -```r -a <- 1:10 -b <- a -c <- b -d <- 1:10 -``` - -**A1.** The names (`a`, `b`, and `c`) have same values and point to the same object in memory, as can be seen by their identical memory addresses: - - -```r -obj_addrs <- obj_addrs(list(a, b, c)) -unique(obj_addrs) -#> [1] "0x11a944a10" -``` - -Except `d`, which is a different object, even if it has the same value as `a`, `b`, and `c`: - - -```r -obj_addr(d) -#> [1] "0x11a748978" -``` - ---- - -**Q2.** The following code accesses the mean function in multiple ways. Do they all point to the same underlying function object? Verify this with `lobstr::obj_addr()`. - - -```r -mean -base::mean -get("mean") -evalq(mean) -match.fun("mean") -``` - -**A2.** All listed function calls point to the same underlying function object in memory, as shown by this object's memory address: - - -```r -obj_addrs <- obj_addrs(list( - mean, - base::mean, - get("mean"), - evalq(mean), - match.fun("mean") -)) - -unique(obj_addrs) -#> [1] "0x11a25e2d8" -``` - ---- - -**Q3.** By default, base R data import functions, like `read.csv()`, will automatically convert non-syntactic names to syntactic ones. Why might this be problematic? What option allows you to suppress this behaviour? - -**A3.** The conversion of non-syntactic names to syntactic ones can sometimes corrupt the data. Some datasets may require non-syntactic names. - -To suppress this behavior, one can set `check.names = FALSE`. - ---- - -**Q4.** What rules does `make.names()` use to convert non-syntactic names into syntactic ones? - -**A4.** `make.names()` uses following rules to convert non-syntactic names into syntactic ones: - -- it prepends non-syntactic names with `X` -- it converts invalid characters (like `@`) to `.` -- it adds a `.` as a suffix if the name is a [reserved keyword](https://stat.ethz.ch/R-manual/R-devel/library/base/html/Reserved.html) - - -```r -make.names(c("123abc", "@me", "_yu", " gh", "else")) -#> [1] "X123abc" "X.me" "X_yu" "X..gh" "else." -``` - ---- - -**Q5.** I slightly simplified the rules that govern syntactic names. Why is `.123e1` not a syntactic name? Read `?make.names` for the full details. - -**A5.** `.123e1` is not a syntacti name because it is parsed as a number, and not as a string: - - -```r -typeof(.123e1) -#> [1] "double" -``` - -And as the docs mention (emphasis mine): - -> A syntactically valid name consists of letters, numbers and the dot or underline characters and starts with a letter or **the dot not followed by a number**. - ---- - -## Copy-on-modify (Exercise 2.3.6) - ---- - -**Q1.** Why is `tracemem(1:10)` not useful? - -**A1.** `tracemem()` traces copying of objects in R. For example: - - -```r -x <- 1:10 - -tracemem(x) -#> [1] "<0x107924860>" - -x <- x + 1 - -untracemem(x) -``` - -But since the object created in memory by `1:10` is not assigned a name, it can't be addressed or modified from R, and so there is nothing to trace. - - -```r -obj_addr(1:10) -#> [1] "0x1188da070" - -tracemem(1:10) -#> [1] "<0x1189377b0>" -``` - ---- - -**Q2.** Explain why `tracemem()` shows two copies when you run this code. Hint: carefully look at the difference between this code and the code shown earlier in the section. - - -```r -x <- c(1L, 2L, 3L) -tracemem(x) - -x[[3]] <- 4 -untracemem(x) -``` - -**A2.** This is because the initial atomic vector is of type `integer`, but `4` (and not `4L`) is of type `double`. This is why a new copy is created. - - -```r -x <- c(1L, 2L, 3L) -typeof(x) -#> [1] "integer" -tracemem(x) -#> [1] "<0x106e8d248>" - -x[[3]] <- 4 -#> tracemem[0x106e8d248 -> 0x105c18848]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file do.call eval eval eval eval eval.parent local -#> tracemem[0x105c18848 -> 0x105c2ca88]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file do.call eval eval eval eval eval.parent local -untracemem(x) - -typeof(x) -#> [1] "double" -``` - -Trying with an integer should not create another copy: - - -```r -x <- c(1L, 2L, 3L) -typeof(x) -#> [1] "integer" -tracemem(x) -#> [1] "<0x107ac8348>" - -x[[3]] <- 4L -#> tracemem[0x107ac8348 -> 0x118c7a9c8]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file do.call eval eval eval eval eval.parent local -untracemem(x) - -typeof(x) -#> [1] "integer" -``` - -To understand why this still produces a copy, here is an explanation from the [official solutions manual](https://advanced-r-solutions.rbind.io/names-and-values.html#copy-on-modify): - -> Please be aware that running this code in RStudio will result in additional copies because of the reference from the environment pane. - ---- - -**Q3.** Sketch out the relationship between the following objects: - - -```r -a <- 1:10 -b <- list(a, a) -c <- list(b, a, 1:10) -``` - -**A3.** We can understand the relationship between these objects by looking at their memory addresses: - - -```r -a <- 1:10 -b <- list(a, a) -c <- list(b, a, 1:10) - -ref(a) -#> [1:0x107d1fd98] - -ref(b) -#> β–ˆ [1:0x107d61d88] -#> β”œβ”€[2:0x107d1fd98] -#> └─[2:0x107d1fd98] - -ref(c) -#> β–ˆ [1:0x107d6aa78] -#> β”œβ”€β–ˆ [2:0x107d61d88] -#> β”‚ β”œβ”€[3:0x107d1fd98] -#> β”‚ └─[3:0x107d1fd98] -#> β”œβ”€[3:0x107d1fd98] -#> └─[4:0x106f78ca8] -``` - -Here is what we learn: - -- The name `a` references object `1:10` in the memory. -- The name `b` is bound to a list of two references to the memory address of `a`. -- The name `c` is also bound to a list of references to `a` and `b`, and `1:10` object (not bound to any name). - ---- - -**Q4.** What happens when you run this code? - - -```r -x <- list(1:10) -x[[2]] <- x -``` - -Draw a picture. - -**A4.** - - -```r -x <- list(1:10) -x -#> [[1]] -#> [1] 1 2 3 4 5 6 7 8 9 10 -obj_addr(x) -#> [1] "0x106577798" - -x[[2]] <- x -x -#> [[1]] -#> [1] 1 2 3 4 5 6 7 8 9 10 -#> -#> [[2]] -#> [[2]][[1]] -#> [1] 1 2 3 4 5 6 7 8 9 10 -obj_addr(x) -#> [1] "0x1188dd148" - -ref(x) -#> β–ˆ [1:0x1188dd148] -#> β”œβ”€[2:0x10658cc00] -#> β””β”€β–ˆ [3:0x106577798] -#> └─[2:0x10658cc00] -``` - -I don't have access to OmniGraffle software, so I am including here the figure from the [official solution manual](https://advanced-r-solutions.rbind.io/names-and-values.html#copy-on-modify): - - - ---- - -## Object size (Exercise 2.4.1) - ---- - -**Q1.** In the following example, why are `object.size(y)` and `obj_size(y)` so radically different? Consult the documentation of `object.size()`. - - -```r -y <- rep(list(runif(1e4)), 100) - -object.size(y) -obj_size(y) -``` - -**A1.** As mentioned in the docs for `object.size()`: - -> This function...does not detect if elements of a list are shared. - -This is why the sizes are so different: - - -```r -y <- rep(list(runif(1e4)), 100) - -object.size(y) -#> 8005648 bytes - -obj_size(y) -#> 80.90 kB -``` - ---- - -**Q2.** Take the following list. Why is its size somewhat misleading? - - -```r -funs <- list(mean, sd, var) -obj_size(funs) -``` - -**A2.** These functions are not externally created objects in R, but are always available as part of base packages, so doesn't make much sense to measure their size because they are never going to be *not* available. - - -```r -funs <- list(mean, sd, var) -obj_size(funs) -#> 17.55 kB -``` - ---- - -**Q3.** Predict the output of the following code: - - -```r -a <- runif(1e6) -obj_size(a) - -b <- list(a, a) -obj_size(b) -obj_size(a, b) - -b[[1]][[1]] <- 10 -obj_size(b) -obj_size(a, b) - -b[[2]][[1]] <- 10 -obj_size(b) -obj_size(a, b) -``` - -**A3.** Correctly predicted πŸ˜‰ - - -```r -a <- runif(1e6) -obj_size(a) -#> 8.00 MB - -b <- list(a, a) -obj_size(b) -#> 8.00 MB -obj_size(a, b) -#> 8.00 MB - -b[[1]][[1]] <- 10 -obj_size(b) -#> 16.00 MB -obj_size(a, b) -#> 16.00 MB - -b[[2]][[1]] <- 10 -obj_size(b) -#> 16.00 MB -obj_size(a, b) -#> 24.00 MB -``` - -Key pieces of information to keep in mind to make correct predictions: - -- Size of empty vector - - -```r -obj_size(double()) -#> 48 B -``` - -- Size of a single double: 8 bytes - - -```r -obj_size(double(1)) -#> 56 B -``` - -- Copy-on-modify semantics - ---- - -## Modify-in-place (Exercise 2.5.3) - ---- - -**Q1.** Explain why the following code doesn't create a circular list. - - -```r -x <- list() -x[[1]] <- x -``` - -**A1.** Copy-on-modify prevents the creation of a circular list. - - -```r -x <- list() - -obj_addr(x) -#> [1] "0x106c2ac38" - -tracemem(x) -#> [1] "<0x106c2ac38>" - -x[[1]] <- x -#> tracemem[0x106c2ac38 -> 0x12a7d3a50]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file do.call eval eval eval eval eval.parent local - -obj_addr(x[[1]]) -#> [1] "0x106c2ac38" -``` - ---- - -**Q2.** Wrap the two methods for subtracting medians into two functions, then use the 'bench' package to carefully compare their speeds. How does performance change as the number of columns increase? - -**A2.** Let's first microbenchmark functions that do and do not create copies for varying lengths of number of columns. - - -```r -library(bench) -library(tidyverse) - -generateDataFrame <- function(ncol) { - as.data.frame(matrix(runif(100 * ncol), nrow = 100)) -} - -withCopy <- function(ncol) { - x <- generateDataFrame(ncol) - medians <- vapply(x, median, numeric(1)) - - for (i in seq_along(medians)) { - x[[i]] <- x[[i]] - medians[[i]] - } - - return(x) -} - -withoutCopy <- function(ncol) { - x <- generateDataFrame(ncol) - medians <- vapply(x, median, numeric(1)) - - y <- as.list(x) - - for (i in seq_along(medians)) { - y[[i]] <- y[[i]] - medians[[i]] - } - - return(y) -} - -benchComparison <- function(ncol) { - bench::mark( - withCopy(ncol), - withoutCopy(ncol), - iterations = 100, - check = FALSE - ) %>% - dplyr::select(expression:total_time) -} - -nColList <- list(1, 10, 50, 100, 250, 500, 1000) - -names(nColList) <- as.character(nColList) - -benchDf <- purrr::map_dfr( - .x = nColList, - .f = benchComparison, - .id = "nColumns" -) -``` - -Plotting these benchmarks reveals how the performance gets increasingly worse as the number of data frames increases: - - -```r -ggplot( - benchDf, - aes( - x = as.numeric(nColumns), - y = median, - group = as.character(expression), - color = as.character(expression) - ) -) + - geom_line() + - labs( - x = "Number of Columns", - y = "Median Execution Time (ms)", - colour = "Type of function" - ) -``` - - - ---- - -**Q3.** What happens if you attempt to use `tracemem()` on an environment? - -**A3.** It doesn't work and the documentation for `tracemem()` makes it clear why: - -> It is not useful to trace `NULL`, environments, promises, weak references, or external pointer objects, as these are not duplicated - - -```r -e <- rlang::env(a = 1, b = "3") -tracemem(e) -#> Error in tracemem(e): 'tracemem' is not useful for promise and environment objects -``` - ---- - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> backports 1.4.1 2021-12-13 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bench * 1.1.2 2021-11-30 [1] CRAN (R 4.2.0) -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> broom 1.0.1 2022-08-29 [1] CRAN (R 4.2.0) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cellranger 1.1.0 2016-07-27 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1) -#> P datasets * 4.2.2 2022-10-31 [1] local -#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388) -#> dbplyr 2.2.1 2022-06-27 [1] CRAN (R 4.2.0) -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.1) -#> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.2.0) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> farver 2.1.1 2022-07-06 [1] CRAN (R 4.2.1) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> forcats * 0.5.2 2022-08-19 [1] CRAN (R 4.2.1) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> gargle 1.2.1 2022-09-08 [1] CRAN (R 4.2.1) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.2) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> googledrive 2.0.0 2021-07-08 [1] CRAN (R 4.2.0) -#> googlesheets4 1.0.1 2022-08-13 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> P grid 4.2.2 2022-10-31 [1] local -#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.1) -#> haven 2.5.1 2022-08-22 [1] CRAN (R 4.2.0) -#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0) -#> hms 1.1.2 2022-08-19 [1] CRAN (R 4.2.0) -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> httr 1.4.4 2022-08-17 [1] CRAN (R 4.2.0) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> labeling 0.4.2 2020-10-20 [1] CRAN (R 4.2.0) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> lobstr * 1.1.2 2022-06-22 [1] CRAN (R 4.2.0) -#> lubridate 1.9.0 2022-11-06 [1] CRAN (R 4.2.2) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> modelr 0.1.10 2022-11-11 [1] CRAN (R 4.2.2) -#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0) -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> prettyunits 1.1.1 2020-01-24 [1] CRAN (R 4.2.0) -#> profmem 0.6.0 2020-12-13 [1] CRAN (R 4.2.0) -#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> readr * 2.1.3 2022-10-01 [1] CRAN (R 4.2.1) -#> readxl 1.4.1 2022-08-17 [1] CRAN (R 4.2.0) -#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.1) -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> rvest 1.0.3 2022-08-19 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr * 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble * 3.1.8.9002 2022-10-16 [1] local -#> tidyr * 1.2.1 2022-09-08 [1] CRAN (R 4.2.1) -#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1) -#> tidyverse * 1.3.2 2022-07-18 [1] CRAN (R 4.2.0) -#> timechange 0.1.1 2022-11-04 [1] CRAN (R 4.2.2) -#> P tools 4.2.2 2022-10-31 [1] local -#> tzdb 0.3.0 2022-03-28 [1] CRAN (R 4.2.0) -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Names-values_files/figure-html/Names-values-31-1.png b/_book/Names-values_files/figure-html/Names-values-31-1.png deleted file mode 100644 index 6210e6f4..00000000 Binary files a/_book/Names-values_files/figure-html/Names-values-31-1.png and /dev/null differ diff --git a/_book/OO-tradeoffs.md b/_book/OO-tradeoffs.md deleted file mode 100644 index afc23c3c..00000000 --- a/_book/OO-tradeoffs.md +++ /dev/null @@ -1,3 +0,0 @@ -# Trade-offs - -No exercises. diff --git a/_book/Perf-improve.md b/_book/Perf-improve.md deleted file mode 100644 index c9dc4ff3..00000000 --- a/_book/Perf-improve.md +++ /dev/null @@ -1,606 +0,0 @@ -# Improving performance - - - -Attaching the needed libraries: - - -```r -library(ggplot2) -library(dplyr) -library(purrr) -``` - -## Exercises 24.3.1 - -**Q1.** What are faster alternatives to `lm()`? Which are specifically designed to work with larger datasets? - -**A1.** Faster alternatives to `lm()` can be found by visiting [CRAN Task View: High-Performance and Parallel Computing with R](https://cran.r-project.org/web/views/HighPerformanceComputing.html) page. - -Here are some of the available options: - -- `speedglm::speedlm()` (for large datasets) - -- `biglm::biglm()` (specifically designed for data too large to fit in memory) - -- `RcppEigen::fastLm()` (using the `Eigen` linear algebra library) - -High performances can be obtained with these packages especially if R is linked against an optimized BLAS, such as ATLAS. You can check this information using `sessionInfo()`: - - -```r -sessInfo <- sessionInfo() -sessInfo$matprod -#> [1] "default" -sessInfo$LAPACK -#> [1] "/Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/lib/libRlapack.dylib" -``` - -Comparing performance of different alternatives: - - -```r -library(gapminder) - -# having a look at the data -glimpse(gapminder) -#> Rows: 1,704 -#> Columns: 6 -#> $ country "Afghanistan", "Afghanistan", "Afghanist… -#> $ continent Asia, Asia, Asia, Asia, Asia, Asia, Asia… -#> $ year 1952, 1957, 1962, 1967, 1972, 1977, 1982… -#> $ lifeExp 28.801, 30.332, 31.997, 34.020, 36.088, … -#> $ pop 8425333, 9240934, 10267083, 11537966, 13… -#> $ gdpPercap 779.4453, 820.8530, 853.1007, 836.1971, … - -bench::mark( - "lm" = stats::lm(lifeExp ~ continent * gdpPercap, gapminder), - "speedglm" = speedglm::speedlm(lifeExp ~ continent * gdpPercap, gapminder), - "biglm" = biglm::biglm(lifeExp ~ continent * gdpPercap, gapminder), - "fastLm" = RcppEigen::fastLm(lifeExp ~ continent * gdpPercap, gapminder), - check = FALSE, - iterations = 1000 -)[1:5] -#> # A tibble: 4 Γ— 5 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 lm 690Β΅s 930.09Β΅s 920. 1.25MB -#> 2 speedglm 723Β΅s 1.06ms 834. 66.37MB -#> 3 biglm 580Β΅s 790.93Β΅s 1120. 936.54KB -#> 4 fastLm 665Β΅s 951.9Β΅s 937. 4.21MB -``` - -The results might change depending on the size of the dataset, with the performance benefits accruing bigger the dataset. - -You will have to experiment with different algorithms and find the one that fits the needs of your dataset the best. - -**Q2.** What package implements a version of `match()` that's faster for repeated look ups? How much faster is it? - -**A2.** The package (and the respective function) is `fastmatch::fmatch()`^[In addition to Google search, you can also try [packagefinder](https://www.zuckarelli.de/packagefinder/tutorial.html) to search for CRAN packages.]. - -The documentation for this function notes: - -> It is slightly faster than the built-in version because it uses more specialized code, but in addition it retains the hash table within the table object such that it can be re-used, dramatically reducing the look-up time especially for large table. - -With a small vector, `fmatch()` is only slightly faster, but of the same order of magnitude. - - -```r -library(fastmatch, warn.conflicts = FALSE) - -small_vec <- c("a", "b", "x", "m", "n", "y") - -length(small_vec) -#> [1] 6 - -bench::mark( - "base" = match(c("x", "y"), small_vec), - "fastmatch" = fmatch(c("x", "y"), small_vec) -)[1:5] -#> # A tibble: 2 Γ— 5 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 base 656ns 779ns 509012. 2.77KB -#> 2 fastmatch 533ns 656ns 639718. 2.66KB -``` - -But, with a larger vector, `fmatch()` is orders of magnitude faster! ⚑ - - -```r -large_vec <- c(rep(c("a", "b"), 1e4), "x", rep(c("m", "n"), 1e6), "y") - -length(large_vec) -#> [1] 2020002 - -bench::mark( - "base" = match(c("x", "y"), large_vec), - "fastmatch" = fmatch(c("x", "y"), large_vec) -)[1:5] -#> # A tibble: 2 Γ— 5 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 base 25.7ms 25.8ms 38.7 31.4MB -#> 2 fastmatch 451ns 656ns 798860. 0B -``` - -We can also look at the hash table: - - -```r -fmatch.hash(c("x", "y"), small_vec) -#> [1] "a" "b" "x" "m" "n" "y" -#> attr(,".match.hash") -#> -``` - -Additionally, `{fastmatch}` provides equivalent of the familiar infix operator: - - -```r -library(fastmatch) - -small_vec <- c("a", "b", "x", "m", "n", "y") - -c("x", "y") %in% small_vec -#> [1] TRUE TRUE - -c("x", "y") %fin% small_vec -#> [1] TRUE TRUE -``` - -**Q3.** List four functions (not just those in base R) that convert a string into a date time object. What are their strengths and weaknesses? - -**A3.** Here are four functions that convert a string into a date time object: - -- `base::as.POSIXct()` - - -```r -base::as.POSIXct("2022-05-05 09:23:22") -#> [1] "2022-05-05 09:23:22 CEST" -``` - -- `base::as.POSIXlt()` - - -```r -base::as.POSIXlt("2022-05-05 09:23:22") -#> [1] "2022-05-05 09:23:22 CEST" -``` - -- `lubridate::ymd_hms()` - - -```r -lubridate::ymd_hms("2022-05-05-09-23-22") -#> [1] "2022-05-05 09:23:22 UTC" -``` - -- `fasttime::fastPOSIXct()` - - -```r -fasttime::fastPOSIXct("2022-05-05 09:23:22") -#> [1] "2022-05-05 11:23:22 CEST" -``` - -We can also compare their performance: - - -```r -bench::mark( - "as.POSIXct" = base::as.POSIXct("2022-05-05 09:23:22"), - "as.POSIXlt" = base::as.POSIXlt("2022-05-05 09:23:22"), - "ymd_hms" = lubridate::ymd_hms("2022-05-05-09-23-22"), - "fastPOSIXct" = fasttime::fastPOSIXct("2022-05-05 09:23:22"), - check = FALSE, - iterations = 1000 -) -#> # A tibble: 4 Γ— 6 -#> expression min median `itr/sec` mem_alloc `gc/sec` -#> -#> 1 as.POSIXct 42.11Β΅s 101.76Β΅s 5378. 0B 0 -#> 2 as.POSIXlt 31.49Β΅s 46.58Β΅s 8462. 0B 8.47 -#> 3 ymd_hms 1.48ms 2.78ms 293. 21.5KB 2.37 -#> 4 fastPOSIXct 615.02ns 738.07ns 963715. 0B 0 -``` - -There are many more packages that implement a way to convert from string to a date time object. For more, see [CRAN Task View: Time Series Analysis](https://cran.r-project.org/web/views/TimeSeries.html) - -**Q4.** Which packages provide the ability to compute a rolling mean? - -**A4.** Here are a few packages and respective functions that provide a way to compute a rolling mean: - -- `RcppRoll::roll_mean()` -- `data.table::frollmean()` -- `roll::roll_mean()` -- `zoo::rollmean()` -- `slider::slide_dbl()` - -**Q5.** What are the alternatives to `optim()`? - -**A5.** The `optim()` function provides general-purpose optimization. As noted in its docs: - -> General-purpose optimization based on Nelder–Mead, quasi-Newton and conjugate-gradient algorithms. It includes an option for box-constrained optimization and simulated annealing. - -There are many alternatives and the exact one you would want to choose would depend on the type of optimization you would like to do. - -Most available options can be seen at [CRAN Task View: Optimization and Mathematical Programming](https://cran.r-project.org/web/views/Optimization.html). - -## Exercises 24.4.3 - -**Q1.** What's the difference between `rowSums()` and `.rowSums()`? - -**A1.** The documentation for these functions state: - -> The versions with an initial dot in the name (.colSums() etc) are β€˜bare-bones’ versions for use in programming: they apply only to numeric (like) matrices and do not name the result. - -Looking at the source code, - -- `rowSums()` function does a number of checks to validate if the arguments are acceptable - - -```r -rowSums -#> function (x, na.rm = FALSE, dims = 1L) -#> { -#> if (is.data.frame(x)) -#> x <- as.matrix(x) -#> if (!is.array(x) || length(dn <- dim(x)) < 2L) -#> stop("'x' must be an array of at least two dimensions") -#> if (dims < 1L || dims > length(dn) - 1L) -#> stop("invalid 'dims'") -#> p <- prod(dn[-(id <- seq_len(dims))]) -#> dn <- dn[id] -#> z <- if (is.complex(x)) -#> .Internal(rowSums(Re(x), prod(dn), p, na.rm)) + (0+1i) * -#> .Internal(rowSums(Im(x), prod(dn), p, na.rm)) -#> else .Internal(rowSums(x, prod(dn), p, na.rm)) -#> if (length(dn) > 1L) { -#> dim(z) <- dn -#> dimnames(z) <- dimnames(x)[id] -#> } -#> else names(z) <- dimnames(x)[[1L]] -#> z -#> } -#> -#> -``` - -- `.rowSums()` directly proceeds to computation using an internal code which is built in to the R interpreter - - -```r -.rowSums -#> function (x, m, n, na.rm = FALSE) -#> .Internal(rowSums(x, m, n, na.rm)) -#> -#> -``` - -But they have comparable performance: - - -```r -x <- cbind(x1 = 3, x2 = c(4:1e4, 2:1e5)) - -bench::mark( - "rowSums" = rowSums(x), - ".rowSums" = .rowSums(x, dim(x)[[1]], dim(x)[[2]]) -)[1:5] -#> # A tibble: 2 Γ— 5 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 rowSums 126Β΅s 334Β΅s 1978. 859KB -#> 2 .rowSums 124Β΅s 240Β΅s 3520. 859KB -``` - -**Q2.** Make a faster version of `chisq.test()` that only computes the chi-square test statistic when the input is two numeric vectors with no missing values. You can try simplifying `chisq.test()` or by coding from the [mathematical definition](http://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test). - -**A2.** If the function is supposed to accept only two numeric vectors without missing values, then we can make `chisq.test()` do less work by removing code corresponding to the following : - -- checks for data frame and matrix inputs -- goodness-of-fit test -- simulating *p*-values -- checking for missing values - -This leaves us with a much simpler, bare bones implementation: - - -```r -my_chisq_test <- function(x, y) { - x <- table(x, y) - n <- sum(x) - - nr <- as.integer(nrow(x)) - nc <- as.integer(ncol(x)) - - sr <- rowSums(x) - sc <- colSums(x) - E <- outer(sr, sc, "*") / n - v <- function(r, c, n) c * r * (n - r) * (n - c) / n^3 - V <- outer(sr, sc, v, n) - dimnames(E) <- dimnames(x) - - STATISTIC <- sum((abs(x - E))^2 / E) - PARAMETER <- (nr - 1L) * (nc - 1L) - PVAL <- pchisq(STATISTIC, PARAMETER, lower.tail = FALSE) - - names(STATISTIC) <- "X-squared" - names(PARAMETER) <- "df" - - structure( - list( - statistic = STATISTIC, - parameter = PARAMETER, - p.value = PVAL, - method = "Pearson's Chi-squared test", - observed = x, - expected = E, - residuals = (x - E) / sqrt(E), - stdres = (x - E) / sqrt(V) - ), - class = "htest" - ) -} -``` - -And, indeed, this custom function performs slightly better^[Deliberately choosing a larger dataset to stress test the new function.] than its base equivalent: - - -```r -m <- c(rep("a", 1000), rep("b", 9000)) -n <- c(rep(c("x", "y"), 5000)) - -bench::mark( - "base" = chisq.test(m, n)$statistic[[1]], - "custom" = my_chisq_test(m, n)$statistic[[1]] -)[1:5] -#> # A tibble: 2 Γ— 5 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 base 839Β΅s 1.18ms 681. 1.47MB -#> 2 custom 624Β΅s 861.41Β΅s 999. 1.13MB -``` - -**Q3.** Can you make a faster version of `table()` for the case of an input of two integer vectors with no missing values? Can you use it to speed up your chi-square test? - -**A3.** In order to make a leaner version of `table()`, we can take a similar approach and trim the unnecessary input checks in light of our new API of accepting just two vectors without missing values. We can remove the following components from the code: - -- extracting data from objects entered in `...` argument -- dealing with missing values -- other input validation checks - -In addition to this removal, we can also use `fastmatch::fmatch()` instead of `match()`: - - -```r -my_table <- function(x, y) { - x_sorted <- sort(unique(x)) - y_sorted <- sort(unique(y)) - - x_length <- length(x_sorted) - y_length <- length(y_sorted) - - bin <- - fastmatch::fmatch(x, x_sorted) + - x_length * fastmatch::fmatch(y, y_sorted) - - x_length - - y <- tabulate(bin, x_length * y_length) - - y <- array( - y, - dim = c(x_length, y_length), - dimnames = list(x = x_sorted, y = y_sorted) - ) - - class(y) <- "table" - y -} -``` - -The custom function indeed performs slightly better: - - -```r -x <- c(rep("a", 1000), rep("b", 9000)) -y <- c(rep(c("x", "y"), 5000)) - -# `check = FALSE` because the custom function has an additional attribute: -# ".match.hash" -bench::mark( - "base" = table(x, y), - "custom" = my_table(x, y), - check = FALSE -)[1:5] -#> # A tibble: 2 Γ— 5 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 base 590Β΅s 926Β΅s 836. 960KB -#> 2 custom 319Β΅s 428Β΅s 2042. 488KB -``` - -We can also use this function in our custom chi-squared test function and see if the performance improves any further: - - -```r -my_chisq_test2 <- function(x, y) { - x <- my_table(x, y) - n <- sum(x) - - nr <- as.integer(nrow(x)) - nc <- as.integer(ncol(x)) - - sr <- rowSums(x) - sc <- colSums(x) - E <- outer(sr, sc, "*") / n - v <- function(r, c, n) c * r * (n - r) * (n - c) / n^3 - V <- outer(sr, sc, v, n) - dimnames(E) <- dimnames(x) - - STATISTIC <- sum((abs(x - E))^2 / E) - PARAMETER <- (nr - 1L) * (nc - 1L) - PVAL <- pchisq(STATISTIC, PARAMETER, lower.tail = FALSE) - - names(STATISTIC) <- "X-squared" - names(PARAMETER) <- "df" - - structure( - list( - statistic = STATISTIC, - parameter = PARAMETER, - p.value = PVAL, - method = "Pearson's Chi-squared test", - observed = x, - expected = E, - residuals = (x - E) / sqrt(E), - stdres = (x - E) / sqrt(V) - ), - class = "htest" - ) -} -``` - -And, indeed, this new version of the custom function performs even better than it previously did: - - -```r -m <- c(rep("a", 1000), rep("b", 9000)) -n <- c(rep(c("x", "y"), 5000)) - -bench::mark( - "base" = chisq.test(m, n)$statistic[[1]], - "custom" = my_chisq_test2(m, n)$statistic[[1]] -)[1:5] -#> # A tibble: 2 Γ— 5 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 base 814Β΅s 1.31ms 618. 1.28MB -#> 2 custom 349Β΅s 483.25Β΅s 1691. 594.45KB -``` - -## Exercises 24.5.1 - -**Q1.** The density functions, e.g., `dnorm()`, have a common interface. Which arguments are vectorised over? What does `rnorm(10, mean = 10:1)` do? - -**A1.** The density function family has the following interface: - - -```r -dnorm(x, mean = 0, sd = 1, log = FALSE) -pnorm(q, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE) -qnorm(p, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE) -rnorm(n, mean = 0, sd = 1) -``` - -Reading the documentation reveals that the following parameters are vectorized: -`x`, `q`, `p`, `mean`, `sd`. - -This means that something like the following will work: - - -```r -rnorm(c(1, 2, 3), mean = c(0, -1, 5)) -#> [1] 1.124335 0.930398 3.844935 -``` - -But, for functions that don't have multiple vectorized parameters, it won't. For example, - - -```r -pnorm(c(1, 2, 3), mean = c(0, -1, 5), log.p = c(FALSE, TRUE, TRUE)) -#> [1] 0.84134475 0.99865010 0.02275013 -``` - -The following function call generates 10 random numbers (since `n = 10`) with 10 different distributions with means supplied by the vector `10:1`. - - -```r -rnorm(n = 10, mean = 10:1) -#> [1] 8.2421770 9.3920474 7.1362118 7.5789906 5.2551688 -#> [6] 6.0143714 4.6147891 1.1096247 2.8759129 -0.6756857 -``` - -**Q2.** Compare the speed of `apply(x, 1, sum)` with `rowSums(x)` for varying sizes of `x`. - -**A2.** We can write a custom function to vary number of rows in a matrix and extract a data frame comparing performance of these two functions. - - -```r -benc_perform <- function(nRow, nCol = 100) { - x <- matrix(data = rnorm(nRow * nCol), nrow = nRow, ncol = nCol) - - bench::mark( - rowSums(x), - apply(x, 1, sum) - )[1:5] -} - -nRowList <- list(10, 100, 500, 1000, 5000, 10000, 50000, 100000) - -names(nRowList) <- as.character(nRowList) - -benchDF <- map_dfr( - .x = nRowList, - .f = ~ benc_perform(.x), - .id = "nRows" -) %>% - mutate(nRows = as.numeric(nRows)) -``` - -Plotting this data reveals that `rowSums(x)` has *O*(1) behavior, while *O*(n) behavior. - - -```r -ggplot( - benchDF, - aes( - x = as.numeric(nRows), - y = median, - group = as.character(expression), - color = as.character(expression) - ) -) + - geom_point() + - geom_line() + - labs( - x = "Number of Rows", - y = "Median Execution Time", - colour = "Function used" - ) -``` - - - -**Q3.** How can you use `crossprod()` to compute a weighted sum? How much faster is it than the naive `sum(x * w)`? - -**A3.** Both of these functions provide a way to compute a weighted sum: - - -```r -x <- c(1:6, 2, 3) -w <- rnorm(length(x)) - -crossprod(x, w)[[1]] -#> [1] 15.94691 -sum(x * w)[[1]] -#> [1] 15.94691 -``` - -But benchmarking their performance reveals that the latter is significantly faster than the former! - - -```r -bench::mark( - crossprod(x, w)[[1]], - sum(x * w)[[1]], - iterations = 1e6 -)[1:5] -#> # A tibble: 2 Γ— 5 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 crossprod(x, w)[[1]] 328ns 533ns 757816. 0B -#> 2 sum(x * w)[[1]] 164ns 287ns 1621856. 0B -``` diff --git a/_book/Perf-improve_files/figure-html/Perf-improve-28-1.png b/_book/Perf-improve_files/figure-html/Perf-improve-28-1.png deleted file mode 100644 index 639b4862..00000000 Binary files a/_book/Perf-improve_files/figure-html/Perf-improve-28-1.png and /dev/null differ diff --git a/_book/Perf-measure.md b/_book/Perf-measure.md deleted file mode 100644 index 5c1a0a43..00000000 --- a/_book/Perf-measure.md +++ /dev/null @@ -1,272 +0,0 @@ -# Measuring performance - - - -Attaching the needed libraries: - - -```r -library(profvis, warn.conflicts = FALSE) -library(dplyr, warn.conflicts = FALSE) -``` - -## Profiling (Exercises 23.2.4) - ---- - -**Q1.** Profile the following function with `torture = TRUE`. What is surprising? Read the source code of `rm()` to figure out what's going on. - - -```r -f <- function(n = 1e5) { - x <- rep(1, n) - rm(x) -} -``` - -**A1.** Let's source the functions mentioned in exercises. - - -```r -source("profiling-exercises.R") -``` - -First, we try without `torture = TRUE`: it returns no meaningful results. - - -```r -profvis(f()) -#> Error in parse_rprof(prof_output, expr_source): No parsing data available. Maybe your function was too fast? -``` - -As mentioned in the docs, setting `torture = TRUE` - -> Triggers garbage collection after every torture memory allocation call. - -This process somehow never seems to finish and crashes the RStudio session when it stops! - - -```r -profvis(f(), torture = TRUE) -``` - -The question says that documentation for `rm()` may provide clues: - - -```r -rm -#> function (..., list = character(), pos = -1, envir = as.environment(pos), -#> inherits = FALSE) -#> { -#> dots <- match.call(expand.dots = FALSE)$... -#> if (length(dots) && !all(vapply(dots, function(x) is.symbol(x) || -#> is.character(x), NA, USE.NAMES = FALSE))) -#> stop("... must contain names or character strings") -#> names <- vapply(dots, as.character, "") -#> if (length(names) == 0L) -#> names <- character() -#> list <- .Primitive("c")(list, names) -#> .Internal(remove(list, envir, inherits)) -#> } -#> -#> -``` - -I still couldn't figure out why. I would recommend checking out the [official answer](https://advanced-r-solutions.rbind.io/measuring-performance.html#profiling). - ---- - -## Microbenchmarking (Exercises 23.3.3) - ---- - -**Q1.** Instead of using `bench::mark()`, you could use the built-in function `system.time()`. But `system.time()` is much less precise, so you'll need to repeat each operation many times with a loop, and then divide to find the average time of each operation, as in the code below. - - -```r -n <- 1e6 -system.time(for (i in 1:n) sqrt(x)) / n -system.time(for (i in 1:n) x^0.5) / n -``` - -How do the estimates from `system.time()` compare to those from `bench::mark()`? Why are they different? - -**A1.** Let's benchmark first using these two approaches: - - -```r -n <- 1e6 -x <- runif(100) - -# bench ------------------- - -bench_df <- bench::mark( - sqrt(x), - x^0.5, - iterations = n, - time_unit = "us" -) - -t_bench_df <- bench_df %>% - select(expression, time) %>% - rowwise() %>% - mutate(bench_mean = mean(unlist(time))) %>% - ungroup() %>% - select(-time) - -# system.time ------------------- - -# garbage collection performed immediately before the timing -t1_systime_gc <- system.time(for (i in 1:n) sqrt(x), gcFirst = TRUE) / n -t2_systime_gc <- system.time(for (i in 1:n) x^0.5, gcFirst = TRUE) / n - -# garbage collection not performed immediately before the timing -t1_systime_nogc <- system.time(for (i in 1:n) sqrt(x), gcFirst = FALSE) / n -t2_systime_nogc <- system.time(for (i in 1:n) x^0.5, gcFirst = FALSE) / n - -t_systime_df <- tibble( - "expression" = bench_df$expression, - "systime_with_gc" = c(t1_systime_gc["elapsed"], t2_systime_gc["elapsed"]), - "systime_with_nogc" = c(t1_systime_nogc["elapsed"], t2_systime_nogc["elapsed"]) -) %>% - mutate( - systime_with_gc = systime_with_gc * 1e6, # in microseconds - systime_with_nogc = systime_with_nogc * 1e6 # in microseconds - ) -``` - -Now we can compare results from these alternatives: - - -```r -# note that system time columns report time in microseconds -full_join(t_bench_df, t_systime_df, by = "expression") -#> # A tibble: 2 Γ— 4 -#> expression bench_mean systime_with_gc systime_with_nogc -#> -#> 1 sqrt(x) 767.29ns 0.632 0.665 -#> 2 x^0.5 2.45Β΅s 2.27 2.33 -``` - -The comparison reveals that these two approaches yield quite similar results. Slight differences in exact values is possibly due to differences in the precision of timers used internally by these functions. - ---- - -**Q2.** Here are two other ways to compute the square root of a vector. Which do you think will be fastest? Which will be slowest? Use microbenchmarking to test your answers. - - -```r -x^(1 / 2) -exp(log(x) / 2) -``` - ---- - -**A2.** Microbenchmarking all ways to compute square root of a vector mentioned in this chapter. - - -```r -x <- runif(1000) - -bench::mark( - sqrt(x), - x^0.5, - x^(1 / 2), - exp(log(x) / 2), - iterations = 1000 -) %>% - select(expression, median) %>% - arrange(median) -#> # A tibble: 4 Γ— 2 -#> expression median -#> -#> 1 sqrt(x) 2.79Β΅s -#> 2 exp(log(x)/2) 9.51Β΅s -#> 3 x^(1/2) 17.9Β΅s -#> 4 x^0.5 18.53Β΅s -``` - -The specialized primitive function `sqrt()` (written in `C`) is the fastest way to compute square root. - ---- - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bench 1.1.2 2021-11-30 [1] CRAN (R 4.2.0) -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388) -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> htmlwidgets 1.5.4 2021-09-08 [1] CRAN (R 4.2.0) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> profmem 0.6.0 2020-12-13 [1] CRAN (R 4.2.0) -#> profvis * 0.3.7 2020-11-02 [1] CRAN (R 4.2.0) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble 3.1.8.9002 2022-10-16 [1] local -#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Quotation.md b/_book/Quotation.md deleted file mode 100644 index 762b1904..00000000 --- a/_book/Quotation.md +++ /dev/null @@ -1,903 +0,0 @@ -# Quasiquotation - - - -Attaching the needed libraries: - - -```r -library(rlang) -library(purrr) -library(lobstr) -library(dplyr) -library(ggplot2) -``` - -## Motivation (Exercises 19.2.2) - ---- - -**Q1.** For each function in the following base R code, identify which arguments are quoted and which are evaluated. - - -```r -library(MASS) - -mtcars2 <- subset(mtcars, cyl == 4) - -with(mtcars2, sum(vs)) -sum(mtcars2$am) - -rm(mtcars2) -``` - -**A1.** To identify which arguments are quoted and which are evaluated, we can use the trick mentioned in the book: - -> If you’re ever unsure about whether an argument is quoted or evaluated, try executing the code outside of the function. If it doesn’t work or does something different, then that argument is quoted. - -- `library(MASS)` - -The `package` argument in `library()` is quoted: - - -```r -library(MASS) - -MASS -#> Error in eval(expr, envir, enclos): object 'MASS' not found -``` - -- `subset(mtcars, cyl == 4)` - -The argument `x` is evaluated, while the argument `subset` is quoted. - - -```r -mtcars2 <- subset(mtcars, cyl == 4) - -invisible(mtcars) - -cyl == 4 -#> Error in eval(expr, envir, enclos): object 'cyl' not found -``` - -- `with(mtcars2, sum(vs))` - -The argument `data` is evaluated, while `expr` argument is quoted. - - -```r -with(mtcars2, sum(vs)) -#> [1] 10 - -invisible(mtcars2) - -sum(vs) -#> Error in eval(expr, envir, enclos): object 'vs' not found -``` - -- `sum(mtcars2$am)` - -The argument `...` is evaluated. - - -```r -sum(mtcars2$am) -#> [1] 8 - -mtcars2$am -#> [1] 1 0 0 1 1 1 0 1 1 1 1 -``` - -- `rm(mtcars2)` - -The trick we are using so far won't work here since trying to print `mtcars2` will always fail after `rm()` has made a pass at it. - - -```r -rm(mtcars2) -``` - -We can instead look at the docs for `...`: - -> ... the objects to be removed, as names (unquoted) or character strings (quoted). - -Thus, this argument is not evaluated, but rather quoted. - ---- - -**Q2.** For each function in the following tidyverse code, identify which arguments are quoted and which are evaluated. - - -```r -library(dplyr) -library(ggplot2) - -by_cyl <- mtcars %>% - group_by(cyl) %>% - summarise(mean = mean(mpg)) - -ggplot(by_cyl, aes(cyl, mean)) + - geom_point() -``` - -**A2.** As seen in the answer for **Q1.**, `library()` quotes its first argument: - - -```r -library(dplyr) -library(ggplot2) -``` - -In the following code: - -- `%>%` (lazily) evaluates its argument -- `group_by()` and `summarise()` quote their arguments - - -```r -by_cyl <- mtcars %>% - group_by(cyl) %>% - summarise(mean = mean(mpg)) -``` - -In the following code: - -- `ggplot()` evaluates the `data` argument -- `aes()` quotes its arguments - - -```r -ggplot(by_cyl, aes(cyl, mean)) + - geom_point() -``` - - - ---- - -## Quoting (Exercises 19.3.6) - ---- - -**Q1.** How is `expr()` implemented? Look at its source code. - -**A1.** Looking at the source code, we can see that `expr()` is a simple wrapper around `enexpr()`, and captures and returns the user-entered expressions: - - -```r -rlang::expr -#> function (expr) -#> { -#> enexpr(expr) -#> } -#> -#> -``` - -For example: - - -```r -x <- expr(x <- 1) -x -#> x <- 1 -``` - -In its turn, `enexpr()` calls native code: - - -```r -rlang::enexpr -#> function (arg) -#> { -#> .Call(ffi_enexpr, substitute(arg), parent.frame()) -#> } -#> -#> -``` - - ---- - -**Q2.** Compare and contrast the following two functions. Can you predict the output before running them? - - -```r -f1 <- function(x, y) { - exprs(x = x, y = y) -} -f2 <- function(x, y) { - enexprs(x = x, y = y) -} -f1(a + b, c + d) -f2(a + b, c + d) -``` - -**A2.** The `exprs()` captures and returns the expressions specified by the developer instead of their values: - - -```r -f1 <- function(x, y) { - exprs(x = x, y = y) -} - -f1(a + b, c + d) -#> $x -#> x -#> -#> $y -#> y -``` - -On the other hand, `enexprs()` captures the user-entered expressions and returns their values: - - -```r -f2 <- function(x, y) { - enexprs(x = x, y = y) -} - -f2(a + b, c + d) -#> $x -#> a + b -#> -#> $y -#> c + d -``` - ---- - -**Q3.** What happens if you try to use `enexpr()` with an expression (i.e. `enexpr(x + y)`? What happens if `enexpr()` is passed a missing argument? - -**A3.** If you try to use `enexpr()` with an expression, it fails because it works only with `symbol`. - - -```r -enexpr(x + y) -#> Error in `enexpr()`: -#> ! `arg` must be a symbol -``` - -If `enexpr()` is passed a missing argument, it returns a missing argument: - - -```r -arg <- missing_arg() - -enexpr(arg) - -is_missing(enexpr(arg)) -#> [1] TRUE -``` - ---- - -**Q4.** How are `exprs(a)` and `exprs(a = )` different? Think about both the input and the output. - -**A4.** The key difference between `exprs(a)` and `exprs(a = )` is that the former will return an unnamed list, while the latter will return a named list. This is because the former is interpreted as an unnamed argument, while the latter a named argument. - - -```r -exprs(a) -#> [[1]] -#> a - -exprs(a = ) -#> $a -``` - -In both cases, `a` is treated as a symbol: - - -```r -map_lgl(exprs(a), is_symbol) -#> -#> TRUE - -map_lgl(exprs(a = ), is_symbol) -#> a -#> TRUE -``` - -But, the argument is missing only in the latter case, since only the name but no corresponding value is provided: - - -```r -map_lgl(exprs(a), is_missing) -#> -#> FALSE - -map_lgl(exprs(a = ), is_missing) -#> a -#> TRUE -``` - ---- - -**Q5.** What are other differences between `exprs()` and `alist()`? Read the documentation for the named arguments of `exprs()` to find out. - -**A5.** Here are some additional differences between `exprs()` and `alist()`. - -- Names: If the inputs are not named, `exprs()` provides a way to name them automatically using `.named` argument. - - -```r -alist("x" = 1, TRUE, "z" = expr(x + y)) -#> $x -#> [1] 1 -#> -#> [[2]] -#> [1] TRUE -#> -#> $z -#> expr(x + y) - -exprs("x" = 1, TRUE, "z" = expr(x + y), .named = TRUE) -#> $x -#> [1] 1 -#> -#> $`TRUE` -#> [1] TRUE -#> -#> $z -#> expr(x + y) -``` - -- Ignoring empty arguments: The `.ignore_empty` argument in `exprs()` gives you a much finer control over what to do with the empty arguments, while `alist()` doesn't provide a way to ignore such arguments. - - -```r -alist("x" = 1, , TRUE, ) -#> $x -#> [1] 1 -#> -#> [[2]] -#> -#> -#> [[3]] -#> [1] TRUE -#> -#> [[4]] - -exprs("x" = 1, , TRUE, , .ignore_empty = "trailing") -#> $x -#> [1] 1 -#> -#> [[2]] -#> -#> -#> [[3]] -#> [1] TRUE - -exprs("x" = 1, , TRUE, , .ignore_empty = "none") -#> $x -#> [1] 1 -#> -#> [[2]] -#> -#> -#> [[3]] -#> [1] TRUE -#> -#> [[4]] - -exprs("x" = 1, , TRUE, , .ignore_empty = "all") -#> $x -#> [1] 1 -#> -#> [[2]] -#> [1] TRUE -``` - -- Names injection: Using `.unquote_names` argument in `exprs()`, we can inject a name for the argument. - - -```r -alist(foo := bar) -#> [[1]] -#> `:=`(foo, bar) - -exprs(foo := bar, .unquote_names = FALSE) -#> [[1]] -#> `:=`(foo, bar) - -exprs(foo := bar, .unquote_names = TRUE) -#> $foo -#> bar -``` - ---- - -**Q6.** The documentation for `substitute()` says: - -> Substitution takes place by examining each component of the parse tree -> as follows: -> -> * If it is not a bound symbol in `env`, it is unchanged. -> * If it is a promise object (i.e., a formal argument to a function) -> the expression slot of the promise replaces the symbol. -> * If it is an ordinary variable, its value is substituted, unless -> `env` is .GlobalEnv in which case the symbol is left unchanged. - -Create examples that illustrate each of the above cases. - -**A6.** See below examples that illustrate each of the above-mentioned cases. - -> If it is not a bound symbol in `env`, it is unchanged. - -Symbol `x` is not bound in `env`, so it remains unchanged. - - -```r -substitute(x + y, env = list(y = 2)) -#> x + 2 -``` - -> If it is a promise object (i.e., a formal argument to a function) -> the expression slot of the promise replaces the symbol. - - -```r -msg <- "old" -delayedAssign("myVar", msg) # creates a promise -substitute(myVar) -#> myVar -msg <- "new!" -myVar -#> [1] "new!" -``` - -> If it is an ordinary variable, its value is substituted, unless -> `env` is .GlobalEnv in which case the symbol is left unchanged. - - -```r -substitute(x + y, env = env(x = 2, y = 1)) -#> 2 + 1 - -x <- 2 -y <- 1 -substitute(x + y, env = .GlobalEnv) -#> x + y -``` - ---- - -## Unquoting (Exercises 19.4.8) - ---- - -**Q1.** Given the following components: - - -```r -xy <- expr(x + y) -xz <- expr(x + z) -yz <- expr(y + z) -abc <- exprs(a, b, c) -``` - -Use quasiquotation to construct the following calls: - - -```r -(x + y) / (y + z) --(x + z)^(y + z) -(x + y) + (y + z) - (x + y) -atan2(x + y, y + z) -sum(x + y, x + y, y + z) -sum(a, b, c) -mean(c(a, b, c), na.rm = TRUE) -foo(a = x + y, b = y + z) -``` - -**A1.** Using quasiquotation to construct the specified calls: - - -```r -xy <- expr(x + y) -xz <- expr(x + z) -yz <- expr(y + z) -abc <- exprs(a, b, c) - -expr((!!xy) / (!!yz)) -#> (x + y)/(y + z) - -expr(-(!!xz)^(!!yz)) -#> -(x + z)^(y + z) - -expr(((!!xy)) + (!!yz) - (!!xy)) -#> (x + y) + (y + z) - (x + y) - -call2("atan2", expr(!!xy), expr(!!yz)) -#> atan2(x + y, y + z) - -call2("sum", expr(!!xy), expr(!!xy), expr(!!yz)) -#> sum(x + y, x + y, y + z) - -call2("sum", !!!abc) -#> sum(a, b, c) - -expr(mean(c(!!!abc), na.rm = TRUE)) -#> mean(c(a, b, c), na.rm = TRUE) - -call2("foo", a = expr(!!xy), b = expr(!!yz)) -#> foo(a = x + y, b = y + z) -``` - ---- - -**Q2.** The following two calls print the same, but are actually different: - - -```r -(a <- expr(mean(1:10))) -#> mean(1:10) -(b <- expr(mean(!!(1:10)))) -#> mean(1:10) -identical(a, b) -#> [1] FALSE -``` - -What's the difference? Which one is more natural? - -**A2.** We can see the difference between these two expression if we convert them to lists: - - -```r -as.list(expr(mean(1:10))) -#> [[1]] -#> mean -#> -#> [[2]] -#> 1:10 - -as.list(expr(mean(!!(1:10)))) -#> [[1]] -#> mean -#> -#> [[2]] -#> [1] 1 2 3 4 5 6 7 8 9 10 -``` - -As can be seen, the second element of `a` is a `call` object, while that in `b` is an integer vector: - - -```r -waldo::compare(a, b) -#> `old[[2]]` is a call -#> `new[[2]]` is an integer vector (1, 2, 3, 4, 5, ...) -``` - -The same can also be noticed in ASTs for these expressions: - - -```r -ast(expr(mean(1:10))) -#> β–ˆβ”€expr -#> β””β”€β–ˆβ”€mean -#> β””β”€β–ˆβ”€`:` -#> β”œβ”€1 -#> └─10 - -ast(expr(mean(!!(1:10)))) -#> β–ˆβ”€expr -#> β””β”€β–ˆβ”€mean -#> └─ -``` - -The first call is more natural, since the second one inlines a vector directly into the call, something that is rarely done. - ---- - -## `...` (dot-dot-dot) (Exercises 19.6.5) - ---- - -**Q1.** One way to implement `exec()` is shown below. Describe how it works. What are the key ideas? - - -```r -exec <- function(f, ..., .env = caller_env()) { - args <- list2(...) - do.call(f, args, envir = .env) -} -``` - -**A1.** The keys ideas that underlie this implementation of `exec()` function are the following: - -- It constructs a call using function `f` and its argument `...`, and evaluates the call in the environment `.env`. - -- It uses [dynamic dots](https://rlang.r-lib.org/reference/dyn-dots.html) via `list2()`, which means that you can splice arguments using `!!!`, you can inject names using `:=`, and trailing commas are not a problem. - -Here is an example: - - -```r -vec <- c(1:5, NA) -args_list <- list(trim = 0, na.rm = TRUE) - -exec(mean, vec, !!!args_list, , .env = caller_env()) -#> [1] 3 - -rm("exec") -``` - ---- - -**Q2.** Carefully read the source code for `interaction()`, `expand.grid()`, and `par()`. Compare and contrast the techniques they use for switching between dots and list behaviour. - -**A2.** Source code reveals the following comparison table: - -| Function | Capture the dots | Handle list input | -| :-------------- | :------------------ | :------------------------------------------------------------------- | -| `interaction()` | `args <- list(...)` | `length(args) == 1L && is.list(args[[1L]])` | -| `expand.grid()` | `args <- list(...)` | `length(args) == 1L && is.list(args[[1L]])` | -| `par()` | `args <- list(...)` | `length(args) == 1L && (is.list(args[[1L]] || is.null(args[[1L]])))` | - -All functions capture the dots in a list. - -Using these dots, the functions check: - - - if a list was entered as an argument by checking the number of arguments - - if the count is 1, by checking if the argument is a list - ---- - -**Q3.** Explain the problem with this definition of `set_attr()` - - -```r -set_attr <- function(x, ...) { - attr <- rlang::list2(...) - attributes(x) <- attr - x -} -set_attr(1:10, x = 10) -#> Error in attributes(x) <- attr: attributes must be named -``` - -**A3.** The `set_attr()` function signature has a parameter called `x`, and additionally it uses dynamic dots to pass multiple arguments to specify additional attributes for `x`. - -But, as shown in the example, this creates a problem when the attribute is itself named `x`. Naming the arguments won't help either: - - -```r -set_attr <- function(x, ...) { - attr <- rlang::list2(...) - attributes(x) <- attr - x -} -set_attr(x = 1:10, x = 10) -#> Error in set_attr(x = 1:10, x = 10): formal argument "x" matched by multiple actual arguments -``` - -We can avoid these issues by renaming the parameter: - - -```r -set_attr <- function(.x, ...) { - attr <- rlang::list2(...) - attributes(.x) <- attr - .x -} - -set_attr(.x = 1:10, x = 10) -#> [1] 1 2 3 4 5 6 7 8 9 10 -#> attr(,"x") -#> [1] 10 -``` - ---- - -## Case studies (Exercises 19.7.5) - ---- - -**Q1.** In the linear-model example, we could replace the `expr()` in `reduce(summands, ~ expr(!!.x + !!.y))` with `call2()`: `reduce(summands, call2, "+")`. Compare and contrast the two approaches. Which do you think is easier to read? - -**A1.** We can rewrite the `linear()` function from this chapter using `call2()` as follows: - - -```r -linear <- function(var, val) { - var <- ensym(var) - coef_name <- map(seq_along(val[-1]), ~ expr((!!var)[[!!.x]])) - - summands <- map2(val[-1], coef_name, ~ expr((!!.x * !!.y))) - summands <- c(val[[1]], summands) - - reduce(summands, ~ call2("+", .x, .y)) -} - -linear(x, c(10, 5, -4)) -#> 10 + (5 * x[[1L]]) + (-4 * x[[2L]]) -``` - -I personally find the version with `call2()` to be much more readable since the `!!` syntax is a bit esoteric. - ---- - -**Q2.** Re-implement the Box-Cox transform defined below using unquoting and `new_function()`: - - -```r -bc <- function(lambda) { - if (lambda == 0) { - function(x) log(x) - } else { - function(x) (x^lambda - 1) / lambda - } -} -``` - -**A2.** Re-implementation of the Box-Cox transform using unquoting and `new_function()`: - - -```r -bc_new <- function(lambda) { - lambda <- enexpr(lambda) - - if (!!lambda == 0) { - new_function( - exprs(x = ), - expr(log(x)) - ) - } else { - new_function( - exprs(x = ), - expr((x^(!!lambda) - 1) / (!!lambda)) - ) - } -} -``` - -Let's try it out to see if it produces the same output as before: - - -```r -bc(0)(1) -#> [1] 0 -bc_new(0)(1) -#> [1] 0 - -bc(2)(2) -#> [1] 1.5 -bc_new(2)(2) -#> [1] 1.5 -``` - ---- - -**Q3.** Re-implement the simple `compose()` defined below using quasiquotation and `new_function()`: - - -```r -compose <- function(f, g) { - function(...) f(g(...)) -} -``` - -**A3.** Following is a re-implementation of `compose()` using quasiquotation and `new_function()`: - - -```r -compose_new <- function(f, g) { - f <- enexpr(f) # or ensym(f) - g <- enexpr(g) # or ensym(g) - - new_function( - exprs(... = ), - expr((!!f)((!!g)(...))) - ) -} -``` - -Checking that the new version behaves the same way as the original version: - - -```r -not_null <- compose(`!`, is.null) -not_null(4) -#> [1] TRUE - -not_null2 <- compose_new(`!`, is.null) -not_null2(4) -#> [1] TRUE -``` - ---- - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1) -#> P datasets * 4.2.2 2022-10-31 [1] local -#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388) -#> diffobj 0.3.5 2021-10-05 [1] CRAN (R 4.2.0) -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> farver 2.1.1 2022-07-06 [1] CRAN (R 4.2.1) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.2) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> P grid 4.2.2 2022-10-31 [1] local -#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.1) -#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0) -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> labeling 0.4.2 2020-10-20 [1] CRAN (R 4.2.0) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> lobstr * 1.1.2 2022-06-22 [1] CRAN (R 4.2.0) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> MASS * 7.3-58.1 2022-08-03 [1] CRAN (R 4.2.2) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0) -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rematch2 2.1.2 2020-05-01 [1] CRAN (R 4.2.0) -#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble 3.1.8.9002 2022-10-16 [1] local -#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> waldo 0.4.0 2022-03-16 [1] CRAN (R 4.2.0) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Quotation_files/figure-html/Quotation-12-1.png b/_book/Quotation_files/figure-html/Quotation-12-1.png deleted file mode 100644 index e0176d05..00000000 Binary files a/_book/Quotation_files/figure-html/Quotation-12-1.png and /dev/null differ diff --git a/_book/R6.md b/_book/R6.md deleted file mode 100644 index 2ffb99ca..00000000 --- a/_book/R6.md +++ /dev/null @@ -1,633 +0,0 @@ -# R6 - - - -Loading the needed libraries: - - -```r -library(R6) -``` - -## Classes and methods (Exercises 14.2.6) - -**Q1.** Create a bank account R6 class that stores a balance and allows you to deposit and withdraw money. Create a subclass that throws an error if you attempt to go into overdraft. Create another subclass that allows you to go into overdraft, but charges you a fee. Create the superclass and make sure it works as expected. - -**A1.** First, let's create a bank account R6 class that stores a balance and allows you to deposit and withdraw money: - - -```r -library(R6) - -bankAccount <- R6::R6Class( - "bankAccount", - public = list( - balance = 0, - initialize = function(balance) { - self$balance <- balance - }, - deposit = function(amount) { - self$balance <- self$balance + amount - message(paste0("Current balance is: ", self$balance)) - invisible(self) - }, - withdraw = function(amount) { - self$balance <- self$balance - amount - message(paste0("Current balance is: ", self$balance)) - invisible(self) - } - ) -) -``` - -Let's try it out: - - -```r -indra <- bankAccount$new(balance = 100) - -indra$deposit(20) -#> Current balance is: 120 - -indra$withdraw(10) -#> Current balance is: 110 -``` - -Create a subclass that errors if you attempt to overdraw: - - -```r -bankAccountStrict <- R6::R6Class( - "bankAccountStrict", - inherit = bankAccount, - public = list( - withdraw = function(amount) { - if (self$balance - amount < 0) { - stop( - paste0("Can't withdraw more than your current balance: ", self$balance), - call. = FALSE - ) - } - - super$withdraw(amount) - } - ) -) -``` - -Let's try it out: - - -```r -Pritesh <- bankAccountStrict$new(balance = 100) - -Pritesh$deposit(20) -#> Current balance is: 120 - -Pritesh$withdraw(150) -#> Error: Can't withdraw more than your current balance: 120 -``` - -Now let's create a subclass that charges a fee if account is overdrawn: - - -```r -bankAccountFee <- R6::R6Class( - "bankAccountFee", - inherit = bankAccount, - public = list( - withdraw = function(amount) { - super$withdraw(amount) - - if (self$balance) { - self$balance <- self$balance - 10 - message("You're withdrawing more than your current balance. You will be charged a fee of 10 euros.") - } - } - ) -) -``` - -Let's try it out: - - -```r -Mangesh <- bankAccountFee$new(balance = 100) - -Mangesh$deposit(20) -#> Current balance is: 120 - -Mangesh$withdraw(150) -#> Current balance is: -30 -#> You're withdrawing more than your current balance. You will be charged a fee of 10 euros. -``` - -**Q2.** Create an R6 class that represents a shuffled deck of cards. You should be able to draw cards from the deck with `$draw(n)`, and return all cards to the deck and reshuffle with `$reshuffle()`. Use the following code to make a vector of cards. - - -```r -suit <- c("β™ ", "β™₯", "♦", "♣") -value <- c("A", 2:10, "J", "Q", "K") -cards <- paste0(rep(value, 4), suit) -``` - -**A2.** Let's create needed class that represents a shuffled deck of cards: - - -```r -suit <- c("β™ ", "β™₯", "♦", "♣") -value <- c("A", 2:10, "J", "Q", "K") -cards <- paste(rep(value, 4), suit) - -Deck <- R6::R6Class( - "Deck", - public = list( - initialize = function(deck) { - private$cards <- sample(deck) - }, - draw = function(n) { - if (n > length(private$cards)) { - stop( - paste0("Can't draw more than remaining number of cards: ", length(private$cards)), - call. = FALSE - ) - } - - drawn_cards <- sample(private$cards, n) - private$cards <- private$cards[-which(private$cards %in% drawn_cards)] - message(paste0("Remaining number of cards: ", length(private$cards))) - - return(drawn_cards) - }, - reshuffle = function() { - private$cards <- sample(private$cards) - invisible(self) - } - ), - private = list( - cards = NULL - ) -) -``` - -Let's try it out: - - -```r -myDeck <- Deck$new(cards) - -myDeck$draw(4) -#> Remaining number of cards: 48 -#> [1] "2 β™ " "10 ♦" "9 ♦" "3 ♦" - -myDeck$reshuffle()$draw(5) -#> Remaining number of cards: 43 -#> [1] "6 ♦" "10 β™₯" "2 β™₯" "A β™₯" "8 β™₯" - -myDeck$draw(50) -#> Error: Can't draw more than remaining number of cards: 43 -``` - -**Q3.** Why can't you model a bank account or a deck of cards with an S3 class? - -**A3.** We can't model a bank account or a deck of cards with an `S3` class because instances of these classes are *immutable*. - -On the other hand, `R6` classes encapsulate data and represent its *state*, which can change over the course of object's lifecycle. In other words, these objects are *mutable* and well-suited to model a bank account. - -**Q4.** Create an R6 class that allows you to get and set the current time zone. You can access the current time zone with `Sys.timezone()` and set it with `Sys.setenv(TZ = "newtimezone")`. When setting the time zone, make sure the new time zone is in the list provided by `OlsonNames()`. - -**A4.** Here is an `R6` class that manages the current time zone: - - -```r -CurrentTimeZone <- R6::R6Class("CurrentTimeZone", - public = list( - setTimeZone = function(tz) { - stopifnot(tz %in% OlsonNames()) - Sys.setenv(TZ = tz) - }, - getTimeZone = function() { - Sys.timezone() - } - ) -) -``` - -Let's try it out: - - -```r -myCurrentTimeZone <- CurrentTimeZone$new() - -myCurrentTimeZone$getTimeZone() -#> [1] "Europe/Berlin" - -myCurrentTimeZone$setTimeZone("Asia/Kolkata") -myCurrentTimeZone$getTimeZone() -#> [1] "Europe/Berlin" - -myCurrentTimeZone$setTimeZone("Europe/Berlin") -``` - -**Q5.** Create an R6 class that manages the current working directory. It should have `$get()` and `$set()` methods. - -**A5.** Here is an `R6` class that manages the current working directory: - - -```r -ManageDirectory <- R6::R6Class("ManageDirectory", - public = list( - setWorkingDirectory = function(dir) { - setwd(dir) - }, - getWorkingDirectory = function() { - getwd() - } - ) -) -``` - -Let's create an instance of this class and check if the methods work as expected: - - -```r -myDirManager <- ManageDirectory$new() - -# current working directory -myDirManager$getWorkingDirectory() - -# change and check if that worked -myDirManager$setWorkingDirectory("..") -myDirManager$getWorkingDirectory() - -# revert this change -myDirManager$setWorkingDirectory("/Advanced-R-exercises") -``` - -**Q6.** Why can't you model the time zone or current working directory with an S3 class? - -**A6.** Same as answer to **Q3**: - -Objects that represent these real-life entities need to be mutable and `S3` class instances are not mutable. - -**Q7.** What base type are R6 objects built on top of? What attributes do they have? - -**A7.** Let's create an example class and create instance of that class: - - -```r -Example <- R6::R6Class("Example") -myExample <- Example$new() -``` - -The `R6` objects are built on top of environment: - - -```r -typeof(myExample) -#> [1] "environment" - -rlang::env_print(myExample) -#> [L] -#> Parent: -#> Class: Example, R6 -#> Bindings: -#> β€’ .__enclos_env__: -#> β€’ clone: [L] -``` - -And it has only `class` attribute, which is a character vector with the `"R6"` being the last element and the superclasses being other elements: - - -```r -attributes(myExample) -#> $class -#> [1] "Example" "R6" -``` - -## Controlling access (Exercises 14.3.3) - -**Q1.** Create a bank account class that prevents you from directly setting the account balance, but you can still withdraw from and deposit to. Throw an error if you attempt to go into overdraft. - -**A1.** Here is a bank account class that satisfies the specified requirements: - - -```r -SafeBankAccount <- R6::R6Class( - classname = "SafeBankAccount", - public = list( - deposit = function(deposit_amount) { - private$.balance <- private$.balance + deposit_amount - print(paste("Current balance:", private$.balance)) - - invisible(self) - }, - withdraw = function(withdrawal_amount) { - if (withdrawal_amount > private$.balance) { - stop("You can't withdraw more than your current balance.", call. = FALSE) - } - - private$.balance <- private$.balance - withdrawal_amount - print(paste("Current balance:", private$.balance)) - - invisible(self) - } - ), - private = list( - .balance = 0 - ) -) -``` - -Let's check if it works as expected: - - -```r -mySafeBankAccount <- SafeBankAccount$new() - -mySafeBankAccount$deposit(100) -#> [1] "Current balance: 100" - -mySafeBankAccount$withdraw(50) -#> [1] "Current balance: 50" - -mySafeBankAccount$withdraw(100) -#> Error: You can't withdraw more than your current balance. -``` - -**Q2.** Create a class with a write-only `$password` field. It should have `$check_password(password)` method that returns `TRUE` or `FALSE`, but there should be no way to view the complete password. - -**A2.** Here is an implementation of the class with the needed properties: - - -```r -library(R6) - -checkCredentials <- R6Class( - "checkCredentials", - public = list( - # setter - set_password = function(password) { - private$.password <- password - }, - - # checker - check_password = function(password) { - if (is.null(private$.password)) { - stop("No password set to check against.") - } - - identical(password, private$.password) - }, - - # the default print method prints the private fields as well - print = function() { - cat("Password: XXXX") - - # for method chaining - invisible(self) - } - ), - private = list( - .password = NULL - ) -) - -myCheck <- checkCredentials$new() - -myCheck$set_password("1234") -print(myCheck) -#> Password: XXXX - -myCheck$check_password("abcd") -#> [1] FALSE -myCheck$check_password("1234") -#> [1] TRUE -``` - -But, of course, everything is possible: - - -```r -myCheck$.__enclos_env__$private$.password -#> [1] "1234" -``` - -**Q3.** Extend the `Rando` class with another active binding that allows you to access the previous random value. Ensure that active binding is the only way to access the value. - -**A3.** Here is a modified version of the `Rando` class to meet the specified requirements: - - -```r -Rando <- R6::R6Class("Rando", - active = list( - random = function(value) { - if (missing(value)) { - newValue <- runif(1) - private$.previousRandom <- private$.currentRandom - private$.currentRandom <- newValue - return(private$.currentRandom) - } else { - stop("Can't set `$random`", call. = FALSE) - } - }, - previousRandom = function(value) { - if (missing(value)) { - if (is.null(private$.previousRandom)) { - message("No random value has been generated yet.") - } else { - return(private$.previousRandom) - } - } else { - stop("Can't set `$previousRandom`", call. = FALSE) - } - } - ), - private = list( - .currentRandom = NULL, - .previousRandom = NULL - ) -) -``` - -Let's try it out: - - -```r -myRando <- Rando$new() - -# first time -myRando$random -#> [1] 0.5549124 -myRando$previousRandom -#> No random value has been generated yet. -#> NULL - -# second time -myRando$random -#> [1] 0.3482785 -myRando$previousRandom -#> [1] 0.5549124 - -# third time -myRando$random -#> [1] 0.2187275 -myRando$previousRandom -#> [1] 0.3482785 -``` - -**Q4.** Can subclasses access private fields/methods from their parent? Perform an experiment to find out. - -**A4.** Unlike common OOP in other languages (e.g. C++), R6 subclasses (or derived classes) also have access to the private methods in superclass (or base class). - -For instance, in the following example, the `Duck` class has a private method `$quack()`, but its subclass `Mallard` can access it using `super$quack()`. - - -```r -Duck <- R6Class("Duck", - private = list(quack = function() print("Quack Quack")) -) - -Mallard <- R6Class("Mallard", - inherit = Duck, - public = list(quack = function() super$quack()) -) - -myMallard <- Mallard$new() -myMallard$quack() -#> [1] "Quack Quack" -``` - -## Reference semantics (Exercises 14.4.4) - -**Q1.** Create a class that allows you to write a line to a specified file. You should open a connection to the file in `$initialize()`, append a line using `cat()` in `$append_line()`, and close the connection in `$finalize()`. - -**A1.** Here is a class that allows you to write a line to a specified file: - - -```r -fileEditor <- R6Class( - "fileEditor", - public = list( - initialize = function(filePath) { - private$.connection <- file(filePath, open = "wt") - }, - append_line = function(text) { - cat( - text, - file = private$.connection, - sep = "\n", - append = TRUE - ) - } - ), - private = list( - .connection = NULL, - # according to R6 docs, the destructor method should be private - finalize = function() { - print("Closing the file connection!") - close(private$.connection) - } - ) -) -``` - -Let's check if it works as expected: - - -```r -greetMom <- function() { - f <- tempfile() - myfileEditor <- fileEditor$new(f) - - readLines(f) - - myfileEditor$append_line("Hi mom!") - myfileEditor$append_line("It's a beautiful day!") - - readLines(f) -} - -greetMom() -#> [1] "Hi mom!" "It's a beautiful day!" - -# force garbage collection -gc() -#> [1] "Closing the file connection!" -#> used (Mb) gc trigger (Mb) limit (Mb) max used -#> Ncells 768546 41.1 1395468 74.6 NA 1395468 -#> Vcells 1407126 10.8 8388608 64.0 16384 2601527 -#> (Mb) -#> Ncells 74.6 -#> Vcells 19.9 -``` - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> R6 * 2.5.1.9000 2022-10-27 [1] local -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Rcpp.md b/_book/Rcpp.md deleted file mode 100644 index 12b3f09c..00000000 --- a/_book/Rcpp.md +++ /dev/null @@ -1,1163 +0,0 @@ -# Rewriting R code in C++ - - - - -```r -library(Rcpp, warn.conflicts = FALSE) -``` - -## Getting started with C++ (Exercises 25.2.6) - -**Q1.** With the basics of C++ in hand, it's now a great time to practice by reading and writing some simple C++ functions. For each of the following functions, read the code and figure out what the corresponding base R function is. You might not understand every part of the code yet, but you should be able to figure out the basics of what the function does. - - -```cpp -#include -using namespace Rcpp; - -// [[Rcpp::export]] -double f1(NumericVector x) { - int n = x.size(); - double y = 0; - - for(int i = 0; i < n; ++i) { - y += x[i] / n; - } - return y; -} - -// [[Rcpp::export]] -NumericVector f2(NumericVector x) { - int n = x.size(); - NumericVector out(n); - - out[0] = x[0]; - for(int i = 1; i < n; ++i) { - out[i] = out[i - 1] + x[i]; - } - return out; -} - -// [[Rcpp::export]] -bool f3(LogicalVector x) { - int n = x.size(); - - for(int i = 0; i < n; ++i) { - if (x[i]) return true; - } - return false; -} - -// [[Rcpp::export]] -int f4(Function pred, List x) { - int n = x.size(); - - for(int i = 0; i < n; ++i) { - LogicalVector res = pred(x[i]); - if (res[0]) return i + 1; - } - return 0; -} - -// [[Rcpp::export]] -NumericVector f5(NumericVector x, NumericVector y) { - int n = std::max(x.size(), y.size()); - NumericVector x1 = rep_len(x, n); - NumericVector y1 = rep_len(y, n); - - NumericVector out(n); - - for (int i = 0; i < n; ++i) { - out[i] = std::min(x1[i], y1[i]); - } - - return out; -} -``` - -**A1.** - -`f1()` is the same as `mean()`: - - -```r -x <- c(1, 2, 3, 4, 5, 6) - -f1(x) -#> [1] 3.5 -mean(x) -#> [1] 3.5 -``` - -`f2()` is the same as `cumsum()`: - - -```r -x <- c(1, 3, 5, 6) - -f2(x) -#> [1] 1 4 9 15 -cumsum(x) -#> [1] 1 4 9 15 -``` - -`f3()` is the same as `any()`: - - -```r -x1 <- c(TRUE, FALSE, FALSE, TRUE) -x2 <- c(FALSE, FALSE) - -f3(x1) -#> [1] TRUE -any(x1) -#> [1] TRUE - -f3(x2) -#> [1] FALSE -any(x2) -#> [1] FALSE -``` - -`f4()` is the same as `Position()`: - - -```r -x <- list("a", TRUE, "m", 2) - -f4(is.numeric, x) -#> [1] 4 -Position(is.numeric, x) -#> [1] 4 -``` - -`f5()` is the same as `pmin()`: - - -```r -v1 <- c(1, 3, 4, 5, 6, 7) -v2 <- c(1, 2, 7, 2, 8, 1) - -f5(v1, v2) -#> [1] 1 2 4 2 6 1 -pmin(v1, v2) -#> [1] 1 2 4 2 6 1 -``` - -**Q2.** To practice your function writing skills, convert the following functions into C++. For now, assume the inputs have no missing values. - -1. `all()`. - -2. `cumprod()`, `cummin()`, `cummax()`. - -3. `diff()`. Start by assuming lag 1, and then generalise for lag `n`. - -4. `range()`. - -5. `var()`. Read about the approaches you can take on [Wikipedia](http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance). Whenever implementing a numerical algorithm, it's always good to check what is already known about the problem. - -**A2.** The performance benefits are not going to be observed if the function is primitive since those are already tuned to the max in R for performance. So, expect performance gain only for `diff()` and `var()`. - - -```r -is.primitive(all) -#> [1] TRUE -is.primitive(cumprod) -#> [1] TRUE -is.primitive(diff) -#> [1] FALSE -is.primitive(range) -#> [1] TRUE -is.primitive(var) -#> [1] FALSE -``` - -- `all()` - - -```cpp -#include -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -bool allC(std::vector x) -{ - for (const auto& xElement : x) - { - if (!xElement) return false; - } - - return true; -} -``` - - -```r -v1 <- rep(TRUE, 10) -v2 <- c(rep(TRUE, 5), rep(FALSE, 5)) - -all(v1) -#> [1] TRUE -allC(v1) -#> [1] TRUE - -all(v2) -#> [1] FALSE -allC(v2) -#> [1] FALSE - -# performance benefits? -bench::mark( - all(c(rep(TRUE, 1000), rep(FALSE, 1000))), - allC(c(rep(TRUE, 1000), rep(FALSE, 1000))), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min -#> -#> 1 all(c(rep(TRUE, 1000), rep(FALSE, 1000))) 14.2Β΅s -#> 2 allC(c(rep(TRUE, 1000), rep(FALSE, 1000))) 12.6Β΅s -#> median `itr/sec` mem_alloc `gc/sec` -#> -#> 1 16.5Β΅s 60321. 15.8KB 0 -#> 2 15.1Β΅s 64552. 18.3KB 0 -``` - -- `cumprod()` - - -```cpp -#include - -// [[Rcpp::export]] -std::vector cumprodC(const std::vector &x) -{ - std::vector out{x}; - - for (size_t i = 1; i < x.size(); i++) - { - out[i] = out[i - 1] * x[i]; - } - - return out; -} -``` - - - -```r -v1 <- c(10, 4, 6, 8) - -cumprod(v1) -#> [1] 10 40 240 1920 -cumprodC(v1) -#> [1] 10 40 240 1920 - -# performance benefits? -bench::mark( - cumprod(v1), - cumprodC(v1), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 cumprod(v1) 0 41ns 13697871. 0B -#> 2 cumprodC(v1) 779ns 943ns 636481. 6.62KB -#> `gc/sec` -#> -#> 1 0 -#> 2 0 -``` - -- `cumminC()` - - -```cpp -#include -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -std::vector cumminC(const std::vector &x) -{ - std::vector out{x}; - - for (size_t i = 1; i < x.size(); i++) - { - out[i] = (out[i] < out[i - 1]) ? out[i] : out[i - 1]; - } - - return out; -} -``` - - -```r -v1 <- c(3:1, 2:0, 4:2) - -cummin(v1) -#> [1] 3 2 1 1 1 0 0 0 0 -cumminC(v1) -#> [1] 3 2 1 1 1 0 0 0 0 - -# performance benefits? -bench::mark( - cummin(v1), - cumminC(v1), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc `gc/sec` -#> -#> 1 cummin(v1) 41ns 82.07ns 6699581. 0B 0 -#> 2 cumminC(v1) 984ns 1.15Β΅s 694673. 6.62KB 0 -``` - -- `cummaxC()` - - -```cpp -#include -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -std::vector cummaxC(const std::vector &x) -{ - std::vector out{x}; - - for (size_t i = 1; i < x.size(); i++) - { - out[i] = (out[i] > out[i - 1]) ? out[i] : out[i - 1]; - } - - return out; -} -``` - - -```r -v1 <- c(3:1, 2:0, 4:2) - -cummax(v1) -#> [1] 3 3 3 3 3 3 4 4 4 -cummaxC(v1) -#> [1] 3 3 3 3 3 3 4 4 4 - -# performance benefits? -bench::mark( - cummax(v1), - cummaxC(v1), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc `gc/sec` -#> -#> 1 cummax(v1) 40.98ns 41.1ns 8647005. 0B 0 -#> 2 cummaxC(v1) 2.71Β΅s 3.5Β΅s 255715. 6.62KB 0 -``` - -- `diff()` - - -```cpp -#include -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -std::vector diffC(const std::vector &x, int lag) -{ - std::vector vec_start; - std::vector vec_lagged; - std::vector vec_diff; - - for (size_t i = lag; i < x.size(); i++) - { - vec_lagged.push_back(x[i]); - } - - for (size_t i = 0; i < (x.size() - lag); i++) - { - vec_start.push_back(x[i]); - } - - std::transform( - vec_lagged.begin(), vec_lagged.end(), - vec_start.begin(), std::back_inserter(vec_diff), - std::minus()); - - return vec_diff; -} -``` - - -```r -v1 <- c(1, 2, 4, 8, 13) -v2 <- c(1, 2, NA, 8, 13) - -diff(v1, 2) -#> [1] 3 6 9 -diffC(v1, 2) -#> [1] 3 6 9 - -diff(v2, 2) -#> [1] NA 6 NA -diffC(v2, 2) -#> [1] NA 6 NA - -# performance benefits? -bench::mark( - diff(v1, 2), - diffC(v1, 2), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 diff(v1, 2) 2.42Β΅s 3.36Β΅s 166452. 0B -#> 2 diffC(v1, 2) 4.02Β΅s 4.3Β΅s 210097. 2.49KB -#> `gc/sec` -#> -#> 1 0 -#> 2 0 -``` - -- `range()` - - -```cpp -#include -#include -#include -using namespace std; - -// [[Rcpp::export]] -std::vector rangeC(std::vector x) -{ - std::vector rangeVec{0.0, 0.0}; - - rangeVec.at(0) = *std::min_element(x.begin(), x.end()); - rangeVec.at(1) = *std::max_element(x.begin(), x.end()); - - return rangeVec; -} -``` - - -```r -v1 <- c(10, 4, 6, 8) - -range(v1) -#> [1] 4 10 -rangeC(v1) -#> [1] 4 10 - -# performance benefits? -bench::mark( - range(v1), - rangeC(v1), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc `gc/sec` -#> -#> 1 range(v1) 3.4Β΅s 3.65Β΅s 250437. 0B 0 -#> 2 rangeC(v1) 2.91Β΅s 3.16Β΅s 253825. 6.62KB 0 -``` - -- `var()` - - -```cpp -#include -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -double variance(std::vector x) -{ - double sumSquared{0}; - - double mean = std::accumulate(x.begin(), x.end(), 0.0) / x.size(); - - for (const auto& xElement : x) - { - sumSquared += pow(xElement - mean, 2.0); - } - - return sumSquared / (x.size() - 1); -} -``` - - - -```r -v1 <- c(1, 4, 7, 8) - -var(v1) -#> [1] 10 -variance(v1) -#> [1] 10 - -# performance benefits? -bench::mark( - var(v1), - variance(v1), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 var(v1) 4.14Β΅s 5.08Β΅s 139996. 0B -#> 2 variance(v1) 943.08ns 2.91Β΅s 365503. 6.62KB -#> `gc/sec` -#> -#> 1 0 -#> 2 0 -``` - -## Missing values (Exercises 25.4.5) - -**Q1.** Rewrite any of the functions from Exercise 25.2.6 to deal with missing values. If `na.rm` is true, ignore the missing values. If `na.rm` is false, return a missing value if the input contains any missing values. Some good functions to practice with are `min()`, `max()`, `range()`, `mean()`, and `var()`. - -**A1.** We will only create a version of `range()` that deals with missing values. The same principle applies to others: - - -```cpp -#include -#include -#include -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -std::vector rangeC_NA(std::vector x, bool removeNA = true) -{ - std::vector rangeVec{0.0, 0.0}; - - bool naPresent = std::any_of( - x.begin(), - x.end(), - [](double d) - { return isnan(d); }); - - if (naPresent) - { - if (removeNA) - { - std::remove(x.begin(), x.end(), NAN); - } - else - { - rangeVec.at(0) = NA_REAL; // NAN; - rangeVec.at(1) = NA_REAL; // NAN; - - return rangeVec; - } - } - - rangeVec.at(0) = *std::min_element(x.begin(), x.end()); - rangeVec.at(1) = *std::max_element(x.begin(), x.end()); - - return rangeVec; -} -``` - - -```r -v1 <- c(10, 4, NA, 6, 8) - -range(v1, na.rm = FALSE) -#> [1] NA NA -rangeC_NA(v1, FALSE) -#> [1] NA NA - -range(v1, na.rm = TRUE) -#> [1] 4 10 -rangeC_NA(v1, TRUE) -#> [1] 4 10 -``` - -**Q2.** Rewrite `cumsum()` and `diff()` so they can handle missing values. Note that these functions have slightly more complicated behaviour. - -**A2.** The `cumsum()` docs say: - -> An `NA` value in `x` causes the corresponding and following elements of the return value to be `NA`, as does integer overflow in cumsum (with a warning). - -Similarly, `diff()` docs say: - -> `NA`'s propagate. - -Therefore, both of these functions don't allow removing missing values and the `NA`s propagate. - -As seen from the examples above, `diffC()` already behaves this way. - -Similarly, `cumsumC()` propagates `NA`s as well. - - -```cpp -#include -using namespace Rcpp; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -NumericVector cumsumC(NumericVector x) { - int n = x.size(); - NumericVector out(n); - - out[0] = x[0]; - for(int i = 1; i < n; ++i) { - out[i] = out[i - 1] + x[i]; - } - - return out; -} -``` - - -```r -v1 <- c(1, 2, 3, 4) -v2 <- c(1, 2, NA, 4) - -cumsum(v1) -#> [1] 1 3 6 10 -cumsumC(v1) -#> [1] 1 3 6 10 - -cumsum(v2) -#> [1] 1 3 NA NA -cumsumC(v2) -#> [1] 1 3 NA NA -``` - -## Standard Template Library (Exercises 25.5.7) - -**Q1.** To practice using the STL algorithms and data structures, implement the following using R functions in C++, using the hints provided: - -**A1.** - -1. `median.default()` using `partial_sort`. - - -```cpp -#include -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -double medianC(std::vector &x) -{ - int middleIndex = static_cast(x.size() / 2); - - std::partial_sort(x.begin(), x.begin() + middleIndex, x.end()); - - // for even number of observations - if (x.size() % 2 == 0) - { - return (x[middleIndex - 1] + x[middleIndex]) / 2; - } - - return x[middleIndex]; -} -``` - - - -```r -v1 <- c(1, 3, 3, 6, 7, 8, 9) -v2 <- c(1, 2, 3, 4, 5, 6, 8, 9) - -median.default(v1) -#> [1] 6 -medianC(v1) -#> [1] 6 - -median.default(v2) -#> [1] 4.5 -medianC(v2) -#> [1] 4.5 - -# performance benefits? -bench::mark( - median.default(v2), - medianC(v2), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 median.default(v2) 13.1Β΅s 13.88Β΅s 66718. 0B -#> 2 medianC(v2) 943ns 1.15Β΅s 616685. 2.49KB -#> `gc/sec` -#> -#> 1 0 -#> 2 0 -``` - -1. `%in%` using `unordered_set` and the `find()` or `count()` methods. - - -```cpp -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -std::vector matchC(const std::vector &x, const std::vector &table) -{ - std::unordered_set tableUnique(table.begin(), table.end()); - std::vector out; - - for (const auto &xElem : x) - { - out.push_back(tableUnique.find(xElem) != tableUnique.end() ? true : false); - } - - return out; -} -``` - - -```r -x1 <- c(3, 4, 8) -x2 <- c(1, 2, 3, 3, 4, 4, 5, 6) - -x1 %in% x2 -#> [1] TRUE TRUE FALSE -matchC(x1, x2) -#> [1] TRUE TRUE FALSE - -# performance benefits? -bench::mark( - x1 %in% x2, - matchC(x1, x2), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 x1 %in% x2 532.95ns 696.98ns 571727. 0B -#> 2 matchC(x1, x2) 4.26Β΅s 4.57Β΅s 173399. 6.62KB -#> `gc/sec` -#> -#> 1 0 -#> 2 0 -``` - -1. `unique()` using an `unordered_set` (challenge: do it in one line!). - - -```cpp -#include -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -std::unordered_set uniqueC(const std::vector &x) -{ - std::unordered_set xSet(x.begin(), x.end()); - - return xSet; -} -``` - -Note that these functions are **not** comparable. As far as I can see, there is no way to get the same output as the R version of the function using the `unordered_set` data structure. - - -```r -v1 <- c(1, 3, 3, 6, 7, 8, 9) - -unique(v1) -#> [1] 1 3 6 7 8 9 -uniqueC(v1) -#> [1] 9 8 1 7 3 6 -``` - -We can make comparable version using `set` data structure: - - -```cpp -#include -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -std::set uniqueC2(const std::vector &x) -{ - std::set xSet(x.begin(), x.end()); - - return xSet; -} -``` - - -```r -v1 <- c(1, 3, 3, 6, 7, 8, 9) - -unique(v1) -#> [1] 1 3 6 7 8 9 -uniqueC2(v1) -#> [1] 1 3 6 7 8 9 - -# performance benefits? -bench::mark( - unique(v1), - uniqueC2(v1), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 unique(v1) 1.31Β΅s 1.5Β΅s 452423. 0B -#> 2 uniqueC2(v1) 1.02Β΅s 1.21Β΅s 462107. 6.62KB -#> `gc/sec` -#> -#> 1 0 -#> 2 0 -``` - -1. `min()` using `std::min()`, or `max()` using `std::max()`. - - -```cpp -#include -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -const double minC(const std::vector &x) -{ - return *std::min_element(x.begin(), x.end()); -} - -// [[Rcpp::export]] -const double maxC(std::vector x) -{ - return *std::max_element(x.begin(), x.end()); -} -``` - - -```r -v1 <- c(3, 3, 6, 1, 9, 7, 8) - -min(v1) -#> [1] 1 -minC(v1) -#> [1] 1 - -# performance benefits? -bench::mark( - min(v1), - minC(v1), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc `gc/sec` -#> -#> 1 min(v1) 122.94ns 615.02ns 1122395. 0B 0 -#> 2 minC(v1) 2.99Β΅s 3.16Β΅s 249057. 6.62KB 0 - -max(v1) -#> [1] 9 -maxC(v1) -#> [1] 9 - -# performance benefits? -bench::mark( - max(v1), - maxC(v1), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc `gc/sec` -#> -#> 1 max(v1) 123ns 164.03ns 5517545. 0B 0 -#> 2 maxC(v1) 943ns 2.91Β΅s 359208. 6.62KB 0 -``` - -1. `which.min()` using `min_element`, or `which.max()` using `max_element`. - - -```cpp -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -int which_maxC(std::vector &x) -{ - int maxIndex = std::distance(x.begin(), std::max_element(x.begin(), x.end())); - - // R is 1-index based, while C++ is 0-index based - return maxIndex + 1; -} - -// [[Rcpp::export]] -int which_minC(std::vector &x) -{ - int minIndex = std::distance(x.begin(), std::min_element(x.begin(), x.end())); - - // R is 1-index based, while C++ is 0-index based - return minIndex + 1; -} -``` - - - -```r -v1 <- c(3, 3, 6, 1, 9, 7, 8) - -which.min(v1) -#> [1] 4 -which_minC(v1) -#> [1] 4 - -# performance benefits? -bench::mark( - which.min(v1), - which_minC(v1), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 which.min(v1) 287ns 943.08ns 692700. 0B -#> 2 which_minC(v1) 984ns 3.26Β΅s 40664. 6.62KB -#> `gc/sec` -#> -#> 1 0 -#> 2 0 - -which.max(v1) -#> [1] 5 -which_maxC(v1) -#> [1] 5 - -# performance benefits? -bench::mark( - which.max(v1), - which_maxC(v1), - iterations = 100 -) -#> # A tibble: 2 Γ— 6 -#> expression min median `itr/sec` mem_alloc -#> -#> 1 which.max(v1) 205ns 942.96ns 191837. 0B -#> 2 which_maxC(v1) 902ns 1.07Β΅s 513690. 6.62KB -#> `gc/sec` -#> -#> 1 0 -#> 2 0 -``` - -1. `setdiff()`, `union()`, and `intersect()` for integers using sorted ranges and `set_union`, `set_intersection` and `set_difference`. - -Note that the following C++ implementations of given functions are not strictly equivalent to their R versions. As far as I can see, there is no way for them to be identical while satisfying the specifications mentioned in the question. - -- `union()` - - -```cpp -#include -#include -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -std::set unionC(std::vector &v1, std::vector &v2) -{ - std::sort(v1.begin(), v1.end()); - std::sort(v2.begin(), v2.end()); - - std::vector union_vec(v1.size() + v2.size()); - auto it = std::set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), union_vec.begin()); - - union_vec.resize(it - union_vec.begin()); - std::set union_set(union_vec.begin(), union_vec.end()); - - return union_set; -} -``` - - -```r -v1 <- c(1, 4, 5, 5, 5, 6, 2) -v2 <- c(4, 1, 6, 8) - -union(v1, v2) -#> [1] 1 4 5 6 2 8 -unionC(v1, v2) -#> [1] 1 2 4 5 6 8 -``` - -- `intersect()` - - -```cpp -#include -#include -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -std::set intersectC(std::vector &v1, std::vector &v2) -{ - std::sort(v1.begin(), v1.end()); - std::sort(v2.begin(), v2.end()); - - std::vector union_vec(v1.size() + v2.size()); - auto it = std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), union_vec.begin()); - - union_vec.resize(it - union_vec.begin()); - std::set union_set(union_vec.begin(), union_vec.end()); - - return union_set; -} -``` - - -```r -v1 <- c(1, 4, 5, 5, 5, 6, 2) -v2 <- c(4, 1, 6, 8) - -intersect(v1, v2) -#> [1] 1 4 6 -intersectC(v1, v2) -#> [1] 1 4 6 -``` - -- `setdiff()` - - -```cpp -#include -#include -#include -#include -using namespace std; -// [[Rcpp::plugins(cpp11)]] - -// [[Rcpp::export]] -std::set setdiffC(std::vector &v1, std::vector &v2) -{ - std::sort(v1.begin(), v1.end()); - std::sort(v2.begin(), v2.end()); - - std::vector union_vec(v1.size() + v2.size()); - auto it = std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), union_vec.begin()); - - union_vec.resize(it - union_vec.begin()); - std::set union_set(union_vec.begin(), union_vec.end()); - - return union_set; -} -``` - - -```r -v1 <- c(1, 4, 5, 5, 5, 6, 2) -v2 <- c(4, 1, 6, 8) - -setdiff(v1, v2) -#> [1] 5 2 -setdiffC(v1, v2) -#> [1] 2 5 -``` - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> base * 4.2.2 2022-10-31 [?] local -#> bench 1.1.2 2021-11-30 [1] CRAN (R 4.2.0) -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> profmem 0.6.0 2020-12-13 [1] CRAN (R 4.2.0) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> Rcpp * 1.0.9 2022-07-08 [1] CRAN (R 4.2.1) -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble 3.1.8.9002 2022-10-16 [1] local -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` - diff --git a/_book/S3.md b/_book/S3.md deleted file mode 100644 index 65cfc0ee..00000000 --- a/_book/S3.md +++ /dev/null @@ -1,1475 +0,0 @@ -# S3 - - - -Attaching the needed libraries: - - -```r -library(sloop, warn.conflicts = FALSE) -library(dplyr, warn.conflicts = FALSE) -library(purrr, warn.conflicts = FALSE) -``` - -## Basics (Exercises 13.2.1) - ---- - -**Q1.** Describe the difference between `t.test()` and `t.data.frame()`. When is each function called? - -**A1.** The difference between `t.test()` and `t.data.frame()` is the following: - -- `t.test()` is a **generic** function to perform a *t*-test. - -- `t.data.frame()` is a **method** for generic `t()` (a matrix transform function) and will be dispatched for `data.frame` objects. - -We can also confirm these function types using `ftype()`: - - -```r -ftype(t.test) -#> [1] "S3" "generic" -ftype(t.data.frame) -#> [1] "S3" "method" -``` - ---- - -**Q2.** Make a list of commonly used base R functions that contain `.` in their name but are not `S3` methods. - -**A2.** Here are a few common R functions with `.` but that are not `S3` methods: - -- `all.equal()` -- Most of `as.*` functions (like `as.data.frame()`, `as.numeric()`, etc.) -- `install.packages()` -- `on.exit()` -etc. - - - - - - - - - -For example, - - -```r -ftype(as.data.frame) -#> [1] "S3" "generic" -ftype(on.exit) -#> [1] "primitive" -``` - ---- - -**Q3.** What does the `as.data.frame.data.frame()` method do? Why is it confusing? How could you avoid this confusion in your own code? - -**A3.** It's an `S3` **method** for **generic** `as.data.frame()`. - - -```r -ftype(as.data.frame.data.frame) -#> [1] "S3" "method" -``` - -It can be seen in all methods supported by this generic: - - -```r -s3_methods_generic("as.data.frame") %>% - dplyr::filter(class == "data.frame") -#> # A tibble: 1 Γ— 4 -#> generic class visible source -#> -#> 1 as.data.frame data.frame TRUE base -``` - -Given the number of `.`s in this name, it is quite confusing to figure out what is the name of the generic and the name of the class. - ---- - -**Q4.** Describe the difference in behaviour in these two calls. - - -```r -set.seed(1014) -some_days <- as.Date("2017-01-31") + sample(10, 5) -mean(some_days) -#> [1] "2017-02-06" -mean(unclass(some_days)) -#> [1] 17203.4 -``` - -**A4.** The difference in behaviour in the specified calls. - -- Before unclassing, the `mean` generic dispatches `.Date` method: - - -```r -some_days <- as.Date("2017-01-31") + sample(10, 5) - -some_days -#> [1] "2017-02-06" "2017-02-09" "2017-02-05" "2017-02-08" -#> [5] "2017-02-07" - -s3_dispatch(mean(some_days)) -#> => mean.Date -#> * mean.default - -mean(some_days) -#> [1] "2017-02-07" -``` - -- After unclassing, the `mean` generic dispatches `.numeric` method: - - -```r -unclass(some_days) -#> [1] 17203 17206 17202 17205 17204 - -mean(unclass(some_days)) -#> [1] 17204 - -s3_dispatch(mean(unclass(some_days))) -#> mean.double -#> mean.numeric -#> => mean.default -``` - ---- - -**Q5.** What class of object does the following code return? What base type is it built on? What attributes does it use? - - -```r -x <- ecdf(rpois(100, 10)) -x -``` - -**A5.** The object is based on base type `closure`^[of "object of type 'closure' is not subsettable" fame], which is a type of function. - - -```r -x <- ecdf(rpois(100, 10)) -x -#> Empirical CDF -#> Call: ecdf(rpois(100, 10)) -#> x[1:18] = 2, 3, 4, ..., 18, 19 - -otype(x) -#> [1] "S3" -typeof(x) -#> [1] "closure" -``` - -Its class is `ecdf`, which has other superclasses. - - -```r -s3_class(x) -#> [1] "ecdf" "stepfun" "function" -``` - -Apart from `class`, it has the following attributes: - - -```r -attributes(x) -#> $class -#> [1] "ecdf" "stepfun" "function" -#> -#> $call -#> ecdf(rpois(100, 10)) -``` - ---- - -**Q6.** What class of object does the following code return? What base type is it built on? What attributes does it use? - - -```r -x <- table(rpois(100, 5)) -x -``` - -**A6.** The object is based on base type `integer`. - - -```r -x <- table(rpois(100, 5)) -x -#> -#> 1 2 3 4 5 6 7 8 9 10 -#> 7 7 18 13 14 14 16 4 4 3 - -otype(x) -#> [1] "S3" -typeof(x) -#> [1] "integer" -``` - -Its class is `table`. - - -```r -s3_class(x) -#> [1] "table" -``` - -Apart from `class`, it has the following attributes: - - -```r -attributes(x) -#> $dim -#> [1] 10 -#> -#> $dimnames -#> $dimnames[[1]] -#> [1] "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" -#> -#> -#> $class -#> [1] "table" -``` - ---- - -## Classes (Exercises 13.3.4) - ---- - -**Q1.** Write a constructor for `data.frame` objects. What base type is a data frame built on? What attributes does it use? What are the restrictions placed on the individual elements? What about the names? - -**A1.** A data frame is built on top of a named list of atomic vectors and has attributes for row names: - - -```r -unclass(data.frame()) -#> named list() -#> attr(,"row.names") -#> integer(0) -``` - -The restriction imposed on individual elements is that they need to have the same length. Additionally, the names need to be syntactically valid and unique. - - -```r -new_data_frame <- function(x = list(), row.names = character()) { - # row names should be character - if (!all(is.character(row.names))) { - stop("Row name should be of `chracter` type.", call. = FALSE) - } - - # all elements should have the same length - unique_element_lengths <- unique(purrr::map_int(x, length)) - if (length(unique_element_lengths) > 1L) { - stop("All list elements in `x` should have same length.", call. = FALSE) - } - - # if not provided, generate row names - # this is necessary if there is at least one element in the list - if (length(x) > 0L && length(row.names) == 0L) { - row.names <- .set_row_names(unique_element_lengths) - } - - structure(x, class = "data.frame", row.names = row.names) -} -``` - -Let's try it out: - - -```r -new_data_frame(list("x" = 1, "y" = c(2, 3))) -#> Error: All list elements in `x` should have same length. - -new_data_frame(list("x" = 1, "y" = c(2)), row.names = 1L) -#> Error: Row name should be of `chracter` type. - -new_data_frame(list()) -#> data frame with 0 columns and 0 rows - -new_data_frame(list("x" = 1, "y" = 2)) -#> x y -#> 1 1 2 - -new_data_frame(list("x" = 1, "y" = 2), row.names = "row-1") -#> x y -#> row-1 1 2 -``` - ---- - -**Q2.** Enhance my `factor()` helper to have better behaviour when one or more `values` is not found in `levels`. What does `base::factor()` do in this situation? - -**A2.** When one or more `values` is not found in `levels`, those values are converted to `NA` in `base::factor()`: - - -```r -base::factor(c("a", "b", "c"), levels = c("a", "c")) -#> [1] a c -#> Levels: a c -``` - -In the new constructor, we can throw an error to inform the user: - - -```r -new_factor <- function(x = integer(), levels = character()) { - stopifnot(is.integer(x)) - stopifnot(is.character(levels)) - - structure( - x, - levels = levels, - class = "factor" - ) -} - -validate_factor <- function(x) { - values <- unclass(x) - levels <- attr(x, "levels") - - if (!all(!is.na(values) & values > 0)) { - stop( - "All `x` values must be non-missing and greater than zero", - call. = FALSE - ) - } - - if (length(levels) < max(values)) { - stop( - "There must be at least as many `levels` as possible values in `x`", - call. = FALSE - ) - } - - x -} - -create_factor <- function(x = character(), levels = unique(x)) { - ind <- match(x, levels) - - if (any(is.na(ind))) { - missing_values <- x[which(is.na(match(x, levels)))] - - stop( - paste0( - "Following values from `x` are not present in `levels`:\n", - paste0(missing_values, collapse = "\n") - ), - call. = FALSE - ) - } - - validate_factor(new_factor(ind, levels)) -} -``` - -Let's try it out: - - -```r -create_factor(c("a", "b", "c"), levels = c("a", "c")) -#> Error: Following values from `x` are not present in `levels`: -#> b - -create_factor(c("a", "b", "c"), levels = c("a", "b", "c")) -#> [1] a b c -#> Levels: a b c -``` - ---- - -**Q3.** Carefully read the source code of `factor()`. What does it do that my constructor does not? - -**A3.** The source code for `factor()` can be read [here](https://github.com/r-devel/r-svn/blob/master/src/library/base/R/factor.R). - -There are a number ways in which the base version is more flexible. - -- It allows labeling the values: - - -```r -x <- c("a", "b", "b") -levels <- c("a", "b", "c") -labels <- c("one", "two", "three") - -factor(x, levels = levels, labels = labels) -#> [1] one two two -#> Levels: one two three -``` - -- It checks that the levels are not duplicated. - - -```r -x <- c("a", "b", "b") -levels <- c("a", "b", "b") - -factor(x, levels = levels) -#> Error in `levels<-`(`*tmp*`, value = as.character(levels)): factor level [3] is duplicated - -create_factor(x, levels = levels) -#> [1] a b b -#> Levels: a b b -#> Warning in print.factor(x): duplicated level [3] in factor -``` - -- The `levels` argument can be `NULL`. - - -```r -x <- c("a", "b", "b") - -factor(x, levels = NULL) -#> [1] -#> Levels: - -create_factor(x, levels = NULL) -#> Error: Following values from `x` are not present in `levels`: -#> a -#> b -#> b -``` - -**Q4.** Factors have an optional "contrasts" attribute. Read the help for `C()`, and briefly describe the purpose of the attribute. What type should it have? Rewrite the `new_factor()` constructor to include this attribute. - -**A4.** Categorical variables are typically encoded as dummy variables in regression models and by default each level is compared with the first factor level. Contrats provide a flexible way for such comparisons. - -You can set the `"contrasts"` attribute for a factor using `stats::C()`. - -Alternatively, you can set the `"contrasts"` attribute using matrix (`?contrasts`): - -> [Contrasts] can be a matrix with one row for each level of the factor or a suitable function like contr.poly or a character string giving the name of the function - -The constructor provided in the book: - - -```r -new_factor <- function(x = integer(), levels = character()) { - stopifnot(is.integer(x)) - stopifnot(is.character(levels)) - - structure( - x, - levels = levels, - class = "factor" - ) -} -``` - -Here is how it can be updated to also support contrasts: - - -```r -new_factor <- function(x = integer(), - levels = character(), - contrasts = NULL) { - stopifnot(is.integer(x)) - stopifnot(is.character(levels)) - - if (!is.null(contrasts)) { - stopifnot(is.matrix(contrasts) && is.numeric(contrasts)) - } - - structure( - x, - levels = levels, - class = "factor", - contrasts = contrasts - ) -} -``` - -**Q5.** Read the documentation for `utils::as.roman()`. How would you write a constructor for this class? Does it need a validator? What might a helper do? - -**A5.** `utils::as.roman()` converts Indo-Arabic numerals to Roman numerals. Removing its class also reveals that it is implemented using the base type `integer`: - - -```r -as.roman(1) -#> [1] I - -typeof(unclass(as.roman(1))) -#> [1] "integer" -``` - -Therefore, we can create a simple constructor to create a new instance of this class: - - -```r -new_roman <- function(x = integer()) { - stopifnot(is.integer(x)) - - structure(x, class = "roman") -} -``` - -The docs mention the following: - -> Only numbers between 1 and 3899 have a unique representation as roman numbers, and hence others result in as.roman(NA). - - -```r -as.roman(10000) -#> [1] -``` - -Therefore, we can warn the user and then return `NA` in a validator function: - - -```r -validate_new_roman <- function(x) { - int_values <- unclass(x) - - if (any(int_values < 1L | int_values > 3899L)) { - warning( - "Integer should be between 1 and 3899. Returning `NA` otherwise.", - call. = FALSE - ) - } - - x -} -``` - -The helper function can coerce the entered input to integer type for convenience: - - -```r -roman <- function(x = integer()) { - x <- as.integer(x) - - validate_new_roman(new_roman(x)) -} -``` - -Let's try it out: - - -```r -roman(1) -#> [1] I - -roman(c(5, 20, 100, 150, 100000)) -#> Warning: Integer should be between 1 and 3899. Returning -#> `NA` otherwise. -#> [1] V XX C CL -``` - -## Generics and methods (Exercises 13.4.4) - -**Q1.** Read the source code for `t()` and `t.test()` and confirm that `t.test()` is an S3 generic and not an S3 method. What happens if you create an object with class `test` and call `t()` with it? Why? - - -```r -x <- structure(1:10, class = "test") -t(x) -``` - -**A1.** Looking at source code of these functions, we can see that both of these are generic, and we can confirm the same using `{sloop}`: - - -```r -t -#> function (x) -#> UseMethod("t") -#> -#> -sloop::is_s3_generic("t") -#> [1] TRUE - -t.test -#> function (x, ...) -#> UseMethod("t.test") -#> -#> -sloop::is_s3_generic("t.test") -#> [1] TRUE -``` - -Looking at the `S3` dispatch, we can see that since R can't find `S3` method for `test` class for generic function `t()`, it dispatches the default method, which converts the structure to a matrix: - - -```r -x <- structure(1:10, class = "test") -t(x) -#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] -#> [1,] 1 2 3 4 5 6 7 8 9 10 -#> attr(,"class") -#> [1] "test" -s3_dispatch(t(x)) -#> => t.test -#> * t.default -``` - -The same behaviour can be observed with a vector: - - -```r -t(1:10) -#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] -#> [1,] 1 2 3 4 5 6 7 8 9 10 -``` - -**Q2.** What generics does the `table` class have methods for? - -**A2.** The `table` class have methods for the following generics: - - -```r -s3_methods_class("table") -#> # A tibble: 11 Γ— 4 -#> generic class visible source -#> -#> 1 [ table TRUE base -#> 2 aperm table TRUE base -#> 3 as_tibble table FALSE registered S3method -#> 4 as.data.frame table TRUE base -#> 5 Axis table FALSE registered S3method -#> 6 lines table FALSE registered S3method -#> 7 plot table FALSE registered S3method -#> 8 points table FALSE registered S3method -#> 9 print table TRUE base -#> 10 summary table TRUE base -#> 11 tail table FALSE registered S3method -``` - -**Q3.** What generics does the `ecdf` class have methods for? - -**A3.** The `ecdf` class have methods for the following generics: - - -```r -s3_methods_class("ecdf") -#> # A tibble: 4 Γ— 4 -#> generic class visible source -#> -#> 1 plot ecdf TRUE stats -#> 2 print ecdf FALSE registered S3method -#> 3 quantile ecdf FALSE registered S3method -#> 4 summary ecdf FALSE registered S3method -``` - -**Q4.** Which base generic has the greatest number of defined methods? - -**A4.** To answer this question, first, let's list all functions base has and only retain the generics. - - -```r -# getting all functions names -objs <- mget(ls("package:base", all = TRUE), inherits = TRUE) -funs <- Filter(is.function, objs) - -# extracting only generics -genFuns <- names(funs) %>% - purrr::keep(~ sloop::is_s3_generic(.x)) -``` - -Now it's a simple matter of counting number of methods per generic and ordering the data frame in descending order of this count: - - -```r -purrr::map_dfr( - genFuns, - ~ s3_methods_generic(.) -) %>% - dplyr::group_by(generic) %>% - dplyr::tally() %>% - dplyr::arrange(desc(n)) -#> # A tibble: 120 Γ— 2 -#> generic n -#> -#> 1 print 286 -#> 2 format 132 -#> 3 [ 53 -#> 4 summary 39 -#> 5 as.character 37 -#> 6 as.data.frame 31 -#> 7 plot 30 -#> 8 [[ 27 -#> 9 [<- 17 -#> 10 $ 15 -#> # … with 110 more rows -``` - -This reveals that the base generic function with most methods is `print()`. - -**Q5.** Carefully read the documentation for `UseMethod()` and explain why the following code returns the results that it does. What two usual rules of function evaluation does `UseMethod()` violate? - - -```r -g <- function(x) { - x <- 10 - y <- 10 - UseMethod("g") -} -g.default <- function(x) c(x = x, y = y) -x <- 1 -y <- 1 -g(x) -#> x y -#> 1 10 -``` - -**A5.** If called directly, `g.default()` method takes `x` value from argument and `y` from the global environment: - - -```r -g.default(x) -#> x y -#> 1 1 -``` - -But, if `g()` function is called, it takes the `x` from argument, but comes from function environment: - - -```r -g(x) -#> x y -#> 1 10 -``` - -The docs for `?UseMethod()` clarify why this is the case: - -> Any local variables defined before the call to UseMethod are retained - -That is, when `UseMethod()` calls `g.default()`, variables defined inside the generic are also available to `g.default()` method. The arguments supplied to the function are passed on as is, however, and cannot be affected by code inside the generic. - -Two rules of function evaluation violated by `UseMethod()`: - -- Name masking -- A fresh start - -**Q6.** What are the arguments to `[`? Why is this a hard question to answer? - -**A6.** It is difficult to say how many formal arguments the subsetting `[` operator has because it is a generic function with methods for vectors, matrices, arrays, lists, etc., and these different methods have different number of arguments: - - -```r -s3_methods_generic("[") %>% - dplyr::filter(source == "base") -#> # A tibble: 17 Γ— 4 -#> generic class visible source -#> -#> 1 [ AsIs TRUE base -#> 2 [ data.frame TRUE base -#> 3 [ Date TRUE base -#> 4 [ difftime TRUE base -#> 5 [ Dlist TRUE base -#> 6 [ DLLInfoList TRUE base -#> 7 [ factor TRUE base -#> 8 [ hexmode TRUE base -#> 9 [ listof TRUE base -#> 10 [ noquote TRUE base -#> 11 [ numeric_version TRUE base -#> 12 [ octmode TRUE base -#> 13 [ POSIXct TRUE base -#> 14 [ POSIXlt TRUE base -#> 15 [ simple.list TRUE base -#> 16 [ table TRUE base -#> 17 [ warnings TRUE base -``` - -We can sample a few of them to see the wide variation in the number of formal arguments: - - -```r -# table -names(formals(`[.table`)) -#> [1] "x" "i" "j" "..." "drop" - -# Date -names(formals(`[.Date`)) -#> [1] "x" "..." "drop" - -# data frame -names(formals(`[.data.frame`)) -#> [1] "x" "i" "j" "drop" - -# etc. -``` - -## Object styles (Exercises 13.5.1) - -**Q1.** Categorise the objects returned by `lm()`, `factor()`, `table()`, `as.Date()`, `as.POSIXct()` `ecdf()`, `ordered()`, `I()` into the styles described above. - -**A1.** Objects returned by these functions can be categorized as follows: - -- Vector style objects (`length` represents no. of observations) - -`factor()` - - -```r -factor_obj <- factor(c("a", "b")) -length(factor_obj) -#> [1] 2 -length(unclass(factor_obj)) -#> [1] 2 -``` - -`table()` - - -```r -tab_object <- table(mtcars$am) -length(tab_object) -#> [1] 2 -length(unlist(tab_object)) -#> [1] 2 -``` - -`as.Date()` - - -```r -date_obj <- as.Date("02/27/92", "%m/%d/%y") -length(date_obj) -#> [1] 1 -length(unclass(date_obj)) -#> [1] 1 -``` - -`as.POSIXct()` - - -```r -posix_obj <- as.POSIXct(1472562988, origin = "1960-01-01") -length(posix_obj) -#> [1] 1 -length(unclass(posix_obj)) -#> [1] 1 -``` - -`ordered()` - - -```r -ordered_obj <- ordered(factor(c("a", "b"))) -length(ordered_obj) -#> [1] 2 -length(unclass(ordered_obj)) -#> [1] 2 -``` - -- Record style objects (equi-length vectors to represent object components) - -None. - -- Dataframe style objects (Record style but two-dimensions) - -None. - -- Scalar objects (a list to represent a single thing) - -`lm()` (represent one regression model) - - -```r -lm_obj <- lm(wt ~ mpg, mtcars) -length(lm_obj) -#> [1] 12 -length(unclass(lm_obj)) -#> [1] 12 -``` - -`ecdf()` (represents one distribution) - - -```r -ecdf_obj <- ecdf(rnorm(12)) -length(ecdf_obj) -#> [1] 1 -length(unclass(ecdf_obj)) -#> [1] 1 -``` - -`I()` is special: -It just adds a new class to the object to indicate that it should be treated *as is*. - - -```r -x <- ecdf(rnorm(12)) -class(x) -#> [1] "ecdf" "stepfun" "function" -class(I(x)) -#> [1] "AsIs" "ecdf" "stepfun" "function" -``` - -Therefore, the object style would be the same as the superclass' object style. - -**Q2.** What would a constructor function for `lm` objects, `new_lm()`, look like? Use `?lm` and experimentation to figure out the required fields and their types. - -**A2.** The `lm` object is a scalar object, i.e. this object contains a named list of atomic vectors of varying lengths and types to represent a single thing (a regression model). - - -```r -mod <- lm(wt ~ mpg, mtcars) - -typeof(mod) -#> [1] "list" - -attributes(mod) -#> $names -#> [1] "coefficients" "residuals" "effects" -#> [4] "rank" "fitted.values" "assign" -#> [7] "qr" "df.residual" "xlevels" -#> [10] "call" "terms" "model" -#> -#> $class -#> [1] "lm" - -purrr::map_chr(unclass(mod), typeof) -#> coefficients residuals effects rank -#> "double" "double" "double" "integer" -#> fitted.values assign qr df.residual -#> "double" "integer" "list" "integer" -#> xlevels call terms model -#> "list" "language" "language" "list" - -purrr::map_int(unclass(mod), length) -#> coefficients residuals effects rank -#> 2 32 32 1 -#> fitted.values assign qr df.residual -#> 32 2 5 1 -#> xlevels call terms model -#> 0 3 3 2 -``` - -Based on this information, we can write a new constructor for this object: - - -```r -new_lm <- function(coefficients, - residuals, - effects, - rank, - fitted.values, - assign, - qr, - df.residual, - xlevels, - call, - terms, - model) { - stopifnot( - is.double(coefficients), - is.double(residuals), - is.double(effects), - is.integer(rank), - is.double(fitted.values), - is.integer(assign), - is.list(qr), - is.integer(df.residual), - is.list(xlevels), - is.language(call), - is.language(terms), - is.list(model) - ) - - structure( - list( - coefficients = coefficients, - residuals = residuals, - effects = effects, - rank = rank, - fitted.values = fitted.values, - assign = assign, - qr = qr, - df.residual = df.residual, - xlevels = xlevels, - call = call, - terms = terms, - model = model - ), - class = "lm" - ) -} -``` - -## Inheritance (Exercises 13.6.3) - -**Q1.** How does `[.Date` support subclasses? How does it fail to support subclasses? - -**A1.** The `[.Date` method is defined as follows: - - -```r -sloop::s3_get_method("[.Date") -#> function (x, ..., drop = TRUE) -#> { -#> .Date(NextMethod("["), oldClass(x)) -#> } -#> -#> -``` - -The `.Date` function looks like this: - - -```r -.Date -#> function (xx, cl = "Date") -#> `class<-`(xx, cl) -#> -#> -``` - -Here, `oldClass` is the same as `class()`. - -Therefore, by reading this code, we can surmise that: - -- `[.Date` supports subclasses by preserving the class of the input. -- `[.Date` fails to support subclasses by not preserving the attributes of the input. - -For example, - - -```r -x <- structure(Sys.Date(), name = "myName", class = c("subDate", "Date")) - -# `$name` is gone -attributes(x[1]) -#> $class -#> [1] "subDate" "Date" - -x[1] -#> [1] "2022-11-12" -``` - - -**Q2.** R has two classes for representing date time data, `POSIXct` and `POSIXlt`, which both inherit from `POSIXt`. Which generics have different behaviours for the two classes? Which generics share the same behaviour? - -**A2.** First, let's demonstrate that `POSIXct` and `POSIXlt` are indeed subclasses and `POSIXt` is the superclass. - - -```r -dt_lt <- as.POSIXlt(Sys.time(), "GMT") -class(dt_lt) -#> [1] "POSIXlt" "POSIXt" - -dt_ct <- as.POSIXct(Sys.time(), "GMT") -class(dt_ct) -#> [1] "POSIXct" "POSIXt" - -dt_t <- structure(dt_ct, class = "POSIXt") -class(dt_t) -#> [1] "POSIXt" -``` - -Remember that the way `S3` method dispatch works, if a generic has a method for superclass, then that method is also inherited by the subclass. - -We can extract a vector of all generics supported by both sub- and super-classes: - - -```r -(t_generics <- s3_methods_class("POSIXt")$generic) -#> [1] "-" "+" "all.equal" -#> [4] "as.character" "Axis" "cut" -#> [7] "diff" "hist" "is.numeric" -#> [10] "julian" "Math" "months" -#> [13] "Ops" "pretty" "quantile" -#> [16] "quarters" "round" "seq" -#> [19] "str" "trunc" "weekdays" - -(lt_generics <- s3_methods_class("POSIXlt")$generic) -#> [1] "[" "[[" "[[<-" -#> [4] "[<-" "anyNA" "as.data.frame" -#> [7] "as.Date" "as.double" "as.list" -#> [10] "as.matrix" "as.POSIXct" "as.vector" -#> [13] "c" "duplicated" "format" -#> [16] "is.na" "length" "length<-" -#> [19] "mean" "names" "names<-" -#> [22] "print" "rep" "sort" -#> [25] "summary" "Summary" "unique" -#> [28] "weighted.mean" "xtfrm" - -(ct_generics <- s3_methods_class("POSIXct")$generic) -#> [1] "[" "[[" "[<-" -#> [4] "as.data.frame" "as.Date" "as.list" -#> [7] "as.POSIXlt" "c" "format" -#> [10] "length<-" "mean" "print" -#> [13] "rep" "split" "summary" -#> [16] "Summary" "weighted.mean" "xtfrm" -``` - -Methods which are specific to the subclasses: - - -```r -union(lt_generics, ct_generics) -#> [1] "[" "[[" "[[<-" -#> [4] "[<-" "anyNA" "as.data.frame" -#> [7] "as.Date" "as.double" "as.list" -#> [10] "as.matrix" "as.POSIXct" "as.vector" -#> [13] "c" "duplicated" "format" -#> [16] "is.na" "length" "length<-" -#> [19] "mean" "names" "names<-" -#> [22] "print" "rep" "sort" -#> [25] "summary" "Summary" "unique" -#> [28] "weighted.mean" "xtfrm" "as.POSIXlt" -#> [31] "split" -``` - -Let's see an example: - - -```r -s3_dispatch(is.na(dt_lt)) -#> => is.na.POSIXlt -#> is.na.POSIXt -#> is.na.default -#> * is.na (internal) - -s3_dispatch(is.na(dt_ct)) -#> is.na.POSIXct -#> is.na.POSIXt -#> is.na.default -#> => is.na (internal) - -s3_dispatch(is.na(dt_t)) -#> is.na.POSIXt -#> is.na.default -#> => is.na (internal) -``` - -Methods which are inherited by subclasses from superclass: - - -```r -setdiff(t_generics, union(lt_generics, ct_generics)) -#> [1] "-" "+" "all.equal" -#> [4] "as.character" "Axis" "cut" -#> [7] "diff" "hist" "is.numeric" -#> [10] "julian" "Math" "months" -#> [13] "Ops" "pretty" "quantile" -#> [16] "quarters" "round" "seq" -#> [19] "str" "trunc" "weekdays" -``` - -Let's see one example generic: - - -```r -s3_dispatch(is.numeric(dt_lt)) -#> is.numeric.POSIXlt -#> => is.numeric.POSIXt -#> is.numeric.default -#> * is.numeric (internal) - -s3_dispatch(is.numeric(dt_ct)) -#> is.numeric.POSIXct -#> => is.numeric.POSIXt -#> is.numeric.default -#> * is.numeric (internal) - -s3_dispatch(is.numeric(dt_t)) -#> => is.numeric.POSIXt -#> is.numeric.default -#> * is.numeric (internal) -``` - -**Q3.** What do you expect this code to return? What does it actually return? Why? - - -```r -generic2 <- function(x) UseMethod("generic2") -generic2.a1 <- function(x) "a1" -generic2.a2 <- function(x) "a2" -generic2.b <- function(x) { - class(x) <- "a1" - NextMethod() -} - -generic2(structure(list(), class = c("b", "a2"))) -``` - -**A3.** Naively, we would expect for this code to return `"a1"`, but it actually returns `"a2"`: - - -```r -generic2 <- function(x) UseMethod("generic2") -generic2.a1 <- function(x) "a1" -generic2.a2 <- function(x) "a2" -generic2.b <- function(x) { - class(x) <- "a1" - NextMethod() -} - -generic2(structure(list(), class = c("b", "a2"))) -#> [1] "a2" -``` - -`S3` dispatch explains why: - - -```r -sloop::s3_dispatch(generic2(structure(list(), class = c("b", "a2")))) -#> => generic2.b -#> -> generic2.a2 -#> generic2.default -``` - -As mentioned in the book, the `UseMethod()` function - -> tracks the list of potential next methods with a special variable, which means that modifying the object that’s being dispatched upon will have no impact on which method gets called next. - -This special variable is `.Class`: - -> `.Class` is a character vector of classes used to find the next method. `NextMethod` adds an attribute "previous" to `.Class` giving the `.Class` last used for dispatch, and shifts `.Class` along to that used for dispatch. - -So, we can print `.Class` to confirm that adding a new class to `x` indeed doesn't change `.Class`, and therefore dispatch occurs on `"a2"` class: - - -```r -generic2.b <- function(x) { - message(paste0("before: ", paste0(.Class, collapse = ", "))) - class(x) <- "a1" - message(paste0("after: ", paste0(.Class, collapse = ", "))) - - NextMethod() -} - -invisible(generic2(structure(list(), class = c("b", "a2")))) -#> before: b, a2 -#> after: b, a2 -``` - -## Dispatch details (Exercises 13.7.5) - -**Q1.** Explain the differences in dispatch below: - - -```r -length.integer <- function(x) 10 - -x1 <- 1:5 -class(x1) -#> [1] "integer" -s3_dispatch(length(x1)) -#> * length.integer -#> length.numeric -#> length.default -#> => length (internal) - -x2 <- structure(x1, class = "integer") -class(x2) -#> [1] "integer" -s3_dispatch(length(x2)) -#> => length.integer -#> length.default -#> * length (internal) -``` - -**A1.** The differences in the dispatch are due to classes of arguments: - - -```r -s3_class(x1) -#> [1] "integer" "numeric" - -s3_class(x2) -#> [1] "integer" -``` - -`x1` has an implicit class `integer` but it inherits from `numeric`, while `x2` is explicitly assigned the class `integer`. - -**Q2.** What classes have a method for the `Math` group generic in base R? Read the source code. How do the methods work? - -**A2.** The following classes have a method for the `Math` group generic in base R: - - -```r -s3_methods_generic("Math") %>% - dplyr::filter(source == "base") -#> # A tibble: 5 Γ— 4 -#> generic class visible source -#> -#> 1 Math data.frame TRUE base -#> 2 Math Date TRUE base -#> 3 Math difftime TRUE base -#> 4 Math factor TRUE base -#> 5 Math POSIXt TRUE base -``` - -Reading source code for a few of the methods: - -[`Math.factor()`](https://github.com/r-devel/r-svn/blob/master/src/library/base/R/factor.R) and [`Math.Date()`](https://github.com/r-devel/r-svn/blob/master/src/library/base/R/dates.R) provide only error message: - - -```r -Math.factor <- function(x, ...) { - stop(gettextf("%s not meaningful for factors", sQuote(.Generic))) -} - -Math.Date <- function(x, ...) { - stop(gettextf("%s not defined for \"Date\" objects", .Generic), - domain = NA - ) -} -``` - -[`Math.data.frame()`](https://github.com/r-devel/r-svn/blob/master/src/library/base/R/factor.R) is defined as follows (except the first line of code, which I have deliberately added): - - -```r -Math.data.frame <- function(x, ...) { - message(paste0("Environment variable `.Generic` set to: ", .Generic)) - - mode.ok <- vapply(x, function(x) { - is.numeric(x) || is.logical(x) || is.complex(x) - }, NA) - - if (all(mode.ok)) { - x[] <- lapply(X = x, FUN = .Generic, ...) - return(x) - } else { - vnames <- names(x) - if (is.null(vnames)) vnames <- seq_along(x) - stop( - "non-numeric-alike variable(s) in data frame: ", - paste(vnames[!mode.ok], collapse = ", ") - ) - } -} -``` - -As can be surmised from the code: the method checks that all elements are of the same and expected type. - -If so, it applies the generic (tracked via the environment variable `.Generic`) to each element of the list of atomic vectors that makes up a data frame: - - -```r -df1 <- data.frame(x = 1:2, y = 3:4) -sqrt(df1) -#> Environment variable `.Generic` set to: sqrt -#> x y -#> 1 1.000000 1.732051 -#> 2 1.414214 2.000000 -``` - -If not, it produces an error: - - -```r -df2 <- data.frame(x = c(TRUE, FALSE), y = c("a", "b")) -abs(df2) -#> Environment variable `.Generic` set to: abs -#> Error in Math.data.frame(df2): non-numeric-alike variable(s) in data frame: y -``` - -**Q3.** `Math.difftime()` is more complicated than I described. Why? - -**A3.** [`Math.difftime()`](https://github.com/r-devel/r-svn/blob/master/src/library/base/R/datetime.R) source code looks like the following: - - -```r -Math.difftime <- function(x, ...) { - switch(.Generic, - "abs" = , - "sign" = , - "floor" = , - "ceiling" = , - "trunc" = , - "round" = , - "signif" = { - units <- attr(x, "units") - .difftime(NextMethod(), units) - }, - ### otherwise : - stop(gettextf("'%s' not defined for \"difftime\" objects", .Generic), - domain = NA - ) - ) -} -``` - -This group generic is a bit more complicated because it produces an error for some generics, while it works for others. - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> codetools 0.2-18 2020-11-04 [1] CRAN (R 4.2.2) -#> P compiler 4.2.2 2022-10-31 [1] local -#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1) -#> P datasets * 4.2.2 2022-10-31 [1] local -#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388) -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> sloop * 1.0.1 2019-02-17 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble 3.1.8.9002 2022-10-16 [1] local -#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` - diff --git a/_book/S4.md b/_book/S4.md deleted file mode 100644 index 4d383a61..00000000 --- a/_book/S4.md +++ /dev/null @@ -1,685 +0,0 @@ -# S4 - - - -## Basics (Exercises 15.2.1) - ---- - -**Q1.** `lubridate::period()` returns an S4 class. What slots does it have? What class is each slot? What accessors does it provide? - -**A1.** Let's first create an instance of `Period` class: - - -```r -library(lubridate) -x <- lubridate::period(c(2, 43, 6), c("hour", "second", "minute")) -x -#> [1] "2H 6M 43S" -``` - -It has the following slots: - - -```r -slotNames(x) -#> [1] ".Data" "year" "month" "day" "hour" "minute" -``` - -Additionally, the base type of each slot (`numeric`) can be seen in `str()` output: - - -```r -str(x) -#> Formal class 'Period' [package "lubridate"] with 6 slots -#> ..@ .Data : num 43 -#> ..@ year : num 0 -#> ..@ month : num 0 -#> ..@ day : num 0 -#> ..@ hour : num 2 -#> ..@ minute: num 6 -``` - -The `{lubridate}` package provides accessors for all slots: - - -```r -year(x) -#> [1] 0 -month(x) -#> [1] 0 -day(x) -#> [1] 0 -hour(x) -#> [1] 2 -minute(x) -#> [1] 6 -second(x) -#> [1] 43 -``` - ---- - -**Q2.** What other ways can you find help for a method? Read `?"?"` and summarise the details. - -**A2.** The `"?"` operator allows access to documentation in three ways. To demonstrate different ways to access documentation, let's define a new `S4` class. - - -```r -pow <- function(x, exp) c(x, exp) -setGeneric("pow") -#> [1] "pow" -setMethod("pow", c("numeric", "numeric"), function(x, exp) x^exp) -``` - -Ways to access documentation: - -- The general documentation for a generic can be found with `?topic`: - - -```r -?pow -``` - -- The expression `type?topic` will look for the overall documentation methods for the function `f`. - - -```r -?pow # produces the function documentation - -methods?pow # looks for the overall methods documentation -``` - ---- - -## Classes (Exercises 15.3.6) - ---- - -**Q1.** Extend the Person class with fields to match `utils::person()`. Think about what slots you will need, what class each slot should have, and what you'll need to check in your validity method. - -**A1.** The code below extends the `Person` class described in the book to match more closely with `utils::person()`. - - -```r -setClass("Person", - slots = c( - age = "numeric", - given = "character", - family = "character", - middle = "character", - email = "character", - role = "character", - comment = "character" - ), - prototype = list( - age = NA_real_, - given = NA_character_, - family = NA_character_, - middle = NA_character_, - email = NA_character_, - role = NA_character_, - comment = NA_character_ - ) -) - -# Helper function to create an instance of the `Person` class -Person <- function(given, - family, - middle = NA_character_, - age = NA_real_, - email = NA_character_, - role = NA_character_, - comment = NA_character_) { - age <- as.double(age) - - new("Person", - age = age, - given = given, - family = family, - middle = middle, - email = email, - role = role, - comment = comment - ) -} - -# Validator to ensure that each slot is of length one and that the specified -# role is one of the possible roles -setValidity("Person", function(object) { - invalid_length <- NULL - slot_lengths <- c( - length(object@age), - length(object@given), - length(object@middle), - length(object@family), - length(object@email), - length(object@comment) - ) - - if (any(slot_lengths > 1L)) { - invalid_length <- "\nFollowing slots must be of length 1:\n @age, @given, @family, @middle, @email, @comment" - } - - possible_roles <- c( - NA_character_, "aut", "com", "cph", "cre", "ctb", "ctr", "dtc", "fnd", "rev", "ths", "trl" - ) - - if (any(!object@role %in% possible_roles)) { - invalid_length <- paste( - invalid_length, - "\nSlot @role(s) must be one of the following:\n", - paste(possible_roles, collapse = ", ") - ) - } - - if (!is.null(invalid_length)) { - return(invalid_length) - } else { - return(TRUE) - } -}) -#> Class "Person" [in ".GlobalEnv"] -#> -#> Slots: -#> -#> Name: age given family middle email -#> Class: numeric character character character character -#> -#> Name: role comment -#> Class: character character -``` - -Let's make sure that validation works as expected: - - -```r -# length of first argument not 1 -Person(c("Indrajeet", "Surendra"), "Patil") -#> Error in validObject(.Object): invalid class "Person" object: -#> Following slots must be of length 1: -#> @age, @given, @family, @middle, @email, @comment - -# role not recognized -Person("Indrajeet", "Patil", role = "xyz") -#> Error in validObject(.Object): invalid class "Person" object: -#> Slot @role(s) must be one of the following: -#> NA, aut, com, cph, cre, ctb, ctr, dtc, fnd, rev, ths, trl - -# all okay -Person("Indrajeet", "Patil", role = c("aut", "cph")) -#> An object of class "Person" -#> Slot "age": -#> [1] NA -#> -#> Slot "given": -#> [1] "Indrajeet" -#> -#> Slot "family": -#> [1] "Patil" -#> -#> Slot "middle": -#> [1] NA -#> -#> Slot "email": -#> [1] NA -#> -#> Slot "role": -#> [1] "aut" "cph" -#> -#> Slot "comment": -#> [1] NA -``` - ---- - -**Q2.** What happens if you define a new S4 class that doesn't have any slots? (Hint: read about virtual classes in `?setClass`.) - -**A2.** If you define a new `S4` class that doesn't have any slots, it will create *virtual* classes: - - -```r -setClass("Empty") - -isVirtualClass("Empty") -#> [1] TRUE -``` - -You can't create an instance of this class: - - -```r -new("Empty") -#> Error in new("Empty"): trying to generate an object from a virtual class ("Empty") -``` - -So how is this useful? As mentioned in `?setClass` docs: - -> Classes exist for which no actual objects can be created, the virtual classes. -> -> The most common and useful form of virtual class is the class union, a virtual class that is defined in a call to `setClassUnion()` rather than a call to `setClass()`. - -So virtual classes can still be inherited: - - -```r -setClass("Nothing", contains = "Empty") -``` - -In addition to not specifying any slots, here is another way to create virtual classes: - -> Calls to `setClass()` will also create a virtual class, either when only the Class argument is supplied (no slots or superclasses) or when the `contains=` argument includes the special class name `"VIRTUAL"`. - ---- - -**Q3.** Imagine you were going to reimplement factors, dates, and data frames in S4. Sketch out the `setClass()` calls that you would use to define the classes. Think about appropriate `slots` and `prototype`. - -**A3.** The reimplementation of following classes in `S4` might have definitions like the following. - -- `factor` - -For simplicity, we won't provide all options that `factor()` provides. Note that `x` has pseudo-class `ANY` to accept objects of any type. - - -```r -setClass("Factor", - slots = c( - x = "ANY", - levels = "character", - ordered = "logical" - ), - prototype = list( - x = character(), - levels = character(), - ordered = FALSE - ) -) - -new("Factor", x = letters[1:3], levels = LETTERS[1:3]) -#> An object of class "Factor" -#> Slot "x": -#> [1] "a" "b" "c" -#> -#> Slot "levels": -#> [1] "A" "B" "C" -#> -#> Slot "ordered": -#> [1] FALSE - -new("Factor", x = 1:3, levels = letters[1:3]) -#> An object of class "Factor" -#> Slot "x": -#> [1] 1 2 3 -#> -#> Slot "levels": -#> [1] "a" "b" "c" -#> -#> Slot "ordered": -#> [1] FALSE - -new("Factor", x = c(TRUE, FALSE, TRUE), levels = c("x", "y", "x")) -#> An object of class "Factor" -#> Slot "x": -#> [1] TRUE FALSE TRUE -#> -#> Slot "levels": -#> [1] "x" "y" "x" -#> -#> Slot "ordered": -#> [1] FALSE -``` - -- `Date` - -Just like the base-R version, this will have only integer values. - - -```r -setClass("Date2", - slots = list( - data = "integer" - ), - prototype = list( - data = integer() - ) -) - -new("Date2", data = 1342L) -#> An object of class "Date2" -#> Slot "data": -#> [1] 1342 -``` - -- `data.frame` - -The tricky part is supporting the `...` argument of `data.frame()`. For this, we can let the users pass a (named) list. - - -```r -setClass("DataFrame", - slots = c( - data = "list", - row.names = "character" - ), - prototype = list( - data = list(), - row.names = character(0L) - ) -) - -new("DataFrame", data = list(x = c("a", "b"), y = c(1L, 2L))) -#> An object of class "DataFrame" -#> Slot "data": -#> $x -#> [1] "a" "b" -#> -#> $y -#> [1] 1 2 -#> -#> -#> Slot "row.names": -#> character(0) -``` - ---- - -## Generics and methods (Exercises 15.4.5) - ---- - -**Q1.** Add `age()` accessors for the `Person` class. - -**A1.** We first should define a generic and then a method for our class: - - -```r -Indra <- Person("Indrajeet", "Patil", role = c("aut", "cph"), age = 34) - -setGeneric("age", function(x) standardGeneric("age")) -#> [1] "age" -setMethod("age", "Person", function(x) x@age) - -age(Indra) -#> [1] 34 -``` - ---- - -**Q2.** In the definition of the generic, why is it necessary to repeat the name of the generic twice? - -**A2.** Let's look at the generic we just defined; the generic name `"age"` is repeated twice. - - -```r -setGeneric(name = "age", def = function(x) standardGeneric("age")) -``` - -This is because: - -- the `"age"` passed to argument `name` provides the name for the generic -- the `"age"` passed to argument `def` supplies the method dispatch - -This is reminiscent of how we defined `S3` generic, where we also had to repeat the name twice: - - -```r -age <- function(x) { - UseMethod("age") -} -``` - ---- - -**Q3.** Why does the `show()` method defined in Section [Show method](https://adv-r.hadley.nz/s4.html#show-method) use `is(object)[[1]]`? (Hint: try printing the employee subclass.) - -**A3.** Because we wish to define `show()` method for a specific class, we need to disregard the other super-/sub-classes. - - - -Always using the first element ensures that the method will be defined for the class in question: - - -```r -Alice <- new("Employee") - -is(Alice) -#> [1] "Employee" "Person" - -is(Alice)[[1]] -#> [1] "Employee" -``` - ---- - -**Q4.** What happens if you define a method with different argument names to the generic? - -**A4.** Let's experiment with the method we defined in **Q1.** to study this behavior. - -The original method that worked as expected since the argument name between generic and method matched: - - -```r -setMethod("age", "Person", function(x) x@age) -``` - -If this is not the case, we either get a warning or get an error depending on which and how many arguments have been specified: - - -```r -setMethod("age", "Person", function(object) object@age) -#> Warning: For function 'age', signature 'Person': argument in -#> method definition changed from (object) to (x) - -setMethod("age", "Person", function(object, x) object@age) -#> Error in rematchDefinition(definition, fdef, mnames, fnames, signature): methods can add arguments to the generic 'age' only if '...' is an argument to the generic - -setMethod("age", "Person", function(...) ...elt(1)@age) -#> Warning: For function 'age', signature 'Person': argument in -#> method definition changed from (...) to (x) - -setMethod("age", "Person", function(x, ...) x@age) -#> Error in rematchDefinition(definition, fdef, mnames, fnames, signature): methods can add arguments to the generic 'age' only if '...' is an argument to the generic -``` - ---- - -## Method dispatch (Exercises 15.5.5) - ---- - -**Q1.** Draw the method graph for `f(`πŸ˜…, 😽`)`. - -**A1.** I don't how to prepare the visual illustrations used in the book, so I am linking to the illustration in the [official solution manual](https://advanced-r-solutions.rbind.io/s4.html#method-dispatch): - - - ---- - -**Q2.** Draw the method graph for `f(`πŸ˜ƒ, πŸ˜‰, πŸ˜™`)`. - -**A2.** I don't how to prepare the visual illustrations used in the book, so I am linking to the illustration in the [official solution manual](https://advanced-r-solutions.rbind.io/s4.html#method-dispatch): - - - ---- - -**Q3.** Take the last example which shows multiple dispatch over two classes that use multiple inheritance. What happens if you define a method for all terminal classes? Why does method dispatch not save us much work here? - -**A3.** Because one class has distance of 2 to all terminal nodes and the other four have distance of 1 to two terminal nodes each, this will introduce ambiguity. - -Method dispatch not save us much work here because to resolve this ambiguity we have to define five more methods (one per class combination). - ---- - -## `S4` and `S3` (Exercises 15.6.3) - ---- - -**Q1.** What would a full `setOldClass()` definition look like for an ordered factor (i.e. add `slots` and `prototype` the definition above)? - -**A1.** We can register the old-style/`S3` `ordered` class to a formally defined class using `setOldClass()`. - - -```r -setClass("factor", - contains = "integer", - slots = c( - levels = "character" - ), - prototype = structure( - integer(), - levels = character() - ) -) -setOldClass("factor", S4Class = "factor") -#> Warning in rm(list = what, pos = classWhere): object -#> '.__C__factor' not found - -setClass("Ordered", - contains = "factor", - slots = c( - levels = "character", - ordered = "logical" - ), - prototype = structure( - integer(), - levels = character(), - ordered = logical() - ) -) - -setOldClass("ordered", S4Class = "Ordered") -``` - -Let's use it to see if it works as expected. - - -```r -x <- new("Ordered", 1L:4L, levels = letters[1:4], ordered = TRUE) - -x -#> Object of class "Ordered" -#> [1] a b c d -#> Levels: a b c d -#> Slot "ordered": -#> [1] TRUE - -str(x) -#> Formal class 'Ordered' [package ".GlobalEnv"] with 4 slots -#> ..@ .Data : int [1:4] 1 2 3 4 -#> ..@ levels : chr [1:4] "a" "b" "c" "d" -#> ..@ ordered : logi TRUE -#> ..@ .S3Class: chr "factor" - -class(x) -#> [1] "Ordered" -#> attr(,"package") -#> [1] ".GlobalEnv" -``` - ---- - -**Q2.** Define a `length` method for the `Person` class. - -**A2.** Because our `Person` class can be used to create objects that represent multiple people, let's say the `length()` method returns how many persons are in the object. - - -```r -Friends <- new("Person", name = c("Vishu", "Aditi")) -``` - -We can define an `S3` method for this class: - - -```r -length.Person <- function(x) length(x@name) - -length(Friends) -#> [1] 2 -``` - -Alternatively, we can also write `S4` method: - - -```r -setMethod("length", "Person", function(x) length(x@name)) - -length(Friends) -#> [1] 2 -``` - ---- - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0) -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1) -#> P datasets * 4.2.2 2022-10-31 [1] local -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> emo 0.0.0.9000 2022-05-17 [1] Github (hadley/emo@3f03b11) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0) -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lubridate * 1.9.0 2022-11-06 [1] CRAN (R 4.2.2) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> timechange * 0.1.1 2022-11-04 [1] CRAN (R 4.2.2) -#> P tools 4.2.2 2022-10-31 [1] local -#> P utils * 4.2.2 2022-10-31 [1] local -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` - diff --git a/_book/Subsetting.md b/_book/Subsetting.md deleted file mode 100644 index 17679a2a..00000000 --- a/_book/Subsetting.md +++ /dev/null @@ -1,476 +0,0 @@ -# Subsetting - - - -Attaching the needed libraries: - - -```r -library(tibble) -``` - -## Selecting multiple elements (Exercises 4.2.6) - -**Q1.** Fix each of the following common data frame subsetting errors: - - -```r -mtcars[mtcars$cyl = 4, ] -mtcars[-1:4, ] -mtcars[mtcars$cyl <= 5] -mtcars[mtcars$cyl == 4 | 6, ] -``` - -**A1.** Fixed versions of these commands: - - -```r -# `==` instead of `=` -mtcars[mtcars$cyl == 4, ] - -# `-(1:4)` instead of `-1:4` -mtcars[-(1:4), ] - -# `,` was missing -mtcars[mtcars$cyl <= 5, ] - -# correct subsetting syntax -mtcars[mtcars$cyl == 4 | mtcars$cyl == 6, ] -mtcars[mtcars$cyl %in% c(4, 6), ] -``` - -**Q2.** Why does the following code yield five missing values? - - -```r -x <- 1:5 -x[NA] -#> [1] NA NA NA NA NA -``` - -**A2.** This is because of two reasons: - -- The default type of `NA` in R is of `logical` type. - - -```r -typeof(NA) -#> [1] "logical" -``` - -- R recycles indexes to match the length of the vector. - - -```r -x <- 1:5 -x[c(TRUE, FALSE)] # recycled to c(TRUE, FALSE, TRUE, FALSE, TRUE) -#> [1] 1 3 5 -``` - -**Q3.** What does `upper.tri()` return? How does subsetting a matrix with it work? Do we need any additional subsetting rules to describe its behaviour? - - -```r -x <- outer(1:5, 1:5, FUN = "*") -x[upper.tri(x)] -``` - -A3. The documentation for `upper.tri()` states- - -> Returns a matrix of logicals the same size of a given matrix with entries `TRUE` in the **upper triangle** - - -```r -(x <- outer(1:5, 1:5, FUN = "*")) -#> [,1] [,2] [,3] [,4] [,5] -#> [1,] 1 2 3 4 5 -#> [2,] 2 4 6 8 10 -#> [3,] 3 6 9 12 15 -#> [4,] 4 8 12 16 20 -#> [5,] 5 10 15 20 25 - -upper.tri(x) -#> [,1] [,2] [,3] [,4] [,5] -#> [1,] FALSE TRUE TRUE TRUE TRUE -#> [2,] FALSE FALSE TRUE TRUE TRUE -#> [3,] FALSE FALSE FALSE TRUE TRUE -#> [4,] FALSE FALSE FALSE FALSE TRUE -#> [5,] FALSE FALSE FALSE FALSE FALSE -``` - -When used with a matrix for subsetting, elements corresponding to `TRUE` in the subsetting matrix are selected. But, instead of a matrix, this returns a vector: - - -```r -x[upper.tri(x)] -#> [1] 2 3 6 4 8 12 5 10 15 20 -``` - -**Q4.** Why does `mtcars[1:20]` return an error? How does it differ from the similar `mtcars[1:20, ]`? - -**A4.** When indexed like a list, data frame columns at given indices will be selected. - - -```r -head(mtcars[1:2]) -#> mpg cyl -#> Mazda RX4 21.0 6 -#> Mazda RX4 Wag 21.0 6 -#> Datsun 710 22.8 4 -#> Hornet 4 Drive 21.4 6 -#> Hornet Sportabout 18.7 8 -#> Valiant 18.1 6 -``` - -`mtcars[1:20]` doesn't work because there are only 11 columns in `mtcars` dataset. - -On the other hand, `mtcars[1:20, ]` indexes a dataframe like a matrix, and because there are indeed 20 rows in `mtcars`, all columns with these rows are selected. - - -```r -nrow(mtcars[1:20, ]) -#> [1] 20 -``` - -**Q5.** Implement your own function that extracts the diagonal entries from a matrix (it should behave like `diag(x)` where `x` is a matrix). - -**A5.** We can combine the existing functions to our advantage: - - -```r -x[!upper.tri(x) & !lower.tri(x)] -#> [1] 1 4 9 16 25 - -diag(x) -#> [1] 1 4 9 16 25 -``` - -**Q6.** What does `df[is.na(df)] <- 0` do? How does it work? - -**A6.** This expression replaces every instance of `NA` in `df` with `0`. - -`is.na(df)` produces a matrix of logical values, which provides a way of subsetting. - - -```r -(df <- tibble(x = c(1, 2, NA), y = c(NA, 5, NA))) -#> # A tibble: 3 Γ— 2 -#> x y -#> -#> 1 1 NA -#> 2 2 5 -#> 3 NA NA - -is.na(df) -#> x y -#> [1,] FALSE TRUE -#> [2,] FALSE FALSE -#> [3,] TRUE TRUE - -class(is.na(df)) -#> [1] "matrix" "array" -``` - -## Selecting a single element (Exercises 4.3.5) - -**Q1.** Brainstorm as many ways as possible to extract the third value from the `cyl` variable in the `mtcars` dataset. - -**A1.** Possible ways to to extract the third value from the `cyl` variable in the `mtcars` dataset: - - -```r -mtcars[["cyl"]][[3]] -#> [1] 4 -mtcars[[c(2, 3)]] -#> [1] 4 -mtcars[3, ][["cyl"]] -#> [1] 4 -mtcars[3, ]$cyl -#> [1] 4 -mtcars[3, "cyl"] -#> [1] 4 -mtcars[, "cyl"][[3]] -#> [1] 4 -mtcars[3, 2] -#> [1] 4 -mtcars$cyl[[3]] -#> [1] 4 -``` - -**Q2.** Given a linear model, e.g., `mod <- lm(mpg ~ wt, data = mtcars)`, extract the residual degrees of freedom. Then extract the R squared from the model summary (`summary(mod)`) - -**A2.** Given that objects of class `lm` are lists, we can use subsetting operators to extract elements we want. - - -```r -mod <- lm(mpg ~ wt, data = mtcars) -class(mod) -#> [1] "lm" -typeof(mod) -#> [1] "list" -``` - -- extracting the residual degrees of freedom - - -```r -mod$df.residual -#> [1] 30 -mod[["df.residual"]] -#> [1] 30 -``` - -- extracting the R squared from the model summary - - -```r -summary(mod)$r.squared -#> [1] 0.7528328 -summary(mod)[["r.squared"]] -#> [1] 0.7528328 -``` - -## Applications (Exercises 4.5.9) - -**Q1.** How would you randomly permute the columns of a data frame? (This is an important technique in random forests.) Can you simultaneously permute the rows and columns in one step? - -**A1.** Let's create a small data frame to work with. - - -```r -df <- head(mtcars) - -# original -df -#> mpg cyl disp hp drat wt qsec vs am -#> Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 -#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 -#> Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 -#> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 -#> Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 -#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 -#> gear carb -#> Mazda RX4 4 4 -#> Mazda RX4 Wag 4 4 -#> Datsun 710 4 1 -#> Hornet 4 Drive 3 1 -#> Hornet Sportabout 3 2 -#> Valiant 3 1 -``` - -To randomly permute the columns of a data frame, we can combine `[` and `sample()` as follows: - -- randomly permute columns - - -```r -df[sample.int(ncol(df))] -#> drat wt carb am qsec vs hp mpg disp -#> Mazda RX4 3.90 2.620 4 1 16.46 0 110 21.0 160 -#> Mazda RX4 Wag 3.90 2.875 4 1 17.02 0 110 21.0 160 -#> Datsun 710 3.85 2.320 1 1 18.61 1 93 22.8 108 -#> Hornet 4 Drive 3.08 3.215 1 0 19.44 1 110 21.4 258 -#> Hornet Sportabout 3.15 3.440 2 0 17.02 0 175 18.7 360 -#> Valiant 2.76 3.460 1 0 20.22 1 105 18.1 225 -#> cyl gear -#> Mazda RX4 6 4 -#> Mazda RX4 Wag 6 4 -#> Datsun 710 4 4 -#> Hornet 4 Drive 6 3 -#> Hornet Sportabout 8 3 -#> Valiant 6 3 -``` - -- randomly permute rows - - -```r -df[sample.int(nrow(df)), ] -#> mpg cyl disp hp drat wt qsec vs am -#> Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 -#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 -#> Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 -#> Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 -#> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 -#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 -#> gear carb -#> Datsun 710 4 1 -#> Mazda RX4 Wag 4 4 -#> Mazda RX4 4 4 -#> Hornet Sportabout 3 2 -#> Hornet 4 Drive 3 1 -#> Valiant 3 1 -``` - -- randomly permute columns and rows - - -```r -df[sample.int(nrow(df)), sample.int(ncol(df))] -#> qsec vs gear am wt drat carb disp hp -#> Mazda RX4 16.46 0 4 1 2.620 3.90 4 160 110 -#> Hornet 4 Drive 19.44 1 3 0 3.215 3.08 1 258 110 -#> Datsun 710 18.61 1 4 1 2.320 3.85 1 108 93 -#> Mazda RX4 Wag 17.02 0 4 1 2.875 3.90 4 160 110 -#> Valiant 20.22 1 3 0 3.460 2.76 1 225 105 -#> Hornet Sportabout 17.02 0 3 0 3.440 3.15 2 360 175 -#> mpg cyl -#> Mazda RX4 21.0 6 -#> Hornet 4 Drive 21.4 6 -#> Datsun 710 22.8 4 -#> Mazda RX4 Wag 21.0 6 -#> Valiant 18.1 6 -#> Hornet Sportabout 18.7 8 -``` - -**Q2.** How would you select a random sample of `m` rows from a data frame? What if the sample had to be contiguous (i.e., with an initial row, a final row, and every row in between)? - -**A2.** Let's create a small data frame to work with. - - -```r -df <- head(mtcars) - -# original -df -#> mpg cyl disp hp drat wt qsec vs am -#> Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 -#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 -#> Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 -#> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 -#> Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 -#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 -#> gear carb -#> Mazda RX4 4 4 -#> Mazda RX4 Wag 4 4 -#> Datsun 710 4 1 -#> Hornet 4 Drive 3 1 -#> Hornet Sportabout 3 2 -#> Valiant 3 1 - -# number of rows to sample -m <- 2L -``` - -To select a random sample of `m` rows from a data frame, we can combine `[` and `sample()` as follows: - -- random and non-contiguous sample of `m` rows from a data frame - - -```r -df[sample(nrow(df), m), ] -#> mpg cyl disp hp drat wt qsec vs am gear -#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 -#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 -#> carb -#> Valiant 1 -#> Mazda RX4 Wag 4 -``` - -- random and contiguous sample of `m` rows from a data frame - - -```r -# select a random starting position from available number of rows -start_row <- sample(nrow(df) - m + 1, size = 1) - -# adjust ending position while avoiding off-by-one error -end_row <- start_row + m - 1 - -df[start_row:end_row, ] -#> mpg cyl disp hp drat wt qsec vs am gear -#> Mazda RX4 21 6 160 110 3.9 2.620 16.46 0 1 4 -#> Mazda RX4 Wag 21 6 160 110 3.9 2.875 17.02 0 1 4 -#> carb -#> Mazda RX4 4 -#> Mazda RX4 Wag 4 -``` - -**Q3.** How could you put the columns in a data frame in alphabetical order? - -**A3.** we can sort columns in a data frame in the alphabetical order using `[` with `order()`: - - -```r -# columns in original order -names(mtcars) -#> [1] "mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" -#> [9] "am" "gear" "carb" - -# columns in alphabetical order -names(mtcars[order(names(mtcars))]) -#> [1] "am" "carb" "cyl" "disp" "drat" "gear" "hp" "mpg" -#> [9] "qsec" "vs" "wt" -``` - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1) -#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> tibble * 3.1.8.9002 2022-10-16 [1] local -#> P tools 4.2.2 2022-10-31 [1] local -#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0) -#> P utils * 4.2.2 2022-10-31 [1] local -#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1) -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Translation.md b/_book/Translation.md deleted file mode 100644 index 25d2588a..00000000 --- a/_book/Translation.md +++ /dev/null @@ -1,370 +0,0 @@ -# Translation - - - -Needed libraries: - - -```r -library(rlang) -library(purrr) -``` - -## HTML (Exercises 21.2.6) - ---- - -**Q1.** The escaping rules for `` so that the tag isn't closed too early. For example, `script("''")`, shouldn't generate this: - -```html - ' -``` - -But - -```html - -``` - -Adapt the `escape()` to follow these rules when a new argument `script` is set to `TRUE`. - -**A1.** Let's first start with the boilerplate code included in the book: - - -```r -escape <- function(x, ...) UseMethod("escape") - -escape.character <- function(x, script = FALSE) { - if (script) { - x <- gsub("", "<\\/script>", x, fixed = TRUE) - } else { - x <- gsub("&", "&", x) - x <- gsub("<", "<", x) - x <- gsub(">", ">", x) - } - - html(x) -} - -escape.advr_html <- function(x, ...) x -``` - -We will also need to tweak the boilerplate to pass this additional parameter to `escape()`: - - -```r -html <- function(x) structure(x, class = "advr_html") - -print.advr_html <- function(x, ...) { - out <- paste0(" ", x) - cat(paste(strwrap(out), collapse = "\n"), "\n", sep = "") -} - -dots_partition <- function(...) { - dots <- list2(...) - - if (is.null(names(dots))) { - is_named <- rep(FALSE, length(dots)) - } else { - is_named <- names(dots) != "" - } - - list( - named = dots[is_named], - unnamed = dots[!is_named] - ) -} - -tag <- function(tag, script = FALSE) { - force(script) - new_function( - exprs(... = ), - expr({ - dots <- dots_partition(...) - attribs <- html_attributes(dots$named) - children <- map_chr(.x = dots$unnamed, .f = ~ escape(.x, !!script)) - - html(paste0( - !!paste0("<", tag), attribs, ">", - paste(children, collapse = ""), - !!paste0("") - )) - }), - caller_env() - ) -} - -void_tag <- function(tag) { - new_function( - exprs(... = ), - expr({ - dots <- dots_partition(...) - if (length(dots$unnamed) > 0) { - abort(!!paste0("<", tag, "> must not have unnamed arguments")) - } - attribs <- html_attributes(dots$named) - - html(paste0(!!paste0("<", tag), attribs, " />")) - }), - caller_env() - ) -} - -p <- tag("p") -script <- tag("script", script = TRUE) -``` - - -```r -script("''") -#> -``` - ---- - -**Q2.** The use of `...` for all functions has some big downsides. There's no input validation and there will be little information in the documentation or autocomplete about how they are used in the function. Create a new function that, when given a named list of tags and their attribute names (like below), creates tag functions with named arguments. - - -```r -list( - a = c("href"), - img = c("src", "width", "height") -) -``` - -All tags should get `class` and `id` attributes. - ---- - -**Q3.** Reason about the following code that calls `with_html()` referencing objects from the environment. Will it work or fail? Why? Run the code to verify your predictions. - - -```r -greeting <- "Hello!" -with_html(p(greeting)) -p <- function() "p" -address <- "123 anywhere street" -with_html(p(address)) -``` - -**A3.** To work with this, we first need to copy-paste relevant code from the book: - - -```r -tags <- c( - "a", "abbr", "address", "article", "aside", "audio", - "b", "bdi", "bdo", "blockquote", "body", "button", "canvas", - "caption", "cite", "code", "colgroup", "data", "datalist", - "dd", "del", "details", "dfn", "div", "dl", "dt", "em", - "eventsource", "fieldset", "figcaption", "figure", "footer", - "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", - "hgroup", "html", "i", "iframe", "ins", "kbd", "label", - "legend", "li", "mark", "map", "menu", "meter", "nav", - "noscript", "object", "ol", "optgroup", "option", "output", - "p", "pre", "progress", "q", "ruby", "rp", "rt", "s", "samp", - "script", "section", "select", "small", "span", "strong", - "style", "sub", "summary", "sup", "table", "tbody", "td", - "textarea", "tfoot", "th", "thead", "time", "title", "tr", - "u", "ul", "var", "video" -) - -void_tags <- c( - "area", "base", "br", "col", "command", "embed", - "hr", "img", "input", "keygen", "link", "meta", "param", - "source", "track", "wbr" -) - -html_tags <- c( - tags %>% set_names() %>% map(tag), - void_tags %>% set_names() %>% map(void_tag) -) - -with_html <- function(code) { - code <- enquo(code) - eval_tidy(code, html_tags) -} -``` - -Note that `with_html()` uses `eval_tidy()`, and therefore `code` argument is evaluated first in the `html_tags` named list, which acts as a data mask, and if no object is found in the data mask, searches in the caller environment. - -For this reason, the first example code will work: - - -```r -greeting <- "Hello!" -with_html(p(greeting)) -#>

Hello!

-``` - -The following code, however, is not going to work because there is already `address` element in the data mask, and so `p()` will take a function `address()` as an input, and `escape()` doesn't know how to deal with objects of `function` type: - - -```r -"address" %in% names(html_tags) -#> [1] TRUE - -p <- function() "p" -address <- "123 anywhere street" -with_html(p(address)) -#> Error in UseMethod("escape"): no applicable method for 'escape' applied to an object of class "function" -``` - ---- - -**Q4.** Currently the HTML doesn't look terribly pretty, and it's hard to see the structure. How could you adapt `tag()` to do indenting and formatting? (You may need to do some research into block and inline tags.) - -**A4.** Let's first have a look at what it currently looks like: - - -```r -with_html( - body( - h1("A heading", id = "first"), - p("Some text &", b("some bold text.")), - img(src = "myimg.png", width = 100, height = 100) - ) -) -#>

A heading

Some -#> text &some bold text.

src='myimg.png' width='100' height='100' /> -``` - -We can improve this to follow the [Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.html#HTML_Formatting_Rules). - -For this, we need to create a new function to indent the code conditionally: - - -```r -print.advr_html <- function(x, ...) { - cat(paste("", x, sep = "\n")) -} - -indent <- function(x) { - paste0(" ", gsub("\n", "\n ", x)) -} - -format_code <- function(children, indent = FALSE) { - if (indent) { - paste0("\n", paste0(indent(children), collapse = "\n"), "\n") - } else { - paste(children, collapse = "") - } -} -``` - -We can then update the `body()` function to use this new helper: - - -```r -html_tags$body <- function(...) { - dots <- dots_partition(...) - attribs <- html_attributes(dots$named) - children <- map_chr(dots$unnamed, escape) - - html(paste0( - "", - format_code(children, indent = TRUE), - "" - )) -} -``` - -The new formatting looks much better: - - -```r -with_html( - body( - h1("A heading", id = "first"), - p("Some text &", b("some bold text.")), - img(src = "myimg.png", width = 100, height = 100) - ) -) -#> -#> -#>

A heading

-#>

Some text &some bold text.

-#> -#> -``` - ---- - -## LaTeX (Exercises 21.3.8) - -I didn't manage to solve these exercises, and so I'd recommend checking out the solutions in the [official solutions manual](https://advanced-r-solutions.rbind.io/translating-r-code.html#latex). - ---- - -**Q1.** Add escaping. The special symbols that should be escaped by adding a backslash in front of them are `\`, `$`, and `%`. Just as with HTML, you'll need to make sure you don't end up double-escaping. So you'll need to create a small S3 class and then use that in function operators. That will also allow you to embed arbitrary LaTeX if needed. - ---- - -**Q2.** Complete the DSL to support all the functions that `plotmath` supports. - ---- - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> P utils * 4.2.2 2022-10-31 [1] local -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/Vectors.md b/_book/Vectors.md deleted file mode 100644 index 1337b3c0..00000000 --- a/_book/Vectors.md +++ /dev/null @@ -1,887 +0,0 @@ -# Vectors - - - -## Atomic vectors (Exercises 3.2.5) - -**Q1.** How do you create raw and complex scalars? (See `?raw` and `?complex`.) - -**A1.** In R, scalars are nothing but vectors of length 1, and can be created using the same constructor. - -- Raw vectors - -The raw type holds raw bytes, and can be created using `charToRaw()`. For example, - - -```r -x <- "A string" - -(y <- charToRaw(x)) -#> [1] 41 20 73 74 72 69 6e 67 - -typeof(y) -#> [1] "raw" -``` - -An alternative is to use `as.raw()`: - - -```r -as.raw("–") # en-dash -#> Warning: NAs introduced by coercion -#> Warning: out-of-range values treated as 0 in coercion to raw -#> [1] 00 -as.raw("β€”") # em-dash -#> Warning: NAs introduced by coercion - -#> Warning: out-of-range values treated as 0 in coercion to raw -#> [1] 00 -``` - -- Complex vectors - -Complex vectors are used to represent (surprise!) complex numbers. - -Example of a complex scalar: - - -```r -(x <- complex(length.out = 1, real = 1, imaginary = 8)) -#> [1] 1+8i - -typeof(x) -#> [1] "complex" -``` - -**Q2.** Test your knowledge of the vector coercion rules by predicting the output of the following uses of `c()`: - - -```r -c(1, FALSE) -c("a", 1) -c(TRUE, 1L) -``` - -**A2.** The vector coercion rules dictate that the data type with smaller size will be converted to data type with bigger size. - - -```r -c(1, FALSE) -#> [1] 1 0 - -c("a", 1) -#> [1] "a" "1" - -c(TRUE, 1L) -#> [1] 1 1 -``` - -**Q3.** Why is `1 == "1"` true? Why is `-1 < FALSE` true? Why is `"one" < 2` false? - -**A3.** The coercion rules for vectors reveal why some of these comparisons return the results that they do. - - -```r -1 == "1" -#> [1] TRUE - -c(1, "1") -#> [1] "1" "1" -``` - - -```r --1 < FALSE -#> [1] TRUE - -c(-1, FALSE) -#> [1] -1 0 -``` - - -```r -"one" < 2 -#> [1] FALSE - -c("one", 2) -#> [1] "one" "2" - -sort(c("one", 2)) -#> [1] "2" "one" -``` - -**Q4.** Why is the default missing value, `NA`, a logical vector? What's special about logical vectors? (Hint: think about `c(FALSE, NA_character_)`.) - -**A4.** The `"logical"` type is the lowest in the coercion hierarchy. - -So `NA` defaulting to any other type (e.g. `"numeric"`) would mean that any time there is a missing element in a vector, rest of the elements would be converted to a type higher in hierarchy, which would be problematic for types lower in hierarchy. - - -```r -typeof(NA) -#> [1] "logical" - -c(FALSE, NA_character_) -#> [1] "FALSE" NA -``` - -**Q5.** Precisely what do `is.atomic()`, `is.numeric()`, and `is.vector()` test for? - -**A5.** Let's discuss them one-by-one. - -- `is.atomic()` - -This function checks if the object is a vector of atomic *type* (or `NULL`). - -Quoting docs: - -> `is.atomic` is true for the atomic types ("logical", "integer", "numeric", "complex", "character" and "raw") and `NULL`. - - -```r -is.atomic(NULL) -#> [1] TRUE - -is.atomic(list(NULL)) -#> [1] FALSE -``` - -- `is.numeric()` - -Its documentation says: - -> `is.numeric` should only return true if the base type of the class is `double` or `integer` and values can reasonably be regarded as `numeric` - -Therefore, this function only checks for `double` and `integer` base types and not other types based on top of these types (`factor`, `Date`, `POSIXt`, or `difftime`). - - -```r -is.numeric(1L) -#> [1] TRUE - -is.numeric(factor(1L)) -#> [1] FALSE -``` - -- `is.vector()` - -As per its documentation: - -> `is.vector` returns `TRUE` if `x` is a vector of the specified mode having no attributes *other than names*. It returns `FALSE` otherwise. - -Thus, the function can be incorrectif the object has attributes other than `names`. - - -```r -x <- c("x" = 1, "y" = 2) - -is.vector(x) -#> [1] TRUE - -attr(x, "m") <- "abcdef" - -is.vector(x) -#> [1] FALSE -``` - -A better way to check for a vector: - - -```r -is.null(dim(x)) -#> [1] TRUE -``` - -## Attributes (Exercises 3.3.4) - -**Q1.** How is `setNames()` implemented? How is `unname()` implemented? Read the source code. - -**A1.** Let's have a look at implementations for these functions. - -- `setNames()` - - -```r -setNames -#> function (object = nm, nm) -#> { -#> names(object) <- nm -#> object -#> } -#> -#> -``` - -Given this function signature, we can see why, when no first argument is given, the result is still a named vector. - - -```r -setNames(, c("a", "b")) -#> a b -#> "a" "b" - -setNames(c(1, 2), c("a", "b")) -#> a b -#> 1 2 -``` - -- `unname()` - - -```r -unname -#> function (obj, force = FALSE) -#> { -#> if (!is.null(names(obj))) -#> names(obj) <- NULL -#> if (!is.null(dimnames(obj)) && (force || !is.data.frame(obj))) -#> dimnames(obj) <- NULL -#> obj -#> } -#> -#> -``` - -`unname()` removes existing names (or dimnames) by setting them to `NULL`. - - -```r -unname(setNames(, c("a", "b"))) -#> [1] "a" "b" -``` - -**Q2.** What does `dim()` return when applied to a 1-dimensional vector? When might you use `NROW()` or `NCOL()`? - -**A2.** Dimensions for a 1-dimensional vector are `NULL`. For example, - - -```r -dim(c(1, 2)) -#> NULL -``` - - -`NROW()` and `NCOL()` are helpful for getting dimensions for 1D vectors by treating them as if they were matrices or dataframes. - - -```r -# example-1 -x <- character(0) - -dim(x) -#> NULL - -nrow(x) -#> NULL -NROW(x) -#> [1] 0 - -ncol(x) -#> NULL -NCOL(x) -#> [1] 1 - -# example-2 -y <- 1:4 - -dim(y) -#> NULL - -nrow(y) -#> NULL -NROW(y) -#> [1] 4 - -ncol(y) -#> NULL -NCOL(y) -#> [1] 1 -``` - -**Q3.** How would you describe the following three objects? What makes them different from `1:5`? - - -```r -x1 <- array(1:5, c(1, 1, 5)) -x2 <- array(1:5, c(1, 5, 1)) -x3 <- array(1:5, c(5, 1, 1)) -``` - -**A3.** `x1`, `x2`, and `x3` are one-dimensional **array**s, but with different "orientations", if we were to mentally visualize them. - -`x1` has 5 entries in the third dimension, `x2` in the second dimension, while `x1` in the first dimension. - -**Q4.** An early draft used this code to illustrate `structure()`: - - -```r -structure(1:5, comment = "my attribute") -#> [1] 1 2 3 4 5 -``` - -But when you print that object you don't see the comment attribute. Why? Is the attribute missing, or is there something else special about it? (Hint: try using help.) - -**A4.** From `?attributes` (emphasis mine): - -> Note that some attributes (namely class, **comment**, dim, dimnames, names, row.names and tsp) are treated specially and have restrictions on the values which can be set. - - -```r -structure(1:5, x = "my attribute") -#> [1] 1 2 3 4 5 -#> attr(,"x") -#> [1] "my attribute" - -structure(1:5, comment = "my attribute") -#> [1] 1 2 3 4 5 -``` - -## S3 atomic vectors (Exercises 3.4.5) - -**Q1.** What sort of object does `table()` return? What is its type? What attributes does it have? How does the dimensionality change as you tabulate more variables? - -**A1.** `table()` returns an array of `integer` type and its dimensions scale with the number of variables present. - - -```r -(x <- table(mtcars$am)) -#> -#> 0 1 -#> 19 13 -(y <- table(mtcars$am, mtcars$cyl)) -#> -#> 4 6 8 -#> 0 3 4 12 -#> 1 8 3 2 -(z <- table(mtcars$am, mtcars$cyl, mtcars$vs)) -#> , , = 0 -#> -#> -#> 4 6 8 -#> 0 0 0 12 -#> 1 1 3 2 -#> -#> , , = 1 -#> -#> -#> 4 6 8 -#> 0 3 4 0 -#> 1 7 0 0 - -# type -purrr::map(list(x, y, z), typeof) -#> [[1]] -#> [1] "integer" -#> -#> [[2]] -#> [1] "integer" -#> -#> [[3]] -#> [1] "integer" - -# attributes -purrr::map(list(x, y, z), attributes) -#> [[1]] -#> [[1]]$dim -#> [1] 2 -#> -#> [[1]]$dimnames -#> [[1]]$dimnames[[1]] -#> [1] "0" "1" -#> -#> -#> [[1]]$class -#> [1] "table" -#> -#> -#> [[2]] -#> [[2]]$dim -#> [1] 2 3 -#> -#> [[2]]$dimnames -#> [[2]]$dimnames[[1]] -#> [1] "0" "1" -#> -#> [[2]]$dimnames[[2]] -#> [1] "4" "6" "8" -#> -#> -#> [[2]]$class -#> [1] "table" -#> -#> -#> [[3]] -#> [[3]]$dim -#> [1] 2 3 2 -#> -#> [[3]]$dimnames -#> [[3]]$dimnames[[1]] -#> [1] "0" "1" -#> -#> [[3]]$dimnames[[2]] -#> [1] "4" "6" "8" -#> -#> [[3]]$dimnames[[3]] -#> [1] "0" "1" -#> -#> -#> [[3]]$class -#> [1] "table" -``` - -**Q2.** What happens to a factor when you modify its levels? - - -```r -f1 <- factor(letters) -levels(f1) <- rev(levels(f1)) -``` - -**A2.** Its levels change but the underlying integer values remain the same. - - -```r -f1 <- factor(letters) -f1 -#> [1] a b c d e f g h i j k l m n o p q r s t u v w x y z -#> 26 Levels: a b c d e f g h i j k l m n o p q r s t u ... z -as.integer(f1) -#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -#> [19] 19 20 21 22 23 24 25 26 - -levels(f1) <- rev(levels(f1)) -f1 -#> [1] z y x w v u t s r q p o n m l k j i h g f e d c b a -#> 26 Levels: z y x w v u t s r q p o n m l k j i h g f ... a -as.integer(f1) -#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -#> [19] 19 20 21 22 23 24 25 26 -``` - -**Q3.** What does this code do? How do `f2` and `f3` differ from `f1`? - - -```r -f2 <- rev(factor(letters)) -f3 <- factor(letters, levels = rev(letters)) -``` - -**A3.** In this code: - -- `f2`: Only the underlying integers are reversed, but levels remain unchanged. - - -```r -f2 <- rev(factor(letters)) -f2 -#> [1] z y x w v u t s r q p o n m l k j i h g f e d c b a -#> 26 Levels: a b c d e f g h i j k l m n o p q r s t u ... z -as.integer(f2) -#> [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 -#> [19] 8 7 6 5 4 3 2 1 -``` - -- `f3`: Both the levels and the underlying integers are reversed. - - -```r -f3 <- factor(letters, levels = rev(letters)) -f3 -#> [1] a b c d e f g h i j k l m n o p q r s t u v w x y z -#> 26 Levels: z y x w v u t s r q p o n m l k j i h g f ... a -as.integer(f3) -#> [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 -#> [19] 8 7 6 5 4 3 2 1 -``` - -## Lists (Exercises 3.5.4) - -**Q1.** List all the ways that a list differs from an atomic vector. - -**A1.** Here is a table of comparison: - -| feature | atomic vector | list (aka generic vector) | -| :----------------------------- | :----------------------------------------------- | :----------------------------------------------------- | -| element type | unique | mixed^[a list can contain a mix of types] | -| recursive? | no | yes^[a list can contain itself] | -| return for out-of-bounds index | `NA` | `NULL` | -| memory address | single memory reference^[`lobstr::ref(c(1, 2))`] | reference per list element^[`lobstr::ref(list(1, 2))`] | - -**Q2.** Why do you need to use `unlist()` to convert a list to an atomic vector? Why doesn't `as.vector()` work? - -**A2.** A list already *is* a (generic) vector, so `as.vector()` is not going to change anything, and there is no `as.atomic.vector`. Thus, we need to use `unlist()`. - - -```r -x <- list(a = 1, b = 2) - -is.vector(x) -#> [1] TRUE -is.atomic(x) -#> [1] FALSE - -# still a list -as.vector(x) -#> $a -#> [1] 1 -#> -#> $b -#> [1] 2 - -# now a vector -unlist(x) -#> a b -#> 1 2 -``` - -**Q3.** Compare and contrast `c()` and `unlist()` when combining a date and date-time into a single vector. - -**A3.** Let's first create a date and datetime object - - -```r -date <- as.Date("1947-08-15") -datetime <- as.POSIXct("1950-01-26 00:01", tz = "UTC") -``` - -And check their attributes and underlying `double` representation: - - -```r -attributes(date) -#> $class -#> [1] "Date" -attributes(datetime) -#> $class -#> [1] "POSIXct" "POSIXt" -#> -#> $tzone -#> [1] "UTC" - -as.double(date) # number of days since the Unix epoch 1970-01-01 -#> [1] -8175 -as.double(datetime) # number of seconds since then -#> [1] -628991940 -``` - -- Behavior with `c()` - -Since `S3` method for `c()` dispatches on the first argument, the resulting class of the vector is going to be the same as the first argument. Because of this, some attributes will be lost. - - -```r -c(date, datetime) -#> [1] "1947-08-15" "1950-01-26" - -attributes(c(date, datetime)) -#> $class -#> [1] "Date" - -c(datetime, date) -#> [1] "1950-01-26 01:01:00 CET" "1947-08-15 02:00:00 CEST" - -attributes(c(datetime, date)) -#> $class -#> [1] "POSIXct" "POSIXt" -``` - -- Behavior with `unlist()` - -It removes all attributes and we are left only with the underlying double representations of these objects. - - -```r -unlist(list(date, datetime)) -#> [1] -8175 -628991940 - -unlist(list(datetime, date)) -#> [1] -628991940 -8175 -``` - -## Data frames and tibbles (Exercises 3.6.8) - -**Q1.** Can you have a data frame with zero rows? What about zero columns? - -**A1.** Data frame with 0 rows is possible. This is basically a list with a vector of length 0. - - -```r -data.frame(x = numeric(0)) -#> [1] x -#> <0 rows> (or 0-length row.names) -``` - -Data frame with 0 columns is also possible. This will be an empty list. - - -```r -data.frame(row.names = 1) -#> data frame with 0 columns and 1 row -``` - -And, finally, data frame with 0 rows *and* columns is also possible: - - -```r -data.frame() -#> data frame with 0 columns and 0 rows - -dim(data.frame()) -#> [1] 0 0 -``` - -Although, it might not be common to *create* such data frames, they can be results of subsetting. For example, - - -```r -BOD[0, ] -#> [1] Time demand -#> <0 rows> (or 0-length row.names) - -BOD[, 0] -#> data frame with 0 columns and 6 rows - -BOD[0, 0] -#> data frame with 0 columns and 0 rows -``` - -**Q2.** What happens if you attempt to set rownames that are not unique? - -**A2.** If you attempt to set data frame rownames that are not unique, it will not work. - - -```r -data.frame(row.names = c(1, 1)) -#> Error in data.frame(row.names = c(1, 1)): duplicate row.names: 1 -``` - -**Q3.** If `df` is a data frame, what can you say about `t(df)`, and `t(t(df))`? Perform some experiments, making sure to try different column types. - -**A3.** Transposing a data frame: - -- transforms it into a matrix -- coerces all its elements to be of the same type - - -```r -# original -(df <- head(iris)) -#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species -#> 1 5.1 3.5 1.4 0.2 setosa -#> 2 4.9 3.0 1.4 0.2 setosa -#> 3 4.7 3.2 1.3 0.2 setosa -#> 4 4.6 3.1 1.5 0.2 setosa -#> 5 5.0 3.6 1.4 0.2 setosa -#> 6 5.4 3.9 1.7 0.4 setosa - -# transpose -t(df) -#> 1 2 3 4 5 -#> Sepal.Length "5.1" "4.9" "4.7" "4.6" "5.0" -#> Sepal.Width "3.5" "3.0" "3.2" "3.1" "3.6" -#> Petal.Length "1.4" "1.4" "1.3" "1.5" "1.4" -#> Petal.Width "0.2" "0.2" "0.2" "0.2" "0.2" -#> Species "setosa" "setosa" "setosa" "setosa" "setosa" -#> 6 -#> Sepal.Length "5.4" -#> Sepal.Width "3.9" -#> Petal.Length "1.7" -#> Petal.Width "0.4" -#> Species "setosa" - -# transpose of a transpose -t(t(df)) -#> Sepal.Length Sepal.Width Petal.Length Petal.Width -#> 1 "5.1" "3.5" "1.4" "0.2" -#> 2 "4.9" "3.0" "1.4" "0.2" -#> 3 "4.7" "3.2" "1.3" "0.2" -#> 4 "4.6" "3.1" "1.5" "0.2" -#> 5 "5.0" "3.6" "1.4" "0.2" -#> 6 "5.4" "3.9" "1.7" "0.4" -#> Species -#> 1 "setosa" -#> 2 "setosa" -#> 3 "setosa" -#> 4 "setosa" -#> 5 "setosa" -#> 6 "setosa" - -# is it a dataframe? -is.data.frame(df) -#> [1] TRUE -is.data.frame(t(df)) -#> [1] FALSE -is.data.frame(t(t(df))) -#> [1] FALSE - -# check type -typeof(df) -#> [1] "list" -typeof(t(df)) -#> [1] "character" -typeof(t(t(df))) -#> [1] "character" - -# check dimensions -dim(df) -#> [1] 6 5 -dim(t(df)) -#> [1] 5 6 -dim(t(t(df))) -#> [1] 6 5 -``` - -**Q4.** What does `as.matrix()` do when applied to a data frame with columns of different types? How does it differ from `data.matrix()`? - -**A4.** The return type of `as.matrix()` depends on the data frame column types. - -As docs for `as.matrix()` mention: - -> The method for data frames will return a character matrix if there is only atomic columns and any non-(numeric/logical/complex) column, applying as.vector to factors and format to other non-character columns. Otherwise the usual coercion hierarchy (logical < integer < double < complex) will be used, e.g. all-logical data frames will be coerced to a logical matrix, mixed logical-integer will give an integer matrix, etc. - -Let's experiment: - - -```r -# example with mixed types (coerced to character) -(df <- head(iris)) -#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species -#> 1 5.1 3.5 1.4 0.2 setosa -#> 2 4.9 3.0 1.4 0.2 setosa -#> 3 4.7 3.2 1.3 0.2 setosa -#> 4 4.6 3.1 1.5 0.2 setosa -#> 5 5.0 3.6 1.4 0.2 setosa -#> 6 5.4 3.9 1.7 0.4 setosa - -as.matrix(df) -#> Sepal.Length Sepal.Width Petal.Length Petal.Width -#> 1 "5.1" "3.5" "1.4" "0.2" -#> 2 "4.9" "3.0" "1.4" "0.2" -#> 3 "4.7" "3.2" "1.3" "0.2" -#> 4 "4.6" "3.1" "1.5" "0.2" -#> 5 "5.0" "3.6" "1.4" "0.2" -#> 6 "5.4" "3.9" "1.7" "0.4" -#> Species -#> 1 "setosa" -#> 2 "setosa" -#> 3 "setosa" -#> 4 "setosa" -#> 5 "setosa" -#> 6 "setosa" - -str(as.matrix(df)) -#> chr [1:6, 1:5] "5.1" "4.9" "4.7" "4.6" "5.0" "5.4" ... -#> - attr(*, "dimnames")=List of 2 -#> ..$ : chr [1:6] "1" "2" "3" "4" ... -#> ..$ : chr [1:5] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" ... - -# another example (no such coercion) -BOD -#> Time demand -#> 1 1 8.3 -#> 2 2 10.3 -#> 3 3 19.0 -#> 4 4 16.0 -#> 5 5 15.6 -#> 6 7 19.8 - -as.matrix(BOD) -#> Time demand -#> [1,] 1 8.3 -#> [2,] 2 10.3 -#> [3,] 3 19.0 -#> [4,] 4 16.0 -#> [5,] 5 15.6 -#> [6,] 7 19.8 -``` - -On the other hand, `data.matrix()` always returns a numeric matrix. - -From documentation of `data.matrix()`: - -> Return the matrix obtained by converting all the variables in a data frame to numeric mode and then binding them together as the columns of a matrix. Factors and ordered factors are replaced by their internal codes. [...] Character columns are first converted to factors and then to integers. - -Let's experiment: - - -```r -data.matrix(df) -#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species -#> 1 5.1 3.5 1.4 0.2 1 -#> 2 4.9 3.0 1.4 0.2 1 -#> 3 4.7 3.2 1.3 0.2 1 -#> 4 4.6 3.1 1.5 0.2 1 -#> 5 5.0 3.6 1.4 0.2 1 -#> 6 5.4 3.9 1.7 0.4 1 - -str(data.matrix(df)) -#> num [1:6, 1:5] 5.1 4.9 4.7 4.6 5 5.4 3.5 3 3.2 3.1 ... -#> - attr(*, "dimnames")=List of 2 -#> ..$ : chr [1:6] "1" "2" "3" "4" ... -#> ..$ : chr [1:5] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" ... -``` - -## Session information - - -```r -sessioninfo::session_info(include_base = TRUE) -#> ─ Session info ─────────────────────────────────────────── -#> setting value -#> version R version 4.2.2 (2022-10-31) -#> os macOS Ventura 13.0 -#> system aarch64, darwin20 -#> ui X11 -#> language (EN) -#> collate en_US.UTF-8 -#> ctype en_US.UTF-8 -#> tz Europe/Berlin -#> date 2022-11-12 -#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown) -#> -#> ─ Packages ─────────────────────────────────────────────── -#> ! package * version date (UTC) lib source -#> base * 4.2.2 2022-10-31 [?] local -#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2) -#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2) -#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0) -#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0) -#> P compiler 4.2.2 2022-10-31 [1] local -#> P datasets * 4.2.2 2022-10-31 [1] local -#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1) -#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1) -#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2) -#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0) -#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0) -#> P graphics * 4.2.2 2022-10-31 [1] local -#> P grDevices * 4.2.2 2022-10-31 [1] local -#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1) -#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0) -#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1) -#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1) -#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) -#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) -#> P methods * 4.2.2 2022-10-31 [1] local -#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.1) -#> R6 2.5.1.9000 2022-10-27 [1] local -#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1) -#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2) -#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1) -#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1) -#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) -#> P stats * 4.2.2 2022-10-31 [1] local -#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1) -#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1) -#> P tools 4.2.2 2022-10-31 [1] local -#> P utils * 4.2.2 2022-10-31 [1] local -#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0) -#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1) -#> xml2 1.3.3.9000 2022-10-10 [1] local -#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1) -#> -#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library -#> -#> P ── Loaded and on-disk path mismatch. -#> -#> ────────────────────────────────────────────────────────── -``` diff --git a/_book/base-types.html b/_book/base-types.html deleted file mode 100644 index 342cba7f..00000000 --- a/_book/base-types.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - -Chapter 12 Base Types | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/base-types.md b/_book/base-types.md deleted file mode 100644 index f42ceee6..00000000 --- a/_book/base-types.md +++ /dev/null @@ -1,3 +0,0 @@ -# Base Types - -No exercises. diff --git a/_book/big-picture.html b/_book/big-picture.html deleted file mode 100644 index 7ed71c80..00000000 --- a/_book/big-picture.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - -Chapter 17 Big Picture | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/conditions.html b/_book/conditions.html deleted file mode 100644 index 543f0e05..00000000 --- a/_book/conditions.html +++ /dev/null @@ -1,769 +0,0 @@ - - - - - - -Chapter 8 Conditions | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-8 Conditions -

-

Attaching the needed libraries:

-
-library(rlang, warn.conflicts = FALSE)
-library(testthat, warn.conflicts = FALSE)
-
-

-8.1 Signalling conditions (Exercises 8.2.4) -

-
-

Q1. Write a wrapper around file.remove() that throws an error if the file to be deleted does not exist.

-

A1. Let’s first create a wrapper function around file.remove() that throws an error if the file to be deleted does not exist.

-
-fileRemove <- function(...) {
-  existing_files <- fs::file_exists(...)
-
-  if (!all(existing_files)) {
-    stop(
-      cat(
-        "The following files to be deleted don't exist:",
-        names(existing_files[!existing_files]),
-        sep = "\n"
-      ),
-      call. = FALSE
-    )
-  }
-
-  file.remove(...)
-}
-

Let’s first create a file that we can delete immediately.

-
-fs::file_create("random.R")
-

The function should fail if there are any other files provided that don’t exist:

-
-fileRemove(c("random.R", "XYZ.csv"))
-#> The following files to be deleted don't exist:
-#> XYZ.csv
-#> Error:
-

But it does work as expected when the file exists:

-
-fileRemove("random.R")
-#> [1] TRUE
-
-

Q2. What does the appendLF argument to message() do? How is it related to cat()?

-

A2. As mentioned in the docs for message(), appendLF argument decides:

-
-

should messages given as a character string have a newline appended?

-
-
    -
  • If TRUE (default value), a final newline is regarded as part of the message:
  • -
-
-foo <- function(appendLF) {
-  message("Beetle", appendLF = appendLF)
-  message("Juice", appendLF = appendLF)
-}
-
-foo(appendLF = TRUE)
-#> Beetle
-#> Juice
-
    -
  • If FALSE, messages will be concatenated:
  • -
-
-foo <- function(appendLF) {
-  message("Beetle", appendLF = appendLF)
-  message("Juice", appendLF = appendLF)
-}
-
-foo(appendLF = FALSE)
-#> BeetleJuice
-

On the other hand, cat() converts its arguments to character vectors and concatenates them to a single character vector by default:

-
-foo <- function() {
-  cat("Beetle")
-  cat("Juice")
-}
-
-foo()
-#> BeetleJuice
-

In order to get message()-like default behavior for outputs, we can set sep = "\n":

-
-foo <- function() {
-  cat("Beetle", sep = "\n")
-  cat("Juice", sep = "\n")
-}
-
-foo()
-#> Beetle
-#> Juice
-
-
-
-

-8.2 Handling conditions (Exercises 8.4.5) -

-
-

Q1. What extra information does the condition generated by abort() contain compared to the condition generated by stop() i.e.Β what’s the difference between these two objects? Read the help for ?abort to learn more.

-
-catch_cnd(stop("An error"))
-catch_cnd(abort("An error"))
-

A1. Compared to base::stop(), rlang::abort() contains two additional pieces of information:

-
    -
  • -trace: A traceback capturing the sequence of calls that lead to the current function
  • -
  • -parent: Information about another condition used as a parent to create a chained condition.
  • -
-
-library(rlang)
-
-stopInfo <- catch_cnd(stop("An error"))
-abortInfo <- catch_cnd(abort("An error"))
-
-str(stopInfo)
-#> List of 2
-#>  $ message: chr "An error"
-#>  $ call   : language force(expr)
-#>  - attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
-
-str(abortInfo)
-#> List of 4
-#>  $ message: chr "An error"
-#>  $ trace  :Classes 'rlang_trace', 'rlib_trace', 'tbl' and 'data.frame':  8 obs. of  6 variables:
-#>   ..$ call       :List of 8
-#>   .. ..$ : language catch_cnd(abort("An error"))
-#>   .. ..$ : language eval_bare(rlang::expr(tryCatch(!!!handlers, {     force(expr) ...
-#>   .. ..$ : language tryCatch(condition = `<fn>`, {     force(expr) ...
-#>   .. ..$ : language tryCatchList(expr, classes, parentenv, handlers)
-#>   .. ..$ : language tryCatchOne(expr, names, parentenv, handlers[[1L]])
-#>   .. ..$ : language doTryCatch(return(expr), name, parentenv, handler)
-#>   .. ..$ : language force(expr)
-#>   .. ..$ : language abort("An error")
-#>   ..$ parent     : int [1:8] 0 1 1 3 4 5 1 0
-#>   ..$ visible    : logi [1:8] FALSE FALSE FALSE FALSE FALSE FALSE ...
-#>   ..$ namespace  : chr [1:8] "rlang" "rlang" "base" "base" ...
-#>   ..$ scope      : chr [1:8] "::" "::" "::" "local" ...
-#>   ..$ error_frame: logi [1:8] FALSE FALSE FALSE FALSE FALSE FALSE ...
-#>   ..- attr(*, "version")= int 2
-#>  $ parent : NULL
-#>  $ call   : NULL
-#>  - attr(*, "class")= chr [1:3] "rlang_error" "error" "condition"
-
-

Q2. Predict the results of evaluating the following code

-
-show_condition <- function(code) {
-  tryCatch(
-    error = function(cnd) "error",
-    warning = function(cnd) "warning",
-    message = function(cnd) "message",
-    {
-      code
-      NULL
-    }
-  )
-}
-
-show_condition(stop("!"))
-show_condition(10)
-show_condition(warning("?!"))
-show_condition({
-  10
-  message("?")
-  warning("?!")
-})
-

A2. Correctly predicted πŸ˜‰

-

The first three pieces of code are straightforward:

-
-show_condition <- function(code) {
-  tryCatch(
-    error = function(cnd) "error",
-    warning = function(cnd) "warning",
-    message = function(cnd) "message",
-    {
-      code
-      NULL
-    }
-  )
-}
-
-show_condition(stop("!"))
-#> [1] "error"
-show_condition(10)
-#> NULL
-show_condition(warning("?!"))
-#> [1] "warning"
-

The last piece of code is the challenging one and it illustrates how tryCatch() works. From its docs:

-
-

When several handlers are supplied in a single tryCatch then the first one is considered more recent than the second.

-
-
-show_condition({
-  10
-  message("?")
-  warning("?!")
-})
-#> [1] "message"
-
-

Q3. Explain the results of running this code:

-
-withCallingHandlers(
-  message = function(cnd) message("b"),
-  withCallingHandlers(
-    message = function(cnd) message("a"),
-    message("c")
-  )
-)
-#> b
-#> a
-#> b
-#> c
-

A3. The surprising part of this output is the b before the last c.

-

This happens because the inner calling handler doesn’t handle the message, so it bubbles up to the outer calling handler.

-
-

Q4. Read the source code for catch_cnd() and explain how it works.

-

A4. Let’s look at the source code for catch_cnd():

-
-rlang::catch_cnd
-#> function (expr, classes = "condition") 
-#> {
-#>     stopifnot(is_character(classes))
-#>     handlers <- rep_named(classes, list(identity))
-#>     eval_bare(rlang::expr(tryCatch(!!!handlers, {
-#>         force(expr)
-#>         return(NULL)
-#>     })))
-#> }
-#> <bytecode: 0x1374a4498>
-#> <environment: namespace:rlang>
-

As mentioned in the function docs:

-
-

This is a small wrapper around tryCatch() that captures any condition signalled while evaluating its argument.

-
-

The classes argument allows a character vector of condition classes to catch, and the complex tidy evaluation generates the necessary condition (if there is any; otherwise NULL).

-
-catch_cnd(10)
-#> NULL
-
-catch_cnd(abort(message = "an error", class = "class1"))
-#> <error/class1>
-#> Error:
-#> ! an error
-#> ---
-#> Backtrace:
-
-

Q5. How could you rewrite show_condition() to use a single handler?

-

A5. The source code for rlang::catch_cond() gives us a clue as to how we can do this.

-

Conditions also have a class attribute, and we can use it to determine which handler will match the condition.

-
-show_condition2 <- function(code) {
-  tryCatch(
-    condition = function(cnd) {
-      if (inherits(cnd, "error")) {
-        return("error")
-      }
-      if (inherits(cnd, "warning")) {
-        return("warning")
-      }
-      if (inherits(cnd, "message")) {
-        return("message")
-      }
-    },
-    {
-      code
-      NULL
-    }
-  )
-}
-

Let’s try this new version with the examples used for the original version:

-
-show_condition2(stop("!"))
-#> [1] "error"
-show_condition2(10)
-#> NULL
-show_condition2(warning("?!"))
-#> [1] "warning"
-show_condition2({
-  10
-  message("?")
-  warning("?!")
-})
-#> [1] "message"
-
-
-
-

-8.3 Custom conditions (Exercises 8.5.4) -

-
-

Q1. Inside a package, it’s occasionally useful to check that a package is installed before using it. Write a function that checks if a package is installed (with requireNamespace("pkg", quietly = FALSE)) and if not, throws a custom condition that includes the package name in the metadata.

-

A1. Here is the desired function:

-
-abort_missing_package <- function(pkg) {
-  msg <- glue::glue("Problem loading `{pkg}` package, which is missing and must be installed.")
-
-  abort("error_missing_package",
-    message = msg,
-    pkg = pkg
-  )
-}
-
-check_if_pkg_installed <- function(pkg) {
-  if (!requireNamespace(pkg, quietly = TRUE)) {
-    abort_missing_package(pkg)
-  }
-
-  TRUE
-}
-
-check_if_pkg_installed("xyz123")
-#> Error in `abort_missing_package()`:
-#> ! Problem loading `xyz123` package, which is missing and must be installed.
-check_if_pkg_installed("dplyr")
-#> [1] TRUE
-

For a reference, also see the source code for following functions:

- -
-

Q2. Inside a package you often need to stop with an error when something is not right. Other packages that depend on your package might be tempted to check these errors in their unit tests. How could you help these packages to avoid relying on the error message which is part of the user interface rather than the API and might change without notice?

-

A2. As an example, let’s say that another package developer wanted to use the check_if_pkg_installed() function that we just wrote.

-

So the developer using it in their own package can write a unit test like this:

-
-expect_error(
-  check_if_pkg_installed("xyz123"),
-  "Problem loading `xyz123` package, which is missing and must be installed."
-)
-

To dissuade developers from having to rely on error messages to check for errors, we can instead provide a custom condition, which can be used for unit testing instead:

-
-e <- catch_cnd(check_if_pkg_installed("xyz123"))
-
-inherits(e, "error_missing_package")
-#> [1] TRUE
-

So that the unit test could be:

-
-expect_s3_class(e, "error_missing_package")
-

This test wouldn’t fail even if we decided to change the exact message.

-
-
-
-

-8.4 Applications (Exercises 8.6.6) -

-
-

Q1. Create suppressConditions() that works like suppressMessages() and suppressWarnings() but suppresses everything. Think carefully about how you should handle errors.

-

A1. To create the desired suppressConditions(), we just need to create an equivalent of suppressWarnings() and suppressMessages() for errors. To suppress the error message, we can handle errors within a tryCatch() and return the error object invisibly:

-
-suppressErrors <- function(expr) {
-  tryCatch(
-    error = function(cnd) invisible(cnd),
-    expr
-  )
-}
-
-suppressConditions <- function(expr) {
-  suppressErrors(suppressWarnings(suppressMessages(expr)))
-}
-

Let’s try out and see if this works as expected:

-
-suppressConditions(1)
-#> [1] 1
-
-suppressConditions({
-  message("I'm messaging you")
-  warning("I'm warning you")
-})
-
-suppressConditions({
-  stop("I'm stopping this")
-})
-

All condition messages are now suppressed, but note that if we assign error object to a variable, we can still extract useful information for debugging:

-
-e <- suppressConditions({
-  stop("I'm stopping this")
-})
-
-e
-#> <simpleError in withCallingHandlers(expr, message = function(c) if (inherits(c,     classes)) tryInvokeRestart("muffleMessage")): I'm stopping this>
-
-

Q2. Compare the following two implementations of message2error(). What is the main advantage of withCallingHandlers() in this scenario? (Hint: look carefully at the traceback.)

-
-message2error <- function(code) {
-  withCallingHandlers(code, message = function(e) stop(e))
-}
-message2error <- function(code) {
-  tryCatch(code, message = function(e) stop(e))
-}
-

A2. With withCallingHandlers(), the condition handler is called from the signaling function itself, and, therefore, provides a more detailed call stack.

-
-message2error1 <- function(code) {
-  withCallingHandlers(code, message = function(e) stop("error"))
-}
-
-message2error1({
-  1
-  message("hidden error")
-  NULL
-})
-#> Error in (function (e) : error
-
-traceback()
-#> 9: stop("error") at #2
-#> 8: (function (e)
-#>    stop("error"))(list(message = "hidden error\n",
-#>      call = message("hidden error")))
-#> 7: signalCondition(cond)
-#> 6: doWithOneRestart(return(expr), restart)
-#> 5: withOneRestart(expr, restarts[[1L]])
-#> 4: withRestarts({
-#>        signalCondition(cond)
-#>        defaultHandler(cond)
-#>    }, muffleMessage = function() NULL)
-#> 3: message("hidden error") at #1
-#> 2: withCallingHandlers(code,
-#>      message = function(e) stop("error")) at #2
-#> 1: message2error1({
-#>        1
-#>        message("hidden error")
-#>        NULL
-#>    })
-

With tryCatch(), the signalling function terminates when a condition is raised, and so it doesn’t provide as detailed call stack.

-
-message2error2 <- function(code) {
-  tryCatch(code, message = function(e) (stop("error")))
-}
-
-message2error2({
-  1
-  stop("hidden error")
-  NULL
-})
-#> Error in value[[3L]](cond) : error
-
-traceback()
-#> 6: stop("error") at #2
-#> 5: value[[3L]](cond)
-#> 4: tryCatchOne(expr, names, parentenv, handlers[[1L]])
-#> 3: tryCatchList(expr, classes, parentenv, handlers)
-#> 2: tryCatch(code, message = function(e) (stop("error"))) at #2
-#> 1: message2error2({
-#>        1
-#>        message("hidden error")
-#>        NULL
-#>    })
-
-

Q3. How would you modify the catch_cnds() definition if you wanted to recreate the original intermingling of warnings and messages?

-

A3. Actually, you won’t have to modify anything about the function defined in the chapter, since it supports this out of the box.

-

So nothing additional to do here5! πŸ˜…

-
-catch_cnds <- function(expr) {
-  conds <- list()
-  add_cond <- function(cnd) {
-    conds <<- append(conds, list(cnd))
-    cnd_muffle(cnd)
-  }
-
-  withCallingHandlers(
-    message = add_cond,
-    warning = add_cond,
-    expr
-  )
-
-  conds
-}
-
-catch_cnds({
-  inform("a")
-  warn("b")
-  inform("c")
-})
-#> [[1]]
-#> <message/rlang_message>
-#> Message:
-#> a
-#> 
-#> [[2]]
-#> <warning/rlang_warning>
-#> Warning:
-#> b
-#> 
-#> [[3]]
-#> <message/rlang_message>
-#> Message:
-#> c
-
-

Q4. Why is catching interrupts dangerous? Run this code to find out.

-
-bottles_of_beer <- function(i = 99) {
-  message(
-    "There are ", i, " bottles of beer on the wall, ",
-    i, " bottles of beer."
-  )
-  while (i > 0) {
-    tryCatch(
-      Sys.sleep(1),
-      interrupt = function(err) {
-        i <<- i - 1
-        if (i > 0) {
-          message(
-            "Take one down, pass it around, ", i,
-            " bottle", if (i > 1) "s", " of beer on the wall."
-          )
-        }
-      }
-    )
-  }
-  message(
-    "No more bottles of beer on the wall, ",
-    "no more bottles of beer."
-  )
-}
-

A4. Because this function catches the interrupt and there is no way to stop bottles_of_beer(), because the way you would usually stop it by using interrupt!

-
-bottles_of_beer()
-#> There are 99 bottles of beer on the wall, 99 bottles of beer.
-#> Take one down, pass it around, 98 bottles of beer on the wall.
-#> Take one down, pass it around, 97 bottles of beer on the wall.
-#> Take one down, pass it around, 96 bottles of beer on the wall.
-#> Take one down, pass it around, 95 bottles of beer on the wall.
-#> Take one down, pass it around, 94 bottles of beer on the wall.
-#> Take one down, pass it around, 93 bottles of beer on the wall.
-#> Take one down, pass it around, 92 bottles of beer on the wall.
-#> Take one down, pass it around, 91 bottles of beer on the wall.
-#> ...
-

In RStudio IDE, you can snap out of this loop by terminating the R session.

-

This shows why catching interrupt is dangerous and can result in poor user experience.

-
-
-
-

-8.5 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    assertthat    0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    brio          1.1.3      2021-11-30 [1] CRAN (R 4.2.0)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    DBI           1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)
-#>    desc          1.4.2      2022-09-08 [1] CRAN (R 4.2.1)
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    dplyr         1.0.10     2022-09-01 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    pkgload       1.3.1      2022-10-28 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang       * 1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rprojroot     2.0.3      2022-04-02 [1] CRAN (R 4.2.0)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    testthat    * 3.1.5      2022-10-08 [1] CRAN (R 4.2.1)
-#>    tibble        3.1.8.9002 2022-10-16 [1] local
-#>    tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
- -
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/control-flow.html b/_book/control-flow.html deleted file mode 100644 index c2c3a907..00000000 --- a/_book/control-flow.html +++ /dev/null @@ -1,395 +0,0 @@ - - - - - - -Chapter 5 Control flow | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-5 Control flow -

-
-

-5.1 Choices (Exercises 5.2.4) -

-

Q1. What type of vector does each of the following calls to ifelse() return?

-
-ifelse(TRUE, 1, "no")
-ifelse(FALSE, 1, "no")
-ifelse(NA, 1, "no")
-

Read the documentation and write down the rules in your own words.

-

A1. Here are the rules about what a call to ifelse() might return:

-
    -
  • It is type unstable, i.e.Β the type of return will depend on the type of which condition is true (yes or no, i.e.):
  • -
-
-ifelse(TRUE, 1, "no") # `numeric` returned
-#> [1] 1
-ifelse(FALSE, 1, "no") # `character` returned
-#> [1] "no"
-
    -
  • It works only for cases where test argument evaluates to a logical type:
  • -
-
-ifelse(NA_real_, 1, "no")
-#> [1] NA
-ifelse(NaN, 1, "no")
-#> [1] NA
-
    -
  • If test is argument is of logical type, but NA, it will return NA:
  • -
-
-ifelse(NA, 1, "no")
-#> [1] NA
-
    -
  • If the test argument doesn’t resolve to logical type, it will try to coerce the output to a logical type:
  • -
-
-# will work
-ifelse("TRUE", 1, "no")
-#> [1] 1
-ifelse("false", 1, "no")
-#> [1] "no"
-
-# won't work
-ifelse("tRuE", 1, "no")
-#> [1] NA
-ifelse(NaN, 1, "no")
-#> [1] NA
-

This is also clarified in the docs for this function:

-
-

A vector of the same length and attributes (including dimensions and "class") as test and data values from the values of yes or no. The mode of the answer will be coerced from logical to accommodate first any values taken from yes and then any values taken from no.

-
-

Q2. Why does the following code work?

-
-x <- 1:10
-if (length(x)) "not empty" else "empty"
-#> [1] "not empty"
-
-x <- numeric()
-if (length(x)) "not empty" else "empty"
-#> [1] "empty"
-

A2. The code works because the conditional expressions in if() - even though of numeric type - can be successfully coerced to a logical type.

-
-as.logical(length(1:10))
-#> [1] TRUE
-
-as.logical(length(numeric()))
-#> [1] FALSE
-
-
-

-5.2 Loops (Exercises 5.3.3) -

-

Q1. Why does this code succeed without errors or warnings?

-
-x <- numeric()
-out <- vector("list", length(x))
-for (i in 1:length(x)) {
-  out[i] <- x[i]^2
-}
-out
-

A1. This works because 1:length(x) works in both positive and negative directions.

-
-1:2
-#> [1] 1 2
-1:0
-#> [1] 1 0
-1:-3
-#> [1]  1  0 -1 -2 -3
-

In this case, since x is of length 0, i will go from 1 to 0.

-

Additionally, since out-of-bound (OOB) value for atomic vectors is NA, all related operations with OOB values will also produce NA.

-
-x <- numeric()
-out <- vector("list", length(x))
-
-for (i in 1:length(x)) {
-  print(paste("i:", i, ", x[i]:", x[i], ", out[i]:", out[i]))
-
-  out[i] <- x[i]^2
-}
-#> [1] "i: 1 , x[i]: NA , out[i]: NULL"
-#> [1] "i: 0 , x[i]:  , out[i]: "
-
-out
-#> [[1]]
-#> [1] NA
-

A way to do avoid this unintended behavior is to use seq_along() instead:

-
-x <- numeric()
-out <- vector("list", length(x))
-
-for (i in seq_along(x)) {
-  out[i] <- x[i]^2
-}
-
-out
-#> list()
-

Q2. When the following code is evaluated, what can you say about the vector being iterated?

-
-xs <- c(1, 2, 3)
-for (x in xs) {
-  xs <- c(xs, x * 2)
-}
-xs
-#> [1] 1 2 3 2 4 6
-

A2. The iterator variable x initially takes all values of the vector xs. We can check this by printing x for each iteration:

-
-xs <- c(1, 2, 3)
-for (x in xs) {
-  cat("x:", x, "\n")
-  xs <- c(xs, x * 2)
-  cat("xs:", paste(xs), "\n")
-}
-#> x: 1 
-#> xs: 1 2 3 2 
-#> x: 2 
-#> xs: 1 2 3 2 4 
-#> x: 3 
-#> xs: 1 2 3 2 4 6
-

It is worth noting that x is not updated after each iteration; otherwise, it will take increasingly bigger values of xs, and the loop will never end executing.

-

Q3. What does the following code tell you about when the index is updated?

-
-for (i in 1:3) {
-  i <- i * 2
-  print(i)
-}
-#> [1] 2
-#> [1] 4
-#> [1] 6
-

A3. In a for() loop the index is updated in the beginning of each iteration. Otherwise, we will encounter an infinite loop.

-
-for (i in 1:3) {
-  cat("before: ", i, "\n")
-  i <- i * 2
-  cat("after:  ", i, "\n")
-}
-#> before:  1 
-#> after:   2 
-#> before:  2 
-#> after:   4 
-#> before:  3 
-#> after:   6
-

Also, worth contrasting the behavior of for() loop with that of while() loop:

-
-i <- 1
-while (i < 4) {
-  cat("before: ", i, "\n")
-  i <- i * 2
-  cat("after:  ", i, "\n")
-}
-#> before:  1 
-#> after:   2 
-#> before:  2 
-#> after:   4
-
-
-

-5.3 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang         1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/cover.png b/_book/cover.png deleted file mode 100644 index 417e12bb..00000000 Binary files a/_book/cover.png and /dev/null differ diff --git a/_book/debugging.html b/_book/debugging.html deleted file mode 100644 index 873848d2..00000000 --- a/_book/debugging.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - -Chapter 22 Debugging | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/diagrams/environments/recursive-1.png b/_book/diagrams/environments/recursive-1.png deleted file mode 100644 index e42b4ac4..00000000 Binary files a/_book/diagrams/environments/recursive-1.png and /dev/null differ diff --git a/_book/diagrams/environments/recursive-2.png b/_book/diagrams/environments/recursive-2.png deleted file mode 100644 index 11f373d6..00000000 Binary files a/_book/diagrams/environments/recursive-2.png and /dev/null differ diff --git a/_book/environments.html b/_book/environments.html deleted file mode 100644 index 0b1ef18d..00000000 --- a/_book/environments.html +++ /dev/null @@ -1,701 +0,0 @@ - - - - - - -Chapter 7 Environments | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-7 Environments -

- - - - - -

Loading the needed libraries:

-
-library(rlang, warn.conflicts = FALSE)
-
-

-7.1 Environment basics (Exercises 7.2.7) -

-

Q1. List three ways in which an environment differs from a list.

-

A1. As mentioned in the book, here are a few ways in which environments differ from lists:

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyListEnvironment
semanticsvaluereference
data structurelinearnon-linear
duplicated namesallowednot allowed
can have parents?falsetrue
can contain itself?falsetrue
-

Q2. Create an environment as illustrated by this picture.

-
-

A2. Creating the environment illustrated in the picture:

-
-library(rlang)
-
-e <- env()
-e$loop <- e
-env_print(e)
-#> <environment: 0x139a85110>
-#> Parent: <environment: global>
-#> Bindings:
-#> β€’ loop: <env>
-

The binding loop should have the same memory address as the environment e:

-
-lobstr::ref(e$loop)
-#> β–ˆ [1:0x139a85110] <env> 
-#> └─loop = [1:0x139a85110]
-

Q3. Create a pair of environments as illustrated by this picture.

-
-

A3. Creating the specified environment:

-
-e1 <- env()
-e2 <- env()
-
-e1$loop <- e2
-e2$deloop <- e1
-
-# following should be the same
-lobstr::obj_addrs(list(e1, e2$deloop))
-#> [1] "0x1083b4018" "0x1083b4018"
-lobstr::obj_addrs(list(e2, e1$loop))
-#> [1] "0x108409438" "0x108409438"
-

Q4. Explain why e[[1]] and e[c("a", "b")] don’t make sense when e is an environment.

-

A4. An environment is a non-linear data structure, and has no concept of ordered elements. Therefore, indexing it (e.g.Β e[[1]]) doesn’t make sense.

-

Subsetting a list or a vector returns a subset of the underlying data structure. For example, subsetting a vector returns another vector. But it’s unclear what subsetting an environment (e.g.Β e[c("a", "b")]) should return because there is no data structure to contain its returns. It can’t be another environment since environments have reference semantics.

-

Q5. Create a version of env_poke() that will only bind new names, never re-bind old names. Some programming languages only do this, and are known as single assignment languages.

-

A5. Create a version of env_poke() that doesn’t allow re-binding old names:

-
-env_poke2 <- function(env, nm, value) {
-  if (env_has(env, nm)) {
-    abort("Can't re-bind existing names.")
-  }
-
-  env_poke(env, nm, value)
-}
-

Making sure that it behaves as expected:

-
-e <- env(a = 1, b = 2, c = 3)
-
-# re-binding old names not allowed
-env_poke2(e, "b", 4)
-#> Error in `env_poke2()`:
-#> ! Can't re-bind existing names.
-
-# binding new names allowed
-env_poke2(e, "d", 8)
-e$d
-#> [1] 8
-

Contrast this behavior with the following:

-
-e <- env(a = 1, b = 2, c = 3)
-
-e$b
-#> [1] 2
-
-# re-binding old names allowed
-env_poke(e, "b", 4)
-e$b
-#> [1] 4
-

Q6. What does this function do? How does it differ from <<- and why might you prefer it?

-
-rebind <- function(name, value, env = caller_env()) {
-  if (identical(env, empty_env())) {
-    stop("Can't find `", name, "`", call. = FALSE)
-  } else if (env_has(env, name)) {
-    env_poke(env, name, value)
-  } else {
-    rebind(name, value, env_parent(env))
-  }
-}
-rebind("a", 10)
-#> Error: Can't find `a`
-a <- 5
-rebind("a", 10)
-a
-#> [1] 10
-

A6. The downside of <<- is that it will create a new binding if it doesn’t exist in the given environment, which is something that we may not wish:

-
-# `x` doesn't exist
-exists("x")
-#> [1] FALSE
-
-# so `<<-` will create one for us
-{
-  x <<- 5
-}
-
-# in the global environment
-env_has(global_env(), "x")
-#>    x 
-#> TRUE
-x
-#> [1] 5
-

But rebind() function will let us know if the binding doesn’t exist, which is much safer:

-
-rebind <- function(name, value, env = caller_env()) {
-  if (identical(env, empty_env())) {
-    stop("Can't find `", name, "`", call. = FALSE)
-  } else if (env_has(env, name)) {
-    env_poke(env, name, value)
-  } else {
-    rebind(name, value, env_parent(env))
-  }
-}
-
-# doesn't exist
-exists("abc")
-#> [1] FALSE
-
-# so function will produce an error instead of creating it for us
-rebind("abc", 10)
-#> Error: Can't find `abc`
-
-# but it will work as expected when the variable already exists
-abc <- 5
-rebind("abc", 10)
-abc
-#> [1] 10
-
-
-

-7.2 Recursing over environments (Exercises 7.3.1) -

-

Q1. Modify where() to return all environments that contain a binding for name. Carefully think through what type of object the function will need to return.

-

A1. Here is a modified version of where() that returns all environments that contain a binding for name.

-

Since we anticipate more than one environment, we dynamically update a list each time an environment with the specified binding is found. It is important to initialize to an empty list since that signifies that given binding is not found in any of the environments.

-
-where <- function(name, env = caller_env()) {
-  env_list <- list()
-
-  while (!identical(env, empty_env())) {
-    if (env_has(env, name)) {
-      env_list <- append(env_list, env)
-    }
-
-    env <- env_parent(env)
-  }
-
-  return(env_list)
-}
-

Let’s try it out:

-
-where("yyy")
-#> list()
-
-x <- 5
-where("x")
-#> [[1]]
-#> <environment: R_GlobalEnv>
-
-where("mean")
-#> [[1]]
-#> <environment: base>
-
-library(dplyr, warn.conflicts = FALSE)
-where("filter")
-#> [[1]]
-#> <environment: package:dplyr>
-#> attr(,"name")
-#> [1] "package:dplyr"
-#> attr(,"path")
-#> [1] "/Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library/dplyr"
-#> 
-#> [[2]]
-#> <environment: package:stats>
-#> attr(,"name")
-#> [1] "package:stats"
-#> attr(,"path")
-#> [1] "/Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library/stats"
-detach("package:dplyr")
-

Q2. Write a function called fget() that finds only function objects. It should have two arguments, name and env, and should obey the regular scoping rules for functions: if there’s an object with a matching name that’s not a function, look in the parent. For an added challenge, also add an inherits argument which controls whether the function recurses up the parents or only looks in one environment.

-

A2. Here is a function that recursively looks for function objects:

-
-fget <- function(name, env = caller_env(), inherits = FALSE) {
-  # we need only function objects
-  f_value <- mget(name,
-    envir = env,
-    mode = "function",
-    inherits = FALSE, # since we have our custom argument
-    ifnotfound = list(NULL)
-  )
-
-  if (!is.null(f_value[[1]])) {
-    # success case
-    f_value[[1]]
-  } else {
-    if (inherits && !identical(env, empty_env())) {
-      # recursive case
-      env <- env_parent(env)
-      fget(name, env, inherits = TRUE)
-    } else {
-      # base case
-      stop("No function objects with matching name was found.", call. = FALSE)
-    }
-  }
-}
-

Let’s try it out:

-
-fget("mean", inherits = FALSE)
-#> Error: No function objects with matching name was found.
-
-fget("mean", inherits = TRUE)
-#> function (x, ...) 
-#> UseMethod("mean")
-#> <bytecode: 0x13ad9c030>
-#> <environment: namespace:base>
-
-mean <- 5
-fget("mean", inherits = FALSE)
-#> Error: No function objects with matching name was found.
-
-mean <- function() NULL
-fget("mean", inherits = FALSE)
-#> function() NULL
-rm("mean")
-
-
-

-7.3 Special environments (Exercises 7.4.5) -

-

Q1. How is search_envs() different from env_parents(global_env())?

-

A1. The search_envs() lists a chain of environments currently attached to the search path and contains exported functions from these packages. The search path always ends at the {base} package environment. The search path also includes the global environment.

-
-search_envs()
-#>  [[1]] $ <env: global>
-#>  [[2]] $ <env: package:rlang>
-#>  [[3]] $ <env: package:magrittr>
-#>  [[4]] $ <env: package:stats>
-#>  [[5]] $ <env: package:graphics>
-#>  [[6]] $ <env: package:grDevices>
-#>  [[7]] $ <env: package:utils>
-#>  [[8]] $ <env: package:datasets>
-#>  [[9]] $ <env: package:methods>
-#> [[10]] $ <env: Autoloads>
-#> [[11]] $ <env: package:base>
-

The env_parents() lists all parent environments up until the empty environment. Of course, the global environment itself is not included in this list.

-
-env_parents(global_env())
-#>  [[1]] $ <env: package:rlang>
-#>  [[2]] $ <env: package:magrittr>
-#>  [[3]] $ <env: package:stats>
-#>  [[4]] $ <env: package:graphics>
-#>  [[5]] $ <env: package:grDevices>
-#>  [[6]] $ <env: package:utils>
-#>  [[7]] $ <env: package:datasets>
-#>  [[8]] $ <env: package:methods>
-#>  [[9]] $ <env: Autoloads>
-#> [[10]] $ <env: package:base>
-#> [[11]] $ <env: empty>
-

Q2. Draw a diagram that shows the enclosing environments of this function:

-
-f1 <- function(x1) {
-  f2 <- function(x2) {
-    f3 <- function(x3) {
-      x1 + x2 + x3
-    }
-    f3(3)
-  }
-  f2(2)
-}
-f1(1)
-

A2. I don’t have access to the graphics software used to create diagrams in the book, so I am linking the diagram from the official solutions manual, where you will also find a more detailed description for the figure:

-
-

Q3. Write an enhanced version of str() that provides more information about functions. Show where the function was found and what environment it was defined in.

-

A3. To write the required function, we can first re-purpose the fget() function we wrote above to return the environment in which it was found and its enclosing environment:

-
-fget2 <- function(name, env = caller_env()) {
-  # we need only function objects
-  f_value <- mget(name,
-    envir = env,
-    mode = "function",
-    inherits = FALSE,
-    ifnotfound = list(NULL)
-  )
-
-  if (!is.null(f_value[[1]])) {
-    # success case
-    list(
-      "where" = env,
-      "enclosing" = fn_env(f_value[[1]])
-    )
-  } else {
-    if (!identical(env, empty_env())) {
-      # recursive case
-      env <- env_parent(env)
-      fget2(name, env)
-    } else {
-      # base case
-      stop("No function objects with matching name was found.", call. = FALSE)
-    }
-  }
-}
-

Let’s try it out:

-
-fget2("mean")
-#> $where
-#> <environment: base>
-#> 
-#> $enclosing
-#> <environment: namespace:base>
-
-mean <- function() NULL
-fget2("mean")
-#> $where
-#> <environment: R_GlobalEnv>
-#> 
-#> $enclosing
-#> <environment: R_GlobalEnv>
-rm("mean")
-

We can now write the new version of str() as a wrapper around this function. We only need to foresee that the users might enter the function name either as a symbol or a string.

-
-str_function <- function(.f) {
-  fget2(as_string(ensym(.f)))
-}
-

Let’s first try it with base::mean():

-
-str_function(mean)
-#> $where
-#> <environment: base>
-#> 
-#> $enclosing
-#> <environment: namespace:base>
-
-str_function("mean")
-#> $where
-#> <environment: base>
-#> 
-#> $enclosing
-#> <environment: namespace:base>
-

And then with our variant present in the global environment:

-
-mean <- function() NULL
-
-str_function(mean)
-#> $where
-#> <environment: R_GlobalEnv>
-#> 
-#> $enclosing
-#> <environment: R_GlobalEnv>
-
-str_function("mean")
-#> $where
-#> <environment: R_GlobalEnv>
-#> 
-#> $enclosing
-#> <environment: R_GlobalEnv>
-
-rm("mean")
-
-
-

-7.4 Call stacks (Exercises 7.5.5) -

-

Q1. Write a function that lists all the variables defined in the environment in which it was called. It should return the same results as ls().

-

A1. Here is a function that lists all the variables defined in the environment in which it was called:

-
-# let's first remove everything that exists in the global environment right now
-# to test with only newly defined objects
-rm(list = ls())
-rm(.Random.seed, envir = globalenv())
-
-ls_env <- function(env = rlang::caller_env()) {
-  sort(rlang::env_names(env))
-}
-

The workhorse here is rlang::caller_env(), so let’s also have a look at its definition:

-
-rlang::caller_env
-#> function (n = 1) 
-#> {
-#>     parent.frame(n + 1)
-#> }
-#> <bytecode: 0x1596ff7c8>
-#> <environment: namespace:rlang>
-

Let’s try it out:

-
    -
  • In global environment:
  • -
-
-x <- "a"
-y <- 1
-
-ls_env()
-#> [1] "ls_env" "x"      "y"
-
-ls()
-#> [1] "ls_env" "x"      "y"
-
    -
  • In function environment:
  • -
-
-foo <- function() {
-  a <- "x"
-  b <- 2
-
-  print(ls_env())
-
-  print(ls())
-}
-
-foo()
-#> [1] "a" "b"
-#> [1] "a" "b"
-
-
-

-7.5 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    assertthat    0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>    crayon        1.5.2      2022-09-29 [1] CRAN (R 4.2.1)
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    DBI           1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    dplyr         1.0.10     2022-09-01 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    highr         0.9        2021-04-16 [1] CRAN (R 4.2.0)
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    lobstr        1.1.2      2022-06-22 [1] CRAN (R 4.2.0)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    png           0.1-7      2013-12-03 [1] CRAN (R 4.2.0)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang       * 1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble        3.1.8.9002 2022-10-16 [1] local
-#>    tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/evaluation.html b/_book/evaluation.html deleted file mode 100644 index 8ab7d9be..00000000 --- a/_book/evaluation.html +++ /dev/null @@ -1,732 +0,0 @@ - - - - - - -Chapter 20 Evaluation | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-20 Evaluation -

-

Attaching the needed libraries:

- -
-

-20.1 Evaluation basics (Exercises 20.2.4) -

-
-

Q1. Carefully read the documentation for source(). What environment does it use by default? What if you supply local = TRUE? How do you provide a custom environment?

-

A1. The parameter local for source() decides the environment in which the parsed expressions are evaluated.

-

By default local = FALSE, this corresponds to the user’s workspace (the global environment, i.e.).

-
-withr::with_tempdir(
-  code = {
-    f <- tempfile()
-    writeLines("rlang::env_print()", f)
-    foo <- function() source(f, local = FALSE)
-    foo()
-  }
-)
-#> <environment: global>
-#> Parent: <environment: package:rlang>
-#> Bindings:
-#> β€’ .Random.seed: <int>
-#> β€’ foo: <fn>
-#> β€’ f: <chr>
-

If local = TRUE, then the environment from which source() is called will be used.

-
-withr::with_tempdir(
-  code = {
-    f <- tempfile()
-    writeLines("rlang::env_print()", f)
-    foo <- function() source(f, local = TRUE)
-    foo()
-  }
-)
-#> <environment: 0x14af04d30>
-#> Parent: <environment: global>
-

To specify a custom environment, the sys.source() function can be used, which provides an envir parameter.

-
-

Q2. Predict the results of the following lines of code:

-
-eval(expr(eval(expr(eval(expr(2 + 2))))))
-eval(eval(expr(eval(expr(eval(expr(2 + 2)))))))
-expr(eval(expr(eval(expr(eval(expr(2 + 2)))))))
-

A2. Correctly predicted πŸ˜‰

-
-eval(expr(eval(expr(eval(expr(2 + 2))))))
-#> [1] 4
-
-eval(eval(expr(eval(expr(eval(expr(2 + 2)))))))
-#> [1] 4
-
-expr(eval(expr(eval(expr(eval(expr(2 + 2)))))))
-#> eval(expr(eval(expr(eval(expr(2 + 2))))))
-
-

Q3. Fill in the function bodies below to re-implement get() using sym() and eval(), and assign() using sym(), expr(), and eval(). Don’t worry about the multiple ways of choosing an environment that get() and assign() support; assume that the user supplies it explicitly.

-
-# name is a string
-get2 <- function(name, env) {}
-assign2 <- function(name, value, env) {}
-

A3. Here are the required re-implementations:

- -
-get2 <- function(name, env = caller_env()) {
-  name <- sym(name)
-  eval(name, env)
-}
-
-x <- 2
-
-get2("x")
-#> [1] 2
-get("x")
-#> [1] 2
-
-y <- 1:4
-assign("y[1]", 2)
-
-get2("y[1]")
-#> [1] 2
-get("y[1]")
-#> [1] 2
- -
-assign2 <- function(name, value, env = caller_env()) {
-  name <- sym(name)
-  eval(expr(!!name <- !!value), env)
-}
-
-assign("y1", 4)
-y1
-#> [1] 4
-
-assign2("y2", 4)
-y2
-#> [1] 4
-
-

Q4. Modify source2() so it returns the result of every expression, not just the last one. Can you eliminate the for loop?

-

A4. We can use purrr::map() to iterate over every expression and return result of every expression:

-
-source2 <- function(path, env = caller_env()) {
-  file <- paste(readLines(path, warn = FALSE), collapse = "\n")
-  exprs <- parse_exprs(file)
-  purrr::map(exprs, ~ eval(.x, env))
-}
-
-withr::with_tempdir(
-  code = {
-    f <- tempfile(fileext = ".R")
-    writeLines("1 + 1; 2 + 4", f)
-    source2(f)
-  }
-)
-#> [[1]]
-#> [1] 2
-#> 
-#> [[2]]
-#> [1] 6
-
-

Q5. We can make base::local() slightly easier to understand by spreading out over multiple lines:

-
-local3 <- function(expr, envir = new.env()) {
-  call <- substitute(eval(quote(expr), envir))
-  eval(call, envir = parent.frame())
-}
-

Explain how local() works in words. (Hint: you might want to print(call) to help understand what substitute() is doing, and read the documentation to remind yourself what environment new.env() will inherit from.)

-

A5. In order to figure out how this function works, let’s add the suggested print(call):

-
-local3 <- function(expr, envir = new.env()) {
-  call <- substitute(eval(quote(expr), envir))
-  print(call)
-
-  eval(call, envir = parent.frame())
-}
-
-local3({
-  x <- 10
-  y <- 200
-  x + y
-})
-#> eval(quote({
-#>     x <- 10
-#>     y <- 200
-#>     x + y
-#> }), new.env())
-#> [1] 210
-

As docs for substitute() mention:

-
-

Substituting and quoting often cause confusion when the argument is expression(…). The result is a call to the expression constructor function and needs to be evaluated with eval to give the actual expression object.

-
-

Thus, to get the actual expression object, quoted expression needs to be evaluated using eval():

-
-is_expression(eval(quote({
-  x <- 10
-  y <- 200
-  x + y
-}), new.env()))
-#> [1] TRUE
-

Finally, the generated call is evaluated in the caller environment. So the final function call looks like the following:

-
-# outer environment
-eval(
-  # inner environment
-  eval(quote({
-    x <- 10
-    y <- 200
-    x + y
-  }), new.env()),
-  envir = parent.frame()
-)
-

Note here that the bindings for x and y are found in the inner environment, while bindings for functions eval(), quote(), etc. are found in the outer environment.

-
-
-
-

-20.2 Quosures (Exercises 20.3.6) -

-
-

Q1. Predict what each of the following quosures will return if evaluated.

-
-q1 <- new_quosure(expr(x), env(x = 1))
-q1
-#> <quosure>
-#> expr: ^x
-#> env:  0x103b6fd78
-q2 <- new_quosure(expr(x + !!q1), env(x = 10))
-q2
-#> <quosure>
-#> expr: ^x + (^x)
-#> env:  0x14af4ada0
-q3 <- new_quosure(expr(x + !!q2), env(x = 100))
-q3
-#> <quosure>
-#> expr: ^x + (^x + (^x))
-#> env:  0x103f88b78
-

A1. Correctly predicted πŸ˜‰

-
-q1 <- new_quosure(expr(x), env(x = 1))
-eval_tidy(q1)
-#> [1] 1
-
-q2 <- new_quosure(expr(x + !!q1), env(x = 10))
-eval_tidy(q2)
-#> [1] 11
-
-q3 <- new_quosure(expr(x + !!q2), env(x = 100))
-eval_tidy(q3)
-#> [1] 111
-
-

Q2. Write an enenv() function that captures the environment associated with an argument. (Hint: this should only require two function calls.)

-

A2. We can make use of the get_env() helper to get the environment associated with an argument:

-
-enenv <- function(x) {
-  x <- enquo(x)
-  get_env(x)
-}
-
-enenv(x)
-#> <environment: R_GlobalEnv>
-
-foo <- function(x) enenv(x)
-foo()
-#> <environment: 0x118b4ac68>
-
-
-
-

-20.3 Data masks (Exercises 20.4.6) -

-
-

Q1. Why did I use a for loop in transform2() instead of map()? Consider transform2(df, x = x * 2, x = x * 2).

-

A1. To see why map() is not appropriate for this function, let’s create a version of the function with map() and see what happens.

-
-transform2 <- function(.data, ...) {
-  dots <- enquos(...)
-
-  for (i in seq_along(dots)) {
-    name <- names(dots)[[i]]
-    dot <- dots[[i]]
-
-    .data[[name]] <- eval_tidy(dot, .data)
-  }
-
-  .data
-}
-
-transform3 <- function(.data, ...) {
-  dots <- enquos(...)
-
-  purrr::map(dots, function(x, .data = .data) {
-    name <- names(x)
-    dot <- x
-
-    .data[[name]] <- eval_tidy(dot, .data)
-
-    .data
-  })
-}
-

When we use a for() loop, in each iteration, we are updating the x column with the current expression under evaluation. That is, repeatedly modifying the same column works.

-
-df <- data.frame(x = 1:3)
-transform2(df, x = x * 2, x = x * 2)
-#>    x
-#> 1  4
-#> 2  8
-#> 3 12
-

If we use map() instead, we are trying to evaluate all expressions at the same time; i.e., the same column is being attempted to modify on using multiple expressions.

-
-df <- data.frame(x = 1:3)
-transform3(df, x = x * 2, x = x * 2)
-#> Error in eval_tidy(dot, .data): promise already under evaluation: recursive default argument reference or earlier problems?
-
-

Q2. Here’s an alternative implementation of subset2():

-
-subset3 <- function(data, rows) {
-  rows <- enquo(rows)
-  eval_tidy(expr(data[!!rows, , drop = FALSE]), data = data)
-}
-df <- data.frame(x = 1:3)
-subset3(df, x == 1)
-

Compare and contrast subset3() to subset2(). What are its advantages and disadvantages?

-

A2. Let’s first juxtapose these functions and their outputs so that we can compare them better.

-
-subset2 <- function(data, rows) {
-  rows <- enquo(rows)
-  rows_val <- eval_tidy(rows, data)
-  stopifnot(is.logical(rows_val))
-
-  data[rows_val, , drop = FALSE]
-}
-
-df <- data.frame(x = 1:3)
-subset2(df, x == 1)
-#>   x
-#> 1 1
-
-subset3 <- function(data, rows) {
-  rows <- enquo(rows)
-  eval_tidy(expr(data[!!rows, , drop = FALSE]), data = data)
-}
-
-subset3(df, x == 1)
-#>   x
-#> 1 1
-

Disadvantages of subset3() over subset2()

-

When the filtering conditions specified in rows don’t evaluate to a logical, the function doesn’t fail informatively. Indeed, it silently returns incorrect result.

-
-rm("x")
-exists("x")
-#> [1] FALSE
-
-subset2(df, x + 1)
-#> Error in subset2(df, x + 1): is.logical(rows_val) is not TRUE
-
-subset3(df, x + 1)
-#>     x
-#> 2   2
-#> 3   3
-#> NA NA
-

Advantages of subset3() over subset2()

-

Some might argue that the function being shorter is an advantage, but this is very much a subjective preference.

-
-

Q3. The following function implements the basics of dplyr::arrange(). Annotate each line with a comment explaining what it does. Can you explain why !!.na.last is strictly correct, but omitting the !! is unlikely to cause problems?

-
-arrange2 <- function(.df, ..., .na.last = TRUE) {
-  args <- enquos(...)
-  order_call <- expr(order(!!!args, na.last = !!.na.last))
-  ord <- eval_tidy(order_call, .df)
-  stopifnot(length(ord) == nrow(.df))
-  .df[ord, , drop = FALSE]
-}
-

A3. Annotated version of the function:

-
-arrange2 <- function(.df, ..., .na.last = TRUE) {
-  # capture user-supplied expressions (and corresponding environments) as quosures
-  args <- enquos(...)
-
-  # create a call object by splicing a list of quosures
-  order_call <- expr(order(!!!args, na.last = !!.na.last))
-
-  # and evaluate the constructed call in the data frame
-  ord <- eval_tidy(order_call, .df)
-
-  # sanity check
-  stopifnot(length(ord) == nrow(.df))
-
-  .df[ord, , drop = FALSE]
-}
-

To see why it doesn’t matter whether whether we unquote the .na.last argument or not, let’s have a look at this smaller example:

-
-x <- TRUE
-eval(expr(c(x = !!x)))
-#>    x 
-#> TRUE
-eval(expr(c(x = x)))
-#>    x 
-#> TRUE
-

As can be seen:

-
    -
  • without unquoting, .na.last is found in the function environment
  • -
  • with unquoting, .na.last is included in the order call object itself
  • -
-
-
-
-

-20.4 Using tidy evaluation (Exercises 20.5.4) -

-
-

Q1. I’ve included an alternative implementation of threshold_var() below. What makes it different to the approach I used above? What makes it harder?

-
-threshold_var <- function(df, var, val) {
-  var <- ensym(var)
-  subset2(df, `$`(.data, !!var) >= !!val)
-}
-

A1. First, let’s compare the two definitions for the same function and make sure that they produce the same output:

-
-threshold_var_old <- function(df, var, val) {
-  var <- as_string(ensym(var))
-  subset2(df, .data[[var]] >= !!val)
-}
-
-threshold_var_new <- threshold_var
-
-df <- data.frame(x = 1:10)
-
-identical(
-  threshold_var(df, x, 8),
-  threshold_var(df, x, 8)
-)
-#> [1] TRUE
-

The key difference is in the subsetting operator used:

-
    -
  • The old version uses non-quoting [[ operator. Thus, var argument first needs to be converted to a string.
  • -
  • The new version uses quoting $ operator. Thus, var argument is first quoted and then unquoted (using !!).
  • -
-
-
-
-

-20.5 Base evaluation (Exercises 20.6.3) -

-
-

Q1. Why does this function fail?

-
-lm3a <- function(formula, data) {
-  formula <- enexpr(formula)
-  lm_call <- expr(lm(!!formula, data = data))
-  eval(lm_call, caller_env())
-}
-
-lm3a(mpg ~ disp, mtcars)$call
-#> Error in as.data.frame.default(data, optional = TRUE):
-#> cannot coerce class β€˜"function"’ to a data.frame
-

A1. This doesn’t work because when lm_call call is evaluated in caller_env(), it finds a binding for base::data() function, and not data from execution environment.

-

To make it work, we need to unquote data into the expression:

-
-lm3a <- function(formula, data) {
-  formula <- enexpr(formula)
-  lm_call <- expr(lm(!!formula, data = !!data))
-  eval(lm_call, caller_env())
-}
-
-is_call(lm3a(mpg ~ disp, mtcars)$call)
-#> [1] TRUE
-
-

Q2. When model building, typically the response and data are relatively constant while you rapidly experiment with different predictors. Write a small wrapper that allows you to reduce duplication in the code below.

-
-lm(mpg ~ disp, data = mtcars)
-lm(mpg ~ I(1 / disp), data = mtcars)
-lm(mpg ~ disp * cyl, data = mtcars)
-

A2. Here is a small wrapper that allows you to enter only the predictors:

-
-lm_custom <- function(data = mtcars, x, y = mpg) {
-  x <- enexpr(x)
-  y <- enexpr(y)
-  data <- enexpr(data)
-
-  lm_call <- expr(lm(formula = !!y ~ !!x, data = !!data))
-
-  eval(lm_call, caller_env())
-}
-
-identical(
-  lm_custom(x = disp),
-  lm(mpg ~ disp, data = mtcars)
-)
-#> [1] TRUE
-
-identical(
-  lm_custom(x = I(1 / disp)),
-  lm(mpg ~ I(1 / disp), data = mtcars)
-)
-#> [1] TRUE
-
-identical(
-  lm_custom(x = disp * cyl),
-  lm(mpg ~ disp * cyl, data = mtcars)
-)
-#> [1] TRUE
-

But the function is flexible enough to also allow changing both the data and the dependent variable:

-
-lm_custom(data = iris, x = Sepal.Length, y = Petal.Width)
-#> 
-#> Call:
-#> lm(formula = Petal.Width ~ Sepal.Length, data = iris)
-#> 
-#> Coefficients:
-#>  (Intercept)  Sepal.Length  
-#>      -3.2002        0.7529
-
-

Q3. Another way to write resample_lm() would be to include the resample expression (data[sample(nrow(data), replace = TRUE), , drop = FALSE]) in the data argument. Implement that approach. What are the advantages? What are the disadvantages?

-

A3. In this variant of resample_lm(), we are providing the resampled data as an argument.

-
-resample_lm3 <- function(formula,
-                         data,
-                         resample_data = data[sample(nrow(data), replace = TRUE), , drop = FALSE],
-                         env = current_env()) {
-  formula <- enexpr(formula)
-  lm_call <- expr(lm(!!formula, data = resample_data))
-  expr_print(lm_call)
-  eval(lm_call, env)
-}
-
-df <- data.frame(x = 1:10, y = 5 + 3 * (1:10) + round(rnorm(10), 2))
-resample_lm3(y ~ x, data = df)
-#> lm(y ~ x, data = resample_data)
-#> 
-#> Call:
-#> lm(formula = y ~ x, data = resample_data)
-#> 
-#> Coefficients:
-#> (Intercept)            x  
-#>       2.654        3.420
-

This makes use of R’s lazy evaluation of function arguments. That is, resample_data argument will be evaluated only when it is needed in the function.

-
-
-
-

-20.6 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    purrr         0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang       * 1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/expressions.html b/_book/expressions.html deleted file mode 100644 index eaaed750..00000000 --- a/_book/expressions.html +++ /dev/null @@ -1,960 +0,0 @@ - - - - - - -Chapter 18 Expressions | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-18 Expressions -

-

Attaching the needed libraries:

-
-library(rlang, warn.conflicts = FALSE)
-library(lobstr, warn.conflicts = FALSE)
-
-

-18.1 Abstract syntax trees (Exercises 18.2.4) -

-

Q1. Reconstruct the code represented by the trees below:

-
#> β–ˆβ”€f 
-#> β””β”€β–ˆβ”€g 
-#>   β””β”€β–ˆβ”€h
-#> β–ˆβ”€`+` 
-#> β”œβ”€β–ˆβ”€`+` 
-#> β”‚ β”œβ”€1 
-#> β”‚ └─2 
-#> └─3
-#> β–ˆβ”€`*` 
-#> β”œβ”€β–ˆβ”€`(` 
-#> β”‚ β””β”€β–ˆβ”€`+` 
-#> β”‚   β”œβ”€x 
-#> β”‚   └─y 
-#> └─z
-

A1. Below is the reconstructed code.

-
-f(g(h()))
-1 + 2 + 3
-(x + y) * z
-

We can confirm it by drawing ASTs for them:

-
-ast(f(g(h())))
-#> β–ˆβ”€f 
-#> β””β”€β–ˆβ”€g 
-#>   β””β”€β–ˆβ”€h
-
-ast(1 + 2 + 3)
-#> β–ˆβ”€`+` 
-#> β”œβ”€β–ˆβ”€`+` 
-#> β”‚ β”œβ”€1 
-#> β”‚ └─2 
-#> └─3
-
-ast((x + y) * z)
-#> β–ˆβ”€`*` 
-#> β”œβ”€β–ˆβ”€`(` 
-#> β”‚ β””β”€β–ˆβ”€`+` 
-#> β”‚   β”œβ”€x 
-#> β”‚   └─y 
-#> └─z
-

Q2. Draw the following trees by hand and then check your answers with ast().

-
-f(g(h(i(1, 2, 3))))
-f(1, g(2, h(3, i())))
-f(g(1, 2), h(3, i(4, 5)))
-

A2. Successfully drawn by hand. Checking using ast():

-
-ast(f(g(h(i(1, 2, 3)))))
-#> β–ˆβ”€f 
-#> β””β”€β–ˆβ”€g 
-#>   β””β”€β–ˆβ”€h 
-#>     β””β”€β–ˆβ”€i 
-#>       β”œβ”€1 
-#>       β”œβ”€2 
-#>       └─3
-
-ast(f(1, g(2, h(3, i()))))
-#> β–ˆβ”€f 
-#> β”œβ”€1 
-#> β””β”€β–ˆβ”€g 
-#>   β”œβ”€2 
-#>   β””β”€β–ˆβ”€h 
-#>     β”œβ”€3 
-#>     β””β”€β–ˆβ”€i
-
-ast(f(g(1, 2), h(3, i(4, 5))))
-#> β–ˆβ”€f 
-#> β”œβ”€β–ˆβ”€g 
-#> β”‚ β”œβ”€1 
-#> β”‚ └─2 
-#> β””β”€β–ˆβ”€h 
-#>   β”œβ”€3 
-#>   β””β”€β–ˆβ”€i 
-#>     β”œβ”€4 
-#>     └─5
-

Q3. What’s happening with the ASTs below? (Hint: carefully read ?"^".)

-
-ast(`x` + `y`)
-#> β–ˆβ”€`+` 
-#> β”œβ”€x 
-#> └─y
-ast(x**y)
-#> β–ˆβ”€`^` 
-#> β”œβ”€x 
-#> └─y
-ast(1 -> x)
-#> β–ˆβ”€`<-` 
-#> β”œβ”€x 
-#> └─1
-

A3. The str2expression() helps make sense of these ASTs.

-

The non-syntactic names are parsed to names. Thus, backticks have been removed in the AST.

-
-str2expression("`x` + `y`")
-#> expression(x + y)
-

As mentioned in the docs for ^:

-
-

** is translated in the parser to ^

-
-
-str2expression("x**y")
-#> expression(x^y)
-

The rightward assignment is parsed to leftward assignment:

-
-str2expression("1 -> x")
-#> expression(x <- 1)
-

Q4. What is special about the AST below?

-
-ast(function(x = 1, y = 2) {})
-#> β–ˆβ”€`function` 
-#> β”œβ”€β–ˆβ”€x = 1 
-#> β”‚ └─y = 2 
-#> β”œβ”€β–ˆβ”€`{` 
-#> └─<inline srcref>
-

A4. As mentioned in this section:

-
-

Like all objects in R, functions can also possess any number of additional attributes(). One attribute used by base R is srcref, short for source reference. It points to the source code used to create the function. The srcref is used for printing because, unlike body(), it contains code comments and other formatting.

-
-

Therefore, the last leaf in this AST, although not specified in the function call, represents source reference attribute.

-

Q5. What does the call tree of an if statement with multiple else if conditions look like? Why?

-

A5. There is nothing special about this tree. It just shows the nested loop structure inherent to code with if and multiple else if statements.

-
-ast(if (FALSE) 1 else if (FALSE) 2 else if (FALSE) 3 else 4)
-#> β–ˆβ”€`if` 
-#> β”œβ”€FALSE 
-#> β”œβ”€1 
-#> β””β”€β–ˆβ”€`if` 
-#>   β”œβ”€FALSE 
-#>   β”œβ”€2 
-#>   β””β”€β–ˆβ”€`if` 
-#>     β”œβ”€FALSE 
-#>     β”œβ”€3 
-#>     └─4
-
-
-

-18.2 Expressions (Exercises 18.3.5) -

-

Q1. Which two of the six types of atomic vector can’t appear in an expression? Why? Similarly, why can’t you create an expression that contains an atomic vector of length greater than one?

-

A1. Out of the six types of atomic vectors, the two that can’t appear in an expression are: complex and raw.

-

Complex numbers are created via a function call (using +), as can be seen by its AST:

-
-x_complex <- expr(1 + 1i)
-typeof(x_complex)
-#> [1] "language"
-
-ast(1 + 1i)
-#> β–ˆβ”€`+` 
-#> β”œβ”€1 
-#> └─1i
-

Similarly, for raw vectors (using raw()):

-
-x_raw <- expr(raw(2))
-typeof(x_raw)
-#> [1] "language"
-
-ast(raw(2))
-#> β–ˆβ”€raw 
-#> └─2
-

Contrast this with other atomic vectors:

-
-x_int <- expr(2L)
-typeof(x_int)
-#> [1] "integer"
-
-ast(2L)
-#> 2L
-

For the same reason, you can’t you create an expression that contains an atomic vector of length greater than one since that itself is a function call that uses c() function:

-
-x_vec <- expr(c(1, 2))
-typeof(x_vec)
-#> [1] "language"
-
-ast(c(1, 2))
-#> β–ˆβ”€c 
-#> β”œβ”€1 
-#> └─2
-

Q2. What happens when you subset a call object to remove the first element? e.g.Β expr(read.csv("foo.csv", header = TRUE))[-1]. Why?

-

A2. A captured function call like the following creates a call object:

-
-expr(read.csv("foo.csv", header = TRUE))
-#> read.csv("foo.csv", header = TRUE)
-
-typeof(expr(read.csv("foo.csv", header = TRUE)))
-#> [1] "language"
-

As mentioned in the respective section:

-
-

The first element of the call object is the function position.

-
-

Therefore, when the first element in the call object is removed, the next one moves in the function position, and we get the observed output:

-
-expr(read.csv("foo.csv", header = TRUE))[-1]
-#> "foo.csv"(header = TRUE)
-

Q3. Describe the differences between the following call objects.

-
-x <- 1:10
-call2(median, x, na.rm = TRUE)
-call2(expr(median), x, na.rm = TRUE)
-call2(median, expr(x), na.rm = TRUE)
-call2(expr(median), expr(x), na.rm = TRUE)
-

A4. The differences in the constructed call objects are due to the different type of arguments supplied to first two parameters in the call2() function.

-

Types of arguments supplied to .fn:

-
-typeof(median)
-#> [1] "closure"
-typeof(expr(median))
-#> [1] "symbol"
-

Types of arguments supplied to the dynamic dots:

-
-x <- 1:10
-typeof(x)
-#> [1] "integer"
-typeof(expr(x))
-#> [1] "symbol"
-

The following outputs can be understood using the following properties:

-
    -
  • when .fn argument is a closure, that function is inlined in the constructed function call
  • -
  • when x is not a symbol, its value is passed to the function call
  • -
-
-x <- 1:10
-
-call2(median, x, na.rm = TRUE)
-#> (function (x, na.rm = FALSE, ...) 
-#> UseMethod("median"))(1:10, na.rm = TRUE)
-
-call2(expr(median), x, na.rm = TRUE)
-#> median(1:10, na.rm = TRUE)
-
-call2(median, expr(x), na.rm = TRUE)
-#> (function (x, na.rm = FALSE, ...) 
-#> UseMethod("median"))(x, na.rm = TRUE)
-
-call2(expr(median), expr(x), na.rm = TRUE)
-#> median(x, na.rm = TRUE)
-

Importantly, all of the constructed call objects evaluate to give the same result:

-
-x <- 1:10
-
-eval(call2(median, x, na.rm = TRUE))
-#> [1] 5.5
-
-eval(call2(expr(median), x, na.rm = TRUE))
-#> [1] 5.5
-
-eval(call2(median, expr(x), na.rm = TRUE))
-#> [1] 5.5
-
-eval(call2(expr(median), expr(x), na.rm = TRUE))
-#> [1] 5.5
-

Q4. call_standardise() doesn’t work so well for the following calls. Why? What makes mean() special?

-
-call_standardise(quote(mean(1:10, na.rm = TRUE)))
-#> mean(x = 1:10, na.rm = TRUE)
-call_standardise(quote(mean(n = T, 1:10)))
-#> mean(x = 1:10, n = T)
-call_standardise(quote(mean(x = 1:10, , TRUE)))
-#> mean(x = 1:10, , TRUE)
-

A4. This is because of the ellipsis in mean() function signature:

-
-mean
-#> function (x, ...) 
-#> UseMethod("mean")
-#> <bytecode: 0x13042a630>
-#> <environment: namespace:base>
-

As mentioned in the respective section:

-
-

If the function uses ... it’s not possible to standardise all arguments.

-
-

mean() is an S3 generic and the dots are passed to underlying S3 methods.

-

So, the output can be improved using a specific method. For example:

-
-call_standardise(quote(mean.default(n = T, 1:10)))
-#> mean.default(x = 1:10, na.rm = T)
-

Q5. Why does this code not make sense?

-
-x <- expr(foo(x = 1))
-names(x) <- c("x", "y")
-

A5. This doesn’t make sense because the first position in a call object is reserved for function (function position), and so assigning names to this element will just be ignored by R:

-
-x <- expr(foo(x = 1))
-x
-#> foo(x = 1)
-
-names(x) <- c("x", "y")
-x
-#> foo(y = 1)
-

Q6. Construct the expression if(x > 1) "a" else "b" using multiple calls to call2(). How does the code structure reflect the structure of the AST?

-

A6. Using multiple calls to construct the required expression:

-
-x <- 5
-call_obj1 <- call2(">", expr(x), 1)
-call_obj1
-#> x > 1
-
-call_obj2 <- call2("if", cond = call_obj1, cons.expr = "a", alt.expr = "b")
-call_obj2
-#> if (x > 1) "a" else "b"
-

This construction follows from the prefix form of this expression, revealed by its AST:

-
-ast(if (x > 1) "a" else "b")
-#> β–ˆβ”€`if` 
-#> β”œβ”€β–ˆβ”€`>` 
-#> β”‚ β”œβ”€x 
-#> β”‚ └─1 
-#> β”œβ”€"a" 
-#> └─"b"
-
-
-

-18.3 Parsing and grammar (Exercises 18.4.4) -

-

Q1. R uses parentheses in two slightly different ways as illustrated by these two calls:

-
-f((1))
-`(`(1 + 1)
-

Compare and contrast the two uses by referencing the AST.

-

A1. Let’s first have a look at the AST:

-
-ast(f((1)))
-#> β–ˆβ”€f 
-#> β””β”€β–ˆβ”€`(` 
-#>   └─1
-ast(`(`(1 + 1))
-#> β–ˆβ”€`(` 
-#> β””β”€β–ˆβ”€`+` 
-#>   β”œβ”€1 
-#>   └─1
-

As, you can see ( is being used in two separate ways:

-
    -
  • As a function in its own right "`(`" -
  • -
  • As part of the prefix syntax (f())
  • -
-

This is why, in the AST for f((1)), we see only one "`(`" (the first use case), and not for f(), which is part of the function syntax (the second use case).

-

Q2. = can also be used in two ways. Construct a simple example that shows both uses.

-

A2. Here is a simple example illustrating how = can also be used in two ways:

-
    -
  • for assignment
  • -
  • for named arguments in function calls
  • -
-
-m <- mean(x = 1)
-

We can also have a look at its AST:

-
-ast({
-  m <- mean(x = 1)
-})
-#> β–ˆβ”€`{` 
-#> β””β”€β–ˆβ”€`<-` 
-#>   β”œβ”€m 
-#>   β””β”€β–ˆβ”€mean 
-#>     └─x = 1
-

Q3. Does -2^2 yield 4 or -4? Why?

-

A3. The expression -2^2 evaluates to -4 because the operator ^ has higher precedence than the unary - operator:

-
--2^2
-#> [1] -4
-

The same can also be seen by its AST:

-
-ast(-2^2)
-#> β–ˆβ”€`-` 
-#> β””β”€β–ˆβ”€`^` 
-#>   β”œβ”€2 
-#>   └─2
-

A less confusing way to write this would be:

-
--(2^2)
-#> [1] -4
-

Q4. What does !1 + !1 return? Why?

-

A3. The expression !1 + !1 evaluates to FALSE.

-

This is because the ! operator has higher precedence than the unary + operator. Thus, !1 evaluates to FALSE, which is added to 1 + FALSE, which evaluates to 1, and then logically negated to !1, or FALSE.

-

This can be easily seen by its AST:

-
-ast(!1 + !1)
-#> β–ˆβ”€`!` 
-#> β””β”€β–ˆβ”€`+` 
-#>   β”œβ”€1 
-#>   β””β”€β–ˆβ”€`!` 
-#>     └─1
-

Q5. Why does x1 <- x2 <- x3 <- 0 work? Describe the two reasons.

-

A5. There are two reasons why the following works as expected:

-
-x1 <- x2 <- x3 <- 0
-
    -
  • The <- operator is right associative.
  • -
-

Therefore, the order of assignment here is:

-
-(x3 <- 0)
-(x2 <- x3)
-(x1 <- x2)
-
    -
  • The <- operator invisibly returns the assigned value.
  • -
-
-(x <- 1)
-#> [1] 1
-

This is easy to surmise from its AST:

-
-ast(x1 <- x2 <- x3 <- 0)
-#> β–ˆβ”€`<-` 
-#> β”œβ”€x1 
-#> β””β”€β–ˆβ”€`<-` 
-#>   β”œβ”€x2 
-#>   β””β”€β–ˆβ”€`<-` 
-#>     β”œβ”€x3 
-#>     └─0
-

Q6. Compare the ASTs of x + y %+% z and x ^ y %+% z. What have you learned about the precedence of custom infix functions?

-

A6. Looking at the ASTs for these expressions,

-
-ast(x + y %+% z)
-#> β–ˆβ”€`+` 
-#> β”œβ”€x 
-#> β””β”€β–ˆβ”€`%+%` 
-#>   β”œβ”€y 
-#>   └─z
-
-ast(x^y %+% z)
-#> β–ˆβ”€`%+%` 
-#> β”œβ”€β–ˆβ”€`^` 
-#> β”‚ β”œβ”€x 
-#> β”‚ └─y 
-#> └─z
-

we can say that the custom infix operator %+% has:

-
    -
  • higher precedence than the + operator
  • -
  • lower precedence than the ^ operator
  • -
-

Q7. What happens if you call parse_expr() with a string that generates multiple expressions? e.g.Β parse_expr("x + 1; y + 1")

-

A7. It produced an error:

-
-parse_expr("x + 1; y + 1")
-#> Error in `parse_expr()`:
-#> ! `x` must contain exactly 1 expression, not 2.
-

This is expected based on the docs:

-
-

parse_expr() returns one expression. If the text contains more than one expression (separated by semicolons or new lines), an error is issued.

-
-

We instead need to use parse_exprs():

-
-parse_exprs("x + 1; y + 1")
-#> [[1]]
-#> x + 1
-#> 
-#> [[2]]
-#> y + 1
-

Q8. What happens if you attempt to parse an invalid expression? e.g.Β "a +" or "f())".

-

A8. An invalid expression produces an error:

-
-parse_expr("a +")
-#> Error in parse(text = elt): <text>:2:0: unexpected end of input
-#> 1: a +
-#>    ^
-
-parse_expr("f())")
-#> Error in parse(text = elt): <text>:1:4: unexpected ')'
-#> 1: f())
-#>        ^
-

Since the underlying parse() function produces an error:

-
-parse(text = "a +")
-#> Error in parse(text = "a +"): <text>:2:0: unexpected end of input
-#> 1: a +
-#>    ^
-
-parse(text = "f())")
-#> Error in parse(text = "f())"): <text>:1:4: unexpected ')'
-#> 1: f())
-#>        ^
-

Q9. deparse() produces vectors when the input is long. For example, the following call produces a vector of length two:

-
-expr <- expr(g(a + b + c + d + e + f + g + h + i + j + k + l +
-  m + n + o + p + q + r + s + t + u + v + w + x + y + z))
-deparse(expr)
-

What does expr_text() do instead?

-

A9. The only difference between deparse() and expr_text() is that the latter turns the (possibly multi-line) expression into a single string.

-
-expr <- expr(g(a + b + c + d + e + f + g + h + i + j + k + l +
-  m + n + o + p + q + r + s + t + u + v + w + x + y + z))
-
-deparse(expr)
-#> [1] "g(a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + "
-#> [2] "    p + q + r + s + t + u + v + w + x + y + z)"
-
-expr_text(expr)
-#> [1] "g(a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + \n    p + q + r + s + t + u + v + w + x + y + z)"
-

Q10. pairwise.t.test() assumes that deparse() always returns a length one character vector. Can you construct an input that violates this expectation? What happens?

-

A10 Since R 4.0, it is not possible to violate this expectation since the new implementation produces a single string no matter the input:

-
-

New function deparse1() produces one string, wrapping deparse(), to be used typically in deparse1(substitute(*))

-
-
-
-

-18.4 Walking AST with recursive functions (Exercises 18.5.3) -

-

Q1. logical_abbr() returns TRUE for T(1, 2, 3). How could you modify logical_abbr_rec() so that it ignores function calls that use T or F?

-

A1. To avoid function calls that use T or F, we just need to ignore the function position in call objects:

-

Let’s try it out:

-
-logical_abbr_rec(expr(T(1, 2, 3)))
-#> [1] FALSE
-
-logical_abbr_rec(expr(F(1, 2, 3)))
-#> [1] FALSE
-
-logical_abbr_rec(expr(T))
-#> [1] TRUE
-
-logical_abbr_rec(expr(F))
-#> [1] TRUE
-

Q2. logical_abbr() works with expressions. It currently fails when you give it a function. Why? How could you modify logical_abbr() to make it work? What components of a function will you need to recurse over?

-
-logical_abbr(function(x = TRUE) {
-  g(x + T)
-})
-

A2. Surprisingly, logical_abbr() currently doesn’t fail with closures:

-

To see why, let’s see what type of object is produced when we capture user provided closure:

-
-print_enexpr <- function(.f) {
-  print(typeof(enexpr(.f)))
-  print(is.call(enexpr(.f)))
-}
-
-print_enexpr(function(x = TRUE) {
-  g(x + T)
-})
-#> [1] "language"
-#> [1] TRUE
-

Given that closures are converted to call objects, it is not a surprise that the function works:

-
-logical_abbr(function(x = TRUE) {
-  g(x + T)
-})
-#> [1] TRUE
-

The function only fails if it can’t find any negative case. For example, instead of returning FALSE, this produces an error for reasons that remain (as of yet) elusive to me:

- -
-logical_abbr(function(x = TRUE) {
-  g(x + TRUE)
-})
-#> Error: Don't know how to handle type integer
-

Q3. Modify find_assign to also detect assignment using replacement functions, i.e.Β names(x) <- y.

-

A3. Although both simple assignment (x <- y) and assignment using replacement functions (names(x) <- y) have <- operator in their call, in the latter case, names(x) will be a call object and not a symbol:

-
-expr1 <- expr(names(x) <- y)
-as.list(expr1)
-#> [[1]]
-#> `<-`
-#> 
-#> [[2]]
-#> names(x)
-#> 
-#> [[3]]
-#> y
-typeof(expr1[[2]])
-#> [1] "language"
-
-expr2 <- expr(x <- y)
-as.list(expr2)
-#> [[1]]
-#> `<-`
-#> 
-#> [[2]]
-#> x
-#> 
-#> [[3]]
-#> y
-typeof(expr2[[2]])
-#> [1] "symbol"
-

That’s how we can detect this kind of assignment by checking if the second element of the expression is a symbol or language type object.

-
-expr_type <- function(x) {
-  if (is_syntactic_literal(x)) {
-    "constant"
-  } else if (is.symbol(x)) {
-    "symbol"
-  } else if (is.call(x)) {
-    "call"
-  } else if (is.pairlist(x)) {
-    "pairlist"
-  } else {
-    typeof(x)
-  }
-}
-
-switch_expr <- function(x, ...) {
-  switch(expr_type(x),
-    ...,
-    stop("Don't know how to handle type ", typeof(x), call. = FALSE)
-  )
-}
-
-flat_map_chr <- function(.x, .f, ...) {
-  purrr::flatten_chr(purrr::map(.x, .f, ...))
-}
-
-extract_symbol <- function(x) {
-  if (is_symbol(x[[2]])) {
-    as_string(x[[2]])
-  } else {
-    extract_symbol(as.list(x[[2]]))
-  }
-}
-
-find_assign_call <- function(x) {
-  if (is_call(x, "<-") && is_symbol(x[[2]])) {
-    lhs <- as_string(x[[2]])
-    children <- as.list(x)[-1]
-  } else if (is_call(x, "<-") && is_call(x[[2]])) {
-    lhs <- extract_symbol(as.list(x[[2]]))
-    children <- as.list(x)[-1]
-  } else {
-    lhs <- character()
-    children <- as.list(x)
-  }
-
-  c(lhs, flat_map_chr(children, find_assign_rec))
-}
-
-find_assign_rec <- function(x) {
-  switch_expr(x,
-    # Base cases
-    constant = ,
-    symbol = character(),
-
-    # Recursive cases
-    pairlist = flat_map_chr(x, find_assign_rec),
-    call = find_assign_call(x)
-  )
-}
-
-find_assign <- function(x) find_assign_rec(enexpr(x))
-

Let’s try it out:

-
-find_assign(names(x))
-#> character(0)
-
-find_assign(names(x) <- y)
-#> [1] "x"
-
-find_assign(names(f(x)) <- y)
-#> [1] "x"
-
-find_assign(names(x) <- y <- z <- NULL)
-#> [1] "x" "y" "z"
-
-find_assign(a <- b <- c <- 1)
-#> [1] "a" "b" "c"
-
-find_assign(system.time(x <- print(y <- 5)))
-#> [1] "x" "y"
-

Q4. Write a function that extracts all calls to a specified function.

-

A4. Here is a function that extracts all calls to a specified function:

-
-find_function_call <- function(x, .f) {
-  if (is_call(x)) {
-    if (is_call(x, .f)) {
-      list(x)
-    } else {
-      purrr::map(as.list(x), ~ find_function_call(.x, .f)) %>%
-        purrr::compact() %>%
-        unlist(use.names = FALSE)
-    }
-  }
-}
-
-# example-1: with infix operator `:`
-find_function_call(expr(mean(1:2)), ":")
-#> [[1]]
-#> 1:2
-
-find_function_call(expr(sum(mean(1:2))), ":")
-#> [[1]]
-#> 1:2
-
-find_function_call(expr(list(1:5, 4:6, 3:9)), ":")
-#> [[1]]
-#> 1:5
-#> 
-#> [[2]]
-#> 4:6
-#> 
-#> [[3]]
-#> 3:9
-
-find_function_call(expr(list(1:5, sum(4:6), mean(3:9))), ":")
-#> [[1]]
-#> 1:5
-#> 
-#> [[2]]
-#> 4:6
-#> 
-#> [[3]]
-#> 3:9
-
-# example-2: with assignment operator `<-`
-find_function_call(expr(names(x)), "<-")
-#> NULL
-
-find_function_call(expr(names(x) <- y), "<-")
-#> [[1]]
-#> names(x) <- y
-
-find_function_call(expr(names(f(x)) <- y), "<-")
-#> [[1]]
-#> names(f(x)) <- y
-
-find_function_call(expr(names(x) <- y <- z <- NULL), "<-")
-#> [[1]]
-#> names(x) <- y <- z <- NULL
-
-find_function_call(expr(a <- b <- c <- 1), "<-")
-#> [[1]]
-#> a <- b <- c <- 1
-
-find_function_call(expr(system.time(x <- print(y <- 5))), "<-")
-#> [[1]]
-#> x <- print(y <- 5)
-
-
-

-18.5 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>    crayon        1.5.2      2022-09-29 [1] CRAN (R 4.2.1)
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    lobstr      * 1.1.2      2022-06-22 [1] CRAN (R 4.2.0)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    purrr         0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang       * 1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/function-factories.html b/_book/function-factories.html deleted file mode 100644 index 03f71c62..00000000 --- a/_book/function-factories.html +++ /dev/null @@ -1,744 +0,0 @@ - - - - - - -Chapter 10 Function factories | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-10 Function factories -

-

Attaching the needed libraries:

-
-library(rlang, warn.conflicts = FALSE)
-library(ggplot2, warn.conflicts = FALSE)
-
-

-10.1 Factory fundamentals (Exercises 10.2.6) -

-
-

Q1. The definition of force() is simple:

-
-force
-#> function (x) 
-#> x
-#> <bytecode: 0x15e049708>
-#> <environment: namespace:base>
-

Why is it better to force(x) instead of just x?

-

A1. Due to lazy evaluation, argument to a function won’t be evaluated until its value is needed. But sometimes we may want to have eager evaluation, and using force() makes this intent clearer.

-
-

Q2. Base R contains two function factories, approxfun() and ecdf(). Read their documentation and experiment to figure out what the functions do and what they return.

-

A2. About the two function factories-

- -

This function factory returns a function performing the linear (or constant) interpolation.

-
-x <- 1:10
-y <- rnorm(10)
-f <- approxfun(x, y)
-f
-#> function (v) 
-#> .approxfun(x, y, v, method, yleft, yright, f, na.rm)
-#> <bytecode: 0x10d0c0880>
-#> <environment: 0x10d0bfe00>
-f(x)
-#>  [1] -0.7786629 -0.3894764 -2.0337983 -0.9823731  0.2478901
-#>  [6] -2.1038646 -0.3814180  2.0749198  1.0271384  0.4730142
-curve(f(x), 0, 11)
-
- -

This function factory computes an empirical cumulative distribution function.

-
-x <- rnorm(12)
-f <- ecdf(x)
-f
-#> Empirical CDF 
-#> Call: ecdf(x)
-#>  x[1:12] = -1.8793, -1.3221, -1.2392,  ..., 1.1604, 1.7956
-f(seq(-2, 2, by = 0.1))
-#>  [1] 0.00000000 0.00000000 0.08333333 0.08333333 0.08333333
-#>  [6] 0.08333333 0.08333333 0.16666667 0.25000000 0.25000000
-#> [11] 0.33333333 0.33333333 0.33333333 0.41666667 0.41666667
-#> [16] 0.41666667 0.41666667 0.50000000 0.58333333 0.58333333
-#> [21] 0.66666667 0.75000000 0.75000000 0.75000000 0.75000000
-#> [26] 0.75000000 0.75000000 0.75000000 0.75000000 0.83333333
-#> [31] 0.83333333 0.83333333 0.91666667 0.91666667 0.91666667
-#> [36] 0.91666667 0.91666667 0.91666667 1.00000000 1.00000000
-#> [41] 1.00000000
-
-

Q3. Create a function pick() that takes an index, i, as an argument and returns a function with an argument x that subsets x with i.

-
-pick(1)(x)
-# should be equivalent to
-x[[1]]
-
-lapply(mtcars, pick(5))
-# should be equivalent to
-lapply(mtcars, function(x) x[[5]])
-

A3. To write desired function, we just need to make sure that the argument i is eagerly evaluated.

-
-pick <- function(i) {
-  force(i)
-  function(x) x[[i]]
-}
-

Testing it with specified test cases:

-
-x <- list("a", "b", "c")
-identical(x[[1]], pick(1)(x))
-#> [1] TRUE
-
-identical(
-  lapply(mtcars, pick(5)),
-  lapply(mtcars, function(x) x[[5]])
-)
-#> [1] TRUE
-
-

Q4. Create a function that creates functions that compute the ithcentral moment of a numeric vector. You can test it by running the following code:

-
-m1 <- moment(1)
-m2 <- moment(2)
-x <- runif(100)
-stopifnot(all.equal(m1(x), 0))
-stopifnot(all.equal(m2(x), var(x) * 99 / 100))
-

A4. The following function satisfied the specified requirements:

-
-moment <- function(k) {
-  force(k)
-
-  function(x) (sum((x - mean(x))^k)) / length(x)
-}
-

Testing it with specified test cases:

-
-m1 <- moment(1)
-m2 <- moment(2)
-x <- runif(100)
-
-stopifnot(all.equal(m1(x), 0))
-stopifnot(all.equal(m2(x), var(x) * 99 / 100))
-
-

Q5. What happens if you don’t use a closure? Make predictions, then verify with the code below.

-
-i <- 0
-new_counter2 <- function() {
-  i <<- i + 1
-  i
-}
-

A5. In case closures are not used in this context, the counts are stored in a global variable, which can be modified by other processes or even deleted.

-
-new_counter2()
-#> [1] 1
-
-new_counter2()
-#> [1] 2
-
-new_counter2()
-#> [1] 3
-
-i <- 20
-new_counter2()
-#> [1] 21
-
-

Q6. What happens if you use <- instead of <<-? Make predictions, then verify with the code below.

-
-new_counter3 <- function() {
-  i <- 0
-  function() {
-    i <- i + 1
-    i
-  }
-}
-

A6. In this case, the function will always return 1.

-
-new_counter3()
-#> function() {
-#>     i <- i + 1
-#>     i
-#>   }
-#> <environment: 0x13d35f858>
-
-new_counter3()
-#> function() {
-#>     i <- i + 1
-#>     i
-#>   }
-#> <bytecode: 0x10f01fb30>
-#> <environment: 0x13d3dac88>
-
-
-
-

-10.2 Graphical factories (Exercises 10.3.4) -

-
-

Q1. Compare and contrast ggplot2::label_bquote() with scales::number_format().

-

A1. To compare and contrast, let’s first look at the source code for these functions:

- -
-ggplot2::label_bquote
-#> function (rows = NULL, cols = NULL, default) 
-#> {
-#>     cols_quoted <- substitute(cols)
-#>     rows_quoted <- substitute(rows)
-#>     call_env <- env_parent()
-#>     fun <- function(labels) {
-#>         quoted <- resolve_labeller(rows_quoted, cols_quoted, 
-#>             labels)
-#>         if (is.null(quoted)) {
-#>             return(label_value(labels))
-#>         }
-#>         evaluate <- function(...) {
-#>             params <- list(...)
-#>             params <- as_environment(params, call_env)
-#>             eval(substitute(bquote(expr, params), list(expr = quoted)))
-#>         }
-#>         list(inject(mapply(evaluate, !!!labels, SIMPLIFY = FALSE)))
-#>     }
-#>     structure(fun, class = "labeller")
-#> }
-#> <bytecode: 0x10dd51568>
-#> <environment: namespace:ggplot2>
- -
-scales::number_format
-#> function (accuracy = NULL, scale = 1, prefix = "", suffix = "", 
-#>     big.mark = " ", decimal.mark = ".", style_positive = c("none", 
-#>         "plus"), style_negative = c("hyphen", "minus", "parens"), 
-#>     scale_cut = NULL, trim = TRUE, ...) 
-#> {
-#>     force_all(accuracy, scale, prefix, suffix, big.mark, decimal.mark, 
-#>         style_positive, style_negative, scale_cut, trim, ...)
-#>     function(x) {
-#>         number(x, accuracy = accuracy, scale = scale, prefix = prefix, 
-#>             suffix = suffix, big.mark = big.mark, decimal.mark = decimal.mark, 
-#>             style_positive = style_positive, style_negative = style_negative, 
-#>             scale_cut = scale_cut, trim = trim, ...)
-#>     }
-#> }
-#> <bytecode: 0x10c5164d0>
-#> <environment: namespace:scales>
-

Both of these functions return formatting functions used to style the facets labels and other labels to have the desired format in ggplot2 plots.

-

For example, using plotmath expression in the facet label:

-
-library(ggplot2)
-
-p <- ggplot(mtcars, aes(wt, mpg)) +
-  geom_point()
-p + facet_grid(. ~ vs, labeller = label_bquote(cols = alpha^.(vs)))
-
-

Or to display axes labels in the desired format:

-
-library(scales)
-
-ggplot(mtcars, aes(wt, mpg)) +
-  geom_point() +
-  scale_y_continuous(labels = number_format(accuracy = 0.01, decimal.mark = ","))
-
-

The ggplot2::label_bquote() adds an additional class to the returned function.

-

The scales::number_format() function is a simple pass-through method that forces evaluation of all its parameters and passes them on to the underlying scales::number() function.

-
-
-
-

-10.3 Statistical factories (Exercises 10.4.4) -

-
-

Q1. In boot_model(), why don’t I need to force the evaluation of df or model?

-

A1. We don’t need to force the evaluation of df or model because these arguments are automatically evaluated by lm():

-
-boot_model <- function(df, formula) {
-  mod <- lm(formula, data = df)
-  fitted <- unname(fitted(mod))
-  resid <- unname(resid(mod))
-  rm(mod)
-
-  function() {
-    fitted + sample(resid)
-  }
-}
-
-

Q2. Why might you formulate the Box-Cox transformation like this?

-
-boxcox3 <- function(x) {
-  function(lambda) {
-    if (lambda == 0) {
-      log(x)
-    } else {
-      (x^lambda - 1) / lambda
-    }
-  }
-}
-

A2. To see why we formulate this transformation like above, we can compare it to the one mentioned in the book:

-
-boxcox2 <- function(lambda) {
-  if (lambda == 0) {
-    function(x) log(x)
-  } else {
-    function(x) (x^lambda - 1) / lambda
-  }
-}
-

Let’s have a look at one example with each:

-
-boxcox2(1)
-#> function(x) (x^lambda - 1) / lambda
-#> <environment: 0x13de777e0>
-
-boxcox3(mtcars$wt)
-#> function(lambda) {
-#>     if (lambda == 0) {
-#>       log(x)
-#>     } else {
-#>       (x^lambda - 1) / lambda
-#>     }
-#>   }
-#> <environment: 0x13de05540>
-

As can be seen:

-
    -
  • in boxcox2(), we can vary x for the same value of lambda, while
  • -
  • in boxcox3(), we can vary lambda for the same vector.
  • -
-

Thus, boxcox3() can be handy while exploring different transformations across inputs.

-
-

Q3. Why don’t you need to worry that boot_permute() stores a copy of the data inside the function that it generates?

-

A3. If we look at the source code generated by the function factory, we notice that the exact data frame (mtcars) is not referenced:

-
-boot_permute <- function(df, var) {
-  n <- nrow(df)
-  force(var)
-
-  function() {
-    col <- df[[var]]
-    col[sample(n, replace = TRUE)]
-  }
-}
-
-boot_permute(mtcars, "mpg")
-#> function() {
-#>     col <- df[[var]]
-#>     col[sample(n, replace = TRUE)]
-#>   }
-#> <environment: 0x13ded94a0>
-

This is why we don’t need to worry about a copy being made because the df in the function environment points to the memory address of the data frame. We can confirm this by comparing their memory addresses:

-
-boot_permute_env <- rlang::fn_env(boot_permute(mtcars, "mpg"))
-rlang::env_print(boot_permute_env)
-#> <environment: 0x13dfeda78>
-#> Parent: <environment: global>
-#> Bindings:
-#> β€’ n: <int>
-#> β€’ df: <df[,11]>
-#> β€’ var: <chr>
-
-identical(
-  lobstr::obj_addr(boot_permute_env$df),
-  lobstr::obj_addr(mtcars)
-)
-#> [1] TRUE
-

We can also check that the values of these bindings are the same as what we entered into the function factory:

-
-identical(boot_permute_env$df, mtcars)
-#> [1] TRUE
-identical(boot_permute_env$var, "mpg")
-#> [1] TRUE
-
-

Q4. How much time does ll_poisson2() save compared to ll_poisson1()? Use bench::mark() to see how much faster the optimisation occurs. How does changing the length of x change the results?

-

A4. Let’s first compare the performance of these functions with the example in the book:

-
-ll_poisson1 <- function(x) {
-  n <- length(x)
-
-  function(lambda) {
-    log(lambda) * sum(x) - n * lambda - sum(lfactorial(x))
-  }
-}
-
-ll_poisson2 <- function(x) {
-  n <- length(x)
-  sum_x <- sum(x)
-  c <- sum(lfactorial(x))
-
-  function(lambda) {
-    log(lambda) * sum_x - n * lambda - c
-  }
-}
-
-x1 <- c(41, 30, 31, 38, 29, 24, 30, 29, 31, 38)
-
-bench::mark(
-  "LL1" = optimise(ll_poisson1(x1), c(0, 100), maximum = TRUE),
-  "LL2" = optimise(ll_poisson2(x1), c(0, 100), maximum = TRUE)
-)
-#> # A tibble: 2 Γ— 6
-#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
-#> 1 LL1         15.79Β΅s   34.8Β΅s    20353.    12.8KB     30.5
-#> 2 LL2          8.49Β΅s   10.5Β΅s    56011.        0B     39.2
-

As can be seen, the second version is much faster than the first version.

-

We can also vary the length of the vector and confirm that across a wide range of vector lengths, this performance advantage is observed.

-
-generate_ll_benches <- function(n) {
-  x_vec <- sample.int(n, n)
-
-  bench::mark(
-    "LL1" = optimise(ll_poisson1(x_vec), c(0, 100), maximum = TRUE),
-    "LL2" = optimise(ll_poisson2(x_vec), c(0, 100), maximum = TRUE)
-  )[1:4] %>%
-    dplyr::mutate(length = n, .before = expression)
-}
-
-(df_bench <- purrr::map_dfr(
-  .x = c(10, 20, 50, 100, 1000),
-  .f = ~ generate_ll_benches(n = .x)
-))
-#> # A tibble: 10 Γ— 5
-#>    length expression      min   median `itr/sec`
-#>     <dbl> <bch:expr> <bch:tm> <bch:tm>     <dbl>
-#>  1     10 LL1          25.1Β΅s   38.2Β΅s    15958.
-#>  2     10 LL2          10.4Β΅s   14.2Β΅s    46876.
-#>  3     20 LL1          27.4Β΅s   37.9Β΅s    19378.
-#>  4     20 LL2          10.1Β΅s   13.8Β΅s    52872.
-#>  5     50 LL1          32.8Β΅s   46.2Β΅s     9780.
-#>  6     50 LL2            10Β΅s     15Β΅s    46956.
-#>  7    100 LL1          45.6Β΅s   62.6Β΅s    11691.
-#>  8    100 LL2            11Β΅s   14.8Β΅s    44373.
-#>  9   1000 LL1         633.4Β΅s  924.7Β΅s      823.
-#> 10   1000 LL2          36.6Β΅s   50.2Β΅s    14557.
-
-ggplot(
-  df_bench,
-  aes(
-    x = as.numeric(length),
-    y = median,
-    group = as.character(expression),
-    color = as.character(expression)
-  )
-) +
-  geom_point() +
-  geom_line() +
-  labs(
-    x = "Vector length",
-    y = "Median Execution Time",
-    colour = "Function used"
-  )
-
-
-
-
-

-10.4 Function factories + functionals (Exercises 10.5.1) -

-

Q1. Which of the following commands is equivalent to with(x, f(z))?

-
(a) `x$f(x$z)`.
-(b) `f(x$z)`.
-(c) `x$f(z)`.
-(d) `f(z)`.
-(e) It depends.
-

A1. It depends on whether with() is used with a data frame or a list.

-
-f <- mean
-z <- 1
-x <- list(f = mean, z = 1)
-
-identical(with(x, f(z)), x$f(x$z))
-#> [1] TRUE
-
-identical(with(x, f(z)), f(x$z))
-#> [1] TRUE
-
-identical(with(x, f(z)), x$f(z))
-#> [1] TRUE
-
-identical(with(x, f(z)), f(z))
-#> [1] TRUE
-
-

Q2. Compare and contrast the effects of env_bind() vs.Β attach() for the following code.

-

A2. Let’s compare and contrast the effects of env_bind() vs.Β attach().

-
    -
  • -attach() adds funs to the search path. Since these functions have the same names as functions in {base} package, the attached names mask the ones in the {base} package.
  • -
-
-funs <- list(
-  mean = function(x) mean(x, na.rm = TRUE),
-  sum = function(x) sum(x, na.rm = TRUE)
-)
-
-attach(funs)
-#> The following objects are masked from package:base:
-#> 
-#>     mean, sum
-
-mean
-#> function(x) mean(x, na.rm = TRUE)
-head(search())
-#> [1] ".GlobalEnv"       "funs"             "package:scales"  
-#> [4] "package:ggplot2"  "package:rlang"    "package:magrittr"
-
-mean <- function(x) stop("Hi!")
-mean
-#> function(x) stop("Hi!")
-head(search())
-#> [1] ".GlobalEnv"       "funs"             "package:scales"  
-#> [4] "package:ggplot2"  "package:rlang"    "package:magrittr"
-
-detach(funs)
-
    -
  • -env_bind() adds the functions in funs to the global environment, instead of masking the names in the {base} package.
  • -
-
-env_bind(globalenv(), !!!funs)
-mean
-#> function(x) mean(x, na.rm = TRUE)
-
-mean <- function(x) stop("Hi!")
-mean
-#> function(x) stop("Hi!")
-env_unbind(globalenv(), names(funs))
-

Note that there is no "funs" in this output.

-
-
-
-

-10.5 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    assertthat    0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bench         1.1.2      2021-11-30 [1] CRAN (R 4.2.0)
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>    colorspace    2.0-3      2022-02-21 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    DBI           1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    dplyr         1.0.10     2022-09-01 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    farver        2.1.1      2022-07-06 [1] CRAN (R 4.2.1)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    ggplot2     * 3.4.0      2022-11-04 [1] CRAN (R 4.2.2)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>  P grid          4.2.2      2022-10-31 [1] local
-#>    gtable        0.3.1      2022-09-01 [1] CRAN (R 4.2.1)
-#>    highr         0.9        2021-04-16 [1] CRAN (R 4.2.0)
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    labeling      0.4.2      2020-10-20 [1] CRAN (R 4.2.0)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    lobstr        1.1.2      2022-06-22 [1] CRAN (R 4.2.0)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    munsell       0.5.0      2018-06-12 [1] CRAN (R 4.2.0)
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    profmem       0.6.0      2020-12-13 [1] CRAN (R 4.2.0)
-#>    purrr         0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang       * 1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    scales      * 1.2.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble        3.1.8.9002 2022-10-16 [1] local
-#>    tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/function-operators.html b/_book/function-operators.html deleted file mode 100644 index fad96831..00000000 --- a/_book/function-operators.html +++ /dev/null @@ -1,494 +0,0 @@ - - - - - - -Chapter 11 Function operators | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-11 Function operators -

-

Attaching the needed libraries:

-
-library(purrr, warn.conflicts = FALSE)
-
-

-11.1 Existing function operators (Exercises 11.2.3) -

-
-

Q1. Base R provides a function operator in the form of Vectorize(). What does it do? When might you use it?

-

A1. Vectorize() function creates a function that vectorizes the action of the provided function over specified arguments (i.e., it acts on each element of the vector). We will see its utility by solving a problem that otherwise would be difficult to solve.

-

The problem is to find indices of matching numeric values for the given threshold by creating a hybrid of the following functions:

-
    -
  • -%in% (which doesn’t provide any way to provide tolerance when comparing numeric values),
  • -
  • -dplyr::near() (which is vectorized element-wise and thus expects two vectors of equal length)
  • -
-
-which_near <- function(x, y, tolerance) {
-  # Vectorize `dplyr::near()` function only over the `y` argument.
-  # `Vectorize()` is a function operator and will return a function.
-  customNear <- Vectorize(dplyr::near, vectorize.args = c("y"), SIMPLIFY = FALSE)
-
-  # Apply the vectorized function to vector arguments and then check where the
-  # comparisons are equal (i.e. `TRUE`) using `which()`.
-  #
-  # Use `compact()` to remove empty elements from the resulting list.
-  index_list <- purrr::compact(purrr::map(customNear(x, y, tol = tolerance), which))
-
-  # If there are any matches, return the indices as an atomic vector of integers.
-  if (length(index_list) > 0L) {
-    index_vector <- purrr::simplify(index_list, "integer")
-    return(index_vector)
-  }
-
-  # If there are no matches
-  return(integer(0L))
-}
-

Let’s use it:

-
-x1 <- c(2.1, 3.3, 8.45, 8, 6)
-x2 <- c(6, 8.40, 3)
-
-which_near(x1, x2, tolerance = 0.1)
-#> [1] 5 3
-

Note that we needed to create a new function for this because neither of the existing functions do what we want.

-
-which(x1 %in% x2)
-#> [1] 5
-
-which(dplyr::near(x1, x2, tol = 0.1))
-#> Warning in x - y: longer object length is not a multiple of
-#> shorter object length
-#> integer(0)
-

We solved a complex task here using the Vectorize() function!

-
-

Q2. Read the source code for possibly(). How does it work?

-

A2. Let’s have a look at the source code for this function:

-
-possibly
-#> function (.f, otherwise, quiet = TRUE) 
-#> {
-#>     .f <- as_mapper(.f)
-#>     force(otherwise)
-#>     function(...) {
-#>         tryCatch(.f(...), error = function(e) {
-#>             if (!quiet) 
-#>                 message("Error: ", e$message)
-#>             otherwise
-#>         }, interrupt = function(e) {
-#>             stop("Terminated by user", call. = FALSE)
-#>         })
-#>     }
-#> }
-#> <bytecode: 0x114205948>
-#> <environment: namespace:purrr>
-

Looking at this code, we can see that possibly():

-
    -
  • uses tryCatch() for error handling
  • -
  • has a parameter otherwise to specify default value in case an error occurs
  • -
  • has a parameter quiet to suppress error message (if needed)
  • -
-
-

Q3. Read the source code for safely(). How does it work?

-

A3. Let’s have a look at the source code for this function:

-
-safely
-#> function (.f, otherwise = NULL, quiet = TRUE) 
-#> {
-#>     .f <- as_mapper(.f)
-#>     function(...) capture_error(.f(...), otherwise, quiet)
-#> }
-#> <bytecode: 0x114311948>
-#> <environment: namespace:purrr>
-
-purrr:::capture_error
-#> function (code, otherwise = NULL, quiet = TRUE) 
-#> {
-#>     tryCatch(list(result = code, error = NULL), error = function(e) {
-#>         if (!quiet) 
-#>             message("Error: ", e$message)
-#>         list(result = otherwise, error = e)
-#>     }, interrupt = function(e) {
-#>         stop("Terminated by user", call. = FALSE)
-#>     })
-#> }
-#> <bytecode: 0x1149804a8>
-#> <environment: namespace:purrr>
-

Looking at this code, we can see that safely():

-
    -
  • uses a list to save both the results (if the function executes successfully) and the error (if it fails)
  • -
  • uses tryCatch() for error handling
  • -
  • has a parameter otherwise to specify default value in case an error occurs
  • -
  • has a parameter quiet to suppress error message (if needed)
  • -
-
-
-
-

-11.2 Case study: Creating your own function operators (Exercises 11.3.1) -

-
-

Q1. Weigh the pros and cons of download.file %>% dot_every(10) %>% delay_by(0.1) versus download.file %>% delay_by(0.1) %>% dot_every(10).

-

A1. Although both of these chains of piped operations produce the same number of dots and would need the same amount of time, there is a subtle difference in how they do this.

-
    -
  • download.file %>% dot_every(10) %>% delay_by(0.1)
  • -
-

Here, the printing of the dot is also delayed, and the first dot is printed when the 10th URL download starts.

-
    -
  • download.file %>% delay_by(0.1) %>% dot_every(10)
  • -
-

Here, the first dot is printed after the 9th download is finished, and the 10th download starts after a short delay.

-
-

Q2. Should you memoise download.file()? Why or why not?

-

A2. Since download.file() is meant to download files from the Internet, memoising it is not recommended for the following reasons:

-
    -
  • Memoization is helpful when giving the same input the function returns the same output. This is not necessarily the case for webpages since they constantly change, and you may continue to β€œdownload” an outdated version of the webpage.

  • -
  • Memoization works by caching results, which can take up a significant amount of memory.

  • -
-
-

Q3. Create a function operator that reports whenever a file is created or deleted in the working directory, using dir() and setdiff(). What other global function effects might you want to track?

-

A3. First, let’s create helper functions to compare and print added or removed filenames:

-
-print_multiple_entries <- function(header, entries) {
-  message(paste0(header, ":\n"), paste0(entries, collapse = "\n"))
-}
-
-file_comparator <- function(old, new) {
-  if (setequal(old, new)) {
-    return()
-  }
-
-  removed <- setdiff(old, new)
-  added <- setdiff(new, old)
-
-  if (length(removed) > 0L) print_multiple_entries("- File removed", removed)
-  if (length(added) > 0L) print_multiple_entries("- File added", added)
-}
-

We can then write a function operator and use it to create functions that will do the necessary tracking:

-
-dir_tracker <- function(f) {
-  force(f)
-  function(...) {
-    old_files <- dir()
-    on.exit(file_comparator(old_files, dir()), add = TRUE)
-
-    f(...)
-  }
-}
-
-file_creation_tracker <- dir_tracker(file.create)
-file_deletion_tracker <- dir_tracker(file.remove)
-

Let’s try it out:

-
-file_creation_tracker(c("a.txt", "b.txt"))
-#> - File added:
-#> a.txt
-#> b.txt
-#> [1] TRUE TRUE
-
-file_deletion_tracker(c("a.txt", "b.txt"))
-#> - File removed:
-#> a.txt
-#> b.txt
-#> [1] TRUE TRUE
-

Other global function effects we might want to track:

-
    -
  • working directory
  • -
  • environment variables
  • -
  • connections
  • -
  • library paths
  • -
  • graphics devices
  • -
  • etc.
  • -
-
-

Q4. Write a function operator that logs a timestamp and message to a file every time a function is run.

-

A4. The following function operator logs a timestamp and message to a file every time a function is run:

-
-# helper function to write to a file connection
-write_line <- function(filepath, ...) {
-  cat(..., "\n", sep = "", file = filepath, append = TRUE)
-}
-
-# function operator
-logger <- function(f, filepath) {
-  force(f)
-  force(filepath)
-
-  write_line(filepath, "Function created at: ", as.character(Sys.time()))
-
-  function(...) {
-    write_line(filepath, "Function called at:  ", as.character(Sys.time()))
-    f(...)
-  }
-}
-
-# check that the function works as expected with a tempfile
-withr::with_tempfile("logfile", code = {
-  logged_runif <- logger(runif, logfile)
-
-  Sys.sleep(sample.int(10, 1))
-  logged_runif(1)
-
-  Sys.sleep(sample.int(10, 1))
-  logged_runif(2)
-
-  Sys.sleep(sample.int(10, 1))
-  logged_runif(3)
-
-  cat(readLines(logfile), sep = "\n")
-})
-#> Function created at: 2022-11-12 11:49:04
-#> Function called at:  2022-11-12 11:49:09
-#> Function called at:  2022-11-12 11:49:14
-#> Function called at:  2022-11-12 11:49:22
-
-

Q5. Modify delay_by() so that instead of delaying by a fixed amount of time, it ensures that a certain amount of time has elapsed since the function was last called. That is, if you called g <- delay_by(1, f); g(); Sys.sleep(2); g() there shouldn’t be an extra delay.

-

A5. Modified version of the function meeting the specified requirements:

-
-delay_by_atleast <- function(f, amount) {
-  force(f)
-  force(amount)
-
-  # the last time the function was run
-  last_time <- NULL
-
-  function(...) {
-    if (!is.null(last_time)) {
-      wait <- (last_time - Sys.time()) + amount
-      if (wait > 0) Sys.sleep(wait)
-    }
-
-    # update the time in the parent frame for the next run when the function finishes
-    on.exit(last_time <<- Sys.time())
-
-    f(...)
-  }
-}
-
-
-
-

-11.3 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    assertthat    0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    DBI           1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    dplyr         1.0.10     2022-09-01 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    purrr       * 0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang         1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble        3.1.8.9002 2022-10-16 [1] local
-#>    tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/functionals.html b/_book/functionals.html deleted file mode 100644 index 8bee5649..00000000 --- a/_book/functionals.html +++ /dev/null @@ -1,1039 +0,0 @@ - - - - - - -Chapter 9 Functionals | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-9 Functionals -

-

Attaching the needed libraries:

-
-library(purrr, warn.conflicts = FALSE)
-
-

-9.1 My first functional: map() (Exercises 9.2.6) -

-
-

Q1. Use as_mapper() to explore how purrr generates anonymous functions for the integer, character, and list helpers. What helper allows you to extract attributes? Read the documentation to find out.

-

A1. Let’s handle the two parts of the question separately.

- -

Looking at the experimentation below with map() and as_mapper(), we can see that, depending on the type of the input, as_mapper() creates an extractor function using pluck().

-
-# mapping by position -----------------------
-
-x <- list(1, list(2, 3, list(1, 2)))
-
-map(x, 1)
-#> [[1]]
-#> [1] 1
-#> 
-#> [[2]]
-#> [1] 2
-as_mapper(1)
-#> function (x, ...) 
-#> pluck(x, 1, .default = NULL)
-#> <environment: 0x13438ef70>
-
-map(x, list(2, 1))
-#> [[1]]
-#> NULL
-#> 
-#> [[2]]
-#> [1] 3
-as_mapper(list(2, 1))
-#> function (x, ...) 
-#> pluck(x, 2, 1, .default = NULL)
-#> <environment: 0x154e04d80>
-
-# mapping by name -----------------------
-
-y <- list(
-  list(m = "a", list(1, m = "mo")),
-  list(n = "b", list(2, n = "no"))
-)
-
-map(y, "m")
-#> [[1]]
-#> [1] "a"
-#> 
-#> [[2]]
-#> NULL
-as_mapper("m")
-#> function (x, ...) 
-#> pluck(x, "m", .default = NULL)
-#> <environment: 0x154450020>
-
-# mixing position and name
-map(y, list(2, "m"))
-#> [[1]]
-#> [1] "mo"
-#> 
-#> [[2]]
-#> NULL
-as_mapper(list(2, "m"))
-#> function (x, ...) 
-#> pluck(x, 2, "m", .default = NULL)
-#> <environment: 0x154edbd20>
-
-# compact functions ----------------------------
-
-map(y, ~ length(.x))
-#> [[1]]
-#> [1] 2
-#> 
-#> [[2]]
-#> [1] 2
-as_mapper(~ length(.x))
-#> <lambda>
-#> function (..., .x = ..1, .y = ..2, . = ..1) 
-#> length(.x)
-#> attr(,"class")
-#> [1] "rlang_lambda_function" "function"
- -
-pluck(Titanic, attr_getter("class"))
-#> [1] "table"
-
-

Q2. map(1:3, ~ runif(2)) is a useful pattern for generating random numbers, but map(1:3, runif(2)) is not. Why not? Can you explain why it returns the result that it does?

-

A2. As shown by as_mapper() outputs below, the second call is not appropriate for generating random numbers because it translates to pluck() function where the indices for plucking are taken to be randomly generated numbers, and these are not valid accessors and so we get NULLs in return.

-
-map(1:3, ~ runif(2))
-#> [[1]]
-#> [1] 0.2180892 0.9876342
-#> 
-#> [[2]]
-#> [1] 0.3484619 0.3810470
-#> 
-#> [[3]]
-#> [1] 0.02098596 0.74972687
-as_mapper(~ runif(2))
-#> <lambda>
-#> function (..., .x = ..1, .y = ..2, . = ..1) 
-#> runif(2)
-#> attr(,"class")
-#> [1] "rlang_lambda_function" "function"
-
-map(1:3, runif(2))
-#> [[1]]
-#> NULL
-#> 
-#> [[2]]
-#> NULL
-#> 
-#> [[3]]
-#> NULL
-as_mapper(runif(2))
-#> function (x, ...) 
-#> pluck(x, 0.597890264587477, 0.587997315218672, .default = NULL)
-#> <environment: 0x14576e980>
-
-

Q3. Use the appropriate map() function to:

-
a) Compute the standard deviation of every column in a numeric data frame.
-
-a) Compute the standard deviation of every numeric column in a mixed data frame. (Hint: you'll need to do it in two steps.)
-
-a) Compute the number of levels for every factor in a data frame.
-

A3. Using the appropriate map() function to:

-
    -
  • Compute the standard deviation of every column in a numeric data frame:
  • -
-
-map_dbl(mtcars, sd)
-#>         mpg         cyl        disp          hp        drat 
-#>   6.0269481   1.7859216 123.9386938  68.5628685   0.5346787 
-#>          wt        qsec          vs          am        gear 
-#>   0.9784574   1.7869432   0.5040161   0.4989909   0.7378041 
-#>        carb 
-#>   1.6152000
-
    -
  • Compute the standard deviation of every numeric column in a mixed data frame:
  • -
-
-keep(iris, is.numeric) %>%
-  map_dbl(sd)
-#> Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
-#>    0.8280661    0.4358663    1.7652982    0.7622377
-
    -
  • Compute the number of levels for every factor in a data frame:
  • -
-
-modify_if(dplyr::starwars, is.character, as.factor) %>%
-  keep(is.factor) %>%
-  map_int(~ length(levels(.)))
-#>       name hair_color skin_color  eye_color        sex 
-#>         87         12         31         15          4 
-#>     gender  homeworld    species 
-#>          2         48         37
-
-

Q4. The following code simulates the performance of a t-test for non-normal data. Extract the p-value from each test, then visualise.

-
-trials <- map(1:100, ~ t.test(rpois(10, 10), rpois(7, 10)))
-

A4.

-
    -
  • Extract the p-value from each test:
  • -
-
-trials <- map(1:100, ~ t.test(rpois(10, 10), rpois(7, 10)))
-
-(p <- map_dbl(trials, "p.value"))
-#>   [1] 0.81695628 0.53177360 0.94750819 0.41026769 0.34655294
-#>   [6] 0.05300287 0.56479901 0.85936864 0.77517391 0.64321161
-#>  [11] 0.84462914 0.54144946 0.63070476 0.20325827 0.39824435
-#>  [16] 0.67052432 0.39932663 0.44437632 0.51645941 0.96578745
-#>  [21] 0.70219557 0.69931716 0.23946786 0.55100566 0.76028958
-#>  [26] 0.38105366 0.64544126 0.15379307 0.86945196 0.09965658
-#>  [31] 0.96425489 0.54239108 0.38985789 0.59019282 0.96247907
-#>  [36] 0.54997487 0.66111391 0.30961551 0.10897334 0.55049635
-#>  [41] 0.93882405 0.14836866 0.44307287 0.61583610 0.37284284
-#>  [46] 0.38559622 0.42935767 0.26059293 0.07831619 0.93768396
-#>  [51] 0.48459268 0.73571291 0.30288560 0.68521609 0.06374636
-#>  [56] 0.11007808 0.98758443 0.17831882 0.94471538 0.19711729
-#>  [61] 0.02094185 0.12370745 0.23247837 0.93842382 0.19160550
-#>  [66] 0.49005550 0.98146240 0.09034183 0.94912080 0.55857523
-#>  [71] 0.24692070 0.63658206 0.14290966 0.10309770 0.89516449
-#>  [76] 0.25660092 0.16943034 0.41199780 0.82721280 0.74017418
-#>  [81] 0.43724631 0.55944024 0.93615100 0.68788872 0.01416627
-#>  [86] 0.60120497 0.54125910 0.91581929 0.78949327 0.57887371
-#>  [91] 0.83217542 0.90108906 0.97474727 0.99129282 0.54436155
-#>  [96] 0.74159859 0.06534957 0.10834529 0.19737786 0.93750342
-
    -
  • Visualise the extracted p-values:
  • -
-
-plot(p)
-
-
-
-hist(p)
-
-
-

Q5. The following code uses a map nested inside another map to apply a function to every element of a nested list. Why does it fail, and what do you need to do to make it work?

-
-x <- list(
-  list(1, c(3, 9)),
-  list(c(3, 6), 7, c(4, 7, 6))
-)
-
-triple <- function(x) x * 3
-map(x, map, .f = triple)
-#> Error in .f(.x[[i]], ...): unused argument (function (.x, .f, ...) 
-#> {
-#>     .f <- as_mapper(.f, ...)
-#>     .Call(map_impl, environment(), ".x", ".f", "list")
-#> })
-

A5. This function fails because this call effectively evaluates to the following:

-
-map(.x = x, .f = ~ triple(x = .x, map))
-

But triple() has only one parameter (x), and so the execution fails.

-

Here is the fixed version:

-
-x <- list(
-  list(1, c(3, 9)),
-  list(c(3, 6), 7, c(4, 7, 6))
-)
-
-triple <- function(x) x * 3
-map(x, .f = ~ map(.x, ~ triple(.x)))
-#> [[1]]
-#> [[1]][[1]]
-#> [1] 3
-#> 
-#> [[1]][[2]]
-#> [1]  9 27
-#> 
-#> 
-#> [[2]]
-#> [[2]][[1]]
-#> [1]  9 18
-#> 
-#> [[2]][[2]]
-#> [1] 21
-#> 
-#> [[2]][[3]]
-#> [1] 12 21 18
-
-

Q6. Use map() to fit linear models to the mtcars dataset using the formulas stored in this list:

-
-formulas <- list(
-  mpg ~ disp,
-  mpg ~ I(1 / disp),
-  mpg ~ disp + wt,
-  mpg ~ I(1 / disp) + wt
-)
-

A6. Fitting linear models to the mtcars dataset using the provided formulas:

-
-formulas <- list(
-  mpg ~ disp,
-  mpg ~ I(1 / disp),
-  mpg ~ disp + wt,
-  mpg ~ I(1 / disp) + wt
-)
-
-map(formulas, ~ lm(formula = ., data = mtcars))
-#> [[1]]
-#> 
-#> Call:
-#> lm(formula = ., data = mtcars)
-#> 
-#> Coefficients:
-#> (Intercept)         disp  
-#>    29.59985     -0.04122  
-#> 
-#> 
-#> [[2]]
-#> 
-#> Call:
-#> lm(formula = ., data = mtcars)
-#> 
-#> Coefficients:
-#> (Intercept)    I(1/disp)  
-#>       10.75      1557.67  
-#> 
-#> 
-#> [[3]]
-#> 
-#> Call:
-#> lm(formula = ., data = mtcars)
-#> 
-#> Coefficients:
-#> (Intercept)         disp           wt  
-#>    34.96055     -0.01772     -3.35083  
-#> 
-#> 
-#> [[4]]
-#> 
-#> Call:
-#> lm(formula = ., data = mtcars)
-#> 
-#> Coefficients:
-#> (Intercept)    I(1/disp)           wt  
-#>      19.024     1142.560       -1.798
-
-

Q7. Fit the model mpg ~ disp to each of the bootstrap replicates of mtcars in the list below, then extract the \(R^2\) of the model fit (Hint: you can compute the \(R^2\) with summary().)

-
-bootstrap <- function(df) {
-  df[sample(nrow(df), replace = TRUE), , drop = FALSE]
-}
-
-bootstraps <- map(1:10, ~ bootstrap(mtcars))
-

A7. This can be done using map_dbl():

-
-bootstrap <- function(df) {
-  df[sample(nrow(df), replace = TRUE), , drop = FALSE]
-}
-
-bootstraps <- map(1:10, ~ bootstrap(mtcars))
-
-bootstraps %>%
-  map(~ lm(mpg ~ disp, data = .x)) %>%
-  map(summary) %>%
-  map_dbl("r.squared")
-#>  [1] 0.7864562 0.8110818 0.7956331 0.7632399 0.7967824
-#>  [6] 0.7364226 0.7203027 0.6653252 0.7732780 0.6753329
-
-
-
-

-9.2 Map variants (Exercises 9.4.6) -

-
-

Q1. Explain the results of modify(mtcars, 1).

-

A1. modify() returns the object of type same as the input. Since the input here is a data frame of certain dimensions and .f = 1 translates to plucking the first element in each column, it returns a data frame with the same dimensions with the plucked element recycled across rows.

-
-head(modify(mtcars, 1))
-#>                   mpg cyl disp  hp drat   wt  qsec vs am
-#> Mazda RX4          21   6  160 110  3.9 2.62 16.46  0  1
-#> Mazda RX4 Wag      21   6  160 110  3.9 2.62 16.46  0  1
-#> Datsun 710         21   6  160 110  3.9 2.62 16.46  0  1
-#> Hornet 4 Drive     21   6  160 110  3.9 2.62 16.46  0  1
-#> Hornet Sportabout  21   6  160 110  3.9 2.62 16.46  0  1
-#> Valiant            21   6  160 110  3.9 2.62 16.46  0  1
-#>                   gear carb
-#> Mazda RX4            4    4
-#> Mazda RX4 Wag        4    4
-#> Datsun 710           4    4
-#> Hornet 4 Drive       4    4
-#> Hornet Sportabout    4    4
-#> Valiant              4    4
-
-

Q2. Rewrite the following code to use iwalk() instead of walk2(). What are the advantages and disadvantages?

-
-cyls <- split(mtcars, mtcars$cyl)
-paths <- file.path(temp, paste0("cyl-", names(cyls), ".csv"))
-walk2(cyls, paths, write.csv)
-

A2. Let’s first rewrite provided code using iwalk():

-
-cyls <- split(mtcars, mtcars$cyl)
-names(cyls) <- file.path(temp, paste0("cyl-", names(cyls), ".csv"))
-iwalk(cyls, ~ write.csv(.x, .y))
-

The advantage of using iwalk() is that we need to now deal with only a single variable (cyls) instead of two (cyls and paths).

-

The disadvantage is that the code is difficult to reason about: -In walk2(), it’s explicit what .x (= cyls) and .y (= paths) correspond to, while this is not so for iwalk() (i.e., .x = cyls and .y = names(cyls)) with the .y argument being β€œinvisible”.

-
-

Q3. Explain how the following code transforms a data frame using functions stored in a list.

-
-trans <- list(
-  disp = function(x) x * 0.0163871,
-  am = function(x) factor(x, labels = c("auto", "manual"))
-)
-
-nm <- names(trans)
-mtcars[nm] <- map2(trans, mtcars[nm], function(f, var) f(var))
-

Compare and contrast the map2() approach to this map() approach:

-
-mtcars[nm] <- map(nm, ~ trans[[.x]](mtcars[[.x]]))
-

A3. map2() supplies the functions stored in trans as anonymous functions via placeholder f, while the names of the columns specified in mtcars[nm] are supplied as var argument to the anonymous function. Note that the function is iterating over indices for vectors of transformations and column names.

-
-trans <- list(
-  disp = function(x) x * 0.0163871,
-  am = function(x) factor(x, labels = c("auto", "manual"))
-)
-
-nm <- names(trans)
-mtcars[nm] <- map2(trans, mtcars[nm], function(f, var) f(var))
-

In the map() approach, the function is iterating over indices for vectors of column names.

-
-mtcars[nm] <- map(nm, ~ trans[[.x]](mtcars[[.x]]))
-

The latter approach can’t afford passing arguments to placeholders in an anonymous function.

-
-

Q4. What does write.csv() return, i.e.Β what happens if you use it with map2() instead of walk2()?

-

A4. If we use map2(), it will work, but it will print NULLs to the console for every list element.

-
-withr::with_tempdir(
-  code = {
-    ls <- split(mtcars, mtcars$cyl)
-    nm <- names(ls)
-    map2(ls, nm, write.csv)
-  }
-)
-#> $`4`
-#> NULL
-#> 
-#> $`6`
-#> NULL
-#> 
-#> $`8`
-#> NULL
-
-
-
-

-9.3 Predicate functionals (Exercises 9.6.3) -

-
-

Q1. Why isn’t is.na() a predicate function? What base R function is closest to being a predicate version of is.na()?

-

A1. As mentioned in the docs:

-
-

A predicate is a function that returns a single TRUE or FALSE.

-
-

The is.na() function does not return a logical scalar, but instead returns a vector and thus isn’t a predicate function.

-
-# contrast the following behavior of predicate functions
-is.character(c("x", 2))
-#> [1] TRUE
-is.null(c(3, NULL))
-#> [1] FALSE
-
-# with this behavior
-is.na(c(NA, 1))
-#> [1]  TRUE FALSE
-

The closest equivalent of a predicate function in base-R is anyNA() function.

-
-anyNA(c(NA, 1))
-#> [1] TRUE
-
-

Q2. simple_reduce() has a problem when x is length 0 or length 1. Describe the source of the problem and how you might go about fixing it.

-
-simple_reduce <- function(x, f) {
-  out <- x[[1]]
-  for (i in seq(2, length(x))) {
-    out <- f(out, x[[i]])
-  }
-  out
-}
-

A2. The supplied function struggles with inputs of length 0 and 1 because function tries to subscript out-of-bound values.

-
-simple_reduce(numeric(), sum)
-#> Error in x[[1]]: subscript out of bounds
-simple_reduce(1, sum)
-#> Error in x[[i]]: subscript out of bounds
-simple_reduce(1:3, sum)
-#> [1] 6
-

This problem can be solved by adding init argument, which supplies the default or initial value:

-
-simple_reduce2 <- function(x, f, init = 0) {
-  # initializer will become the first value
-  if (length(x) == 0L) {
-    return(init)
-  }
-
-  if (length(x) == 1L) {
-    return(x[[1L]])
-  }
-
-  out <- x[[1]]
-
-  for (i in seq(2, length(x))) {
-    out <- f(out, x[[i]])
-  }
-
-  out
-}
-

Let’s try it out:

-
-simple_reduce2(numeric(), sum)
-#> [1] 0
-simple_reduce2(1, sum)
-#> [1] 1
-simple_reduce2(1:3, sum)
-#> [1] 6
-

Depending on the function, we can provide a different init argument:

-
-simple_reduce2(numeric(), `*`, init = 1)
-#> [1] 1
-simple_reduce2(1, `*`, init = 1)
-#> [1] 1
-simple_reduce2(1:3, `*`, init = 1)
-#> [1] 6
-
-

Q3. Implement the span() function from Haskell: given a list x and a predicate function f, span(x, f) returns the location of the longest sequential run of elements where the predicate is true. (Hint: you might find rle() helpful.)

-

A3. Implementation of span():

-
-span <- function(x, f) {
-  running_lengths <- purrr::map_lgl(x, ~ f(.x)) %>% rle()
-
-  df <- dplyr::tibble(
-    "lengths" = running_lengths$lengths,
-    "values" = running_lengths$values
-  ) %>%
-    dplyr::mutate(rowid = dplyr::row_number()) %>%
-    dplyr::filter(values)
-
-  # no sequence where condition is `TRUE`
-  if (nrow(df) == 0L) {
-    return(integer())
-  }
-
-  # only single sequence where condition is `TRUE`
-  if (nrow(df) == 1L) {
-    return((df$rowid):(df$lengths - 1 + df$rowid))
-  }
-
-  # multiple sequences where condition is `TRUE`; select max one
-  if (nrow(df) > 1L) {
-    df <- dplyr::filter(df, lengths == max(lengths))
-    return((df$rowid):(df$lengths - 1 + df$rowid))
-  }
-}
-

Testing it once:

-
-span(c(0, 0, 0, 0, 0), is.na)
-#> integer(0)
-span(c(NA, 0, NA, NA, NA), is.na)
-#> [1] 3 4 5
-span(c(NA, 0, 0, 0, 0), is.na)
-#> [1] 1
-span(c(NA, NA, 0, 0, 0), is.na)
-#> [1] 1 2
-

Testing it twice:

-
-span(c(3, 1, 2, 4, 5, 6), function(x) x > 3)
-#> [1] 2 3 4
-span(c(3, 1, 2, 4, 5, 6), function(x) x > 9)
-#> integer(0)
-span(c(3, 1, 2, 4, 5, 6), function(x) x == 3)
-#> [1] 1
-span(c(3, 1, 2, 4, 5, 6), function(x) x %in% c(2, 4))
-#> [1] 2 3
-
-

Q4. Implement arg_max(). It should take a function and a vector of inputs, and return the elements of the input where the function returns the highest value. For example, arg_max(-10:5, function(x) x ^ 2) should return -10. arg_max(-5:5, function(x) x ^ 2) should return c(-5, 5). Also implement the matching arg_min() function.

-

A4. Here are implementations for the specified functions:

-
    -
  • Implementing arg_max() -
  • -
-
-arg_max <- function(.x, .f) {
-  df <- dplyr::tibble(
-    original = .x,
-    transformed = purrr::map_dbl(.x, .f)
-  )
-
-  dplyr::filter(df, transformed == max(transformed))[["original"]]
-}
-
-arg_max(-10:5, function(x) x^2)
-#> [1] -10
-arg_max(-5:5, function(x) x^2)
-#> [1] -5  5
-
    -
  • Implementing arg_min() -
  • -
-
-arg_min <- function(.x, .f) {
-  df <- dplyr::tibble(
-    original = .x,
-    transformed = purrr::map_dbl(.x, .f)
-  )
-
-  dplyr::filter(df, transformed == min(transformed))[["original"]]
-}
-
-arg_min(-10:5, function(x) x^2)
-#> [1] 0
-arg_min(-5:5, function(x) x^2)
-#> [1] 0
-
-

Q5. The function below scales a vector so it falls in the range [0, 1]. How would you apply it to every column of a data frame? How would you apply it to every numeric column in a data frame?

-
-scale01 <- function(x) {
-  rng <- range(x, na.rm = TRUE)
-  (x - rng[1]) / (rng[2] - rng[1])
-}
-

A5. We will use purrr package to apply this function. Key thing to keep in mind is that a data frame is a list of atomic vectors of equal length.

-
    -
  • Applying function to every column in a data frame: We will use anscombe as example since it has all numeric columns.
  • -
-
-purrr::map_df(head(anscombe), .f = scale01)
-#> # A tibble: 6 Γ— 8
-#>      x1    x2    x3    x4    y1     y2     y3    y4
-#>   <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>  <dbl> <dbl>
-#> 1 0.333 0.333 0.333   NaN 0.362 0.897  0.116  0.266
-#> 2 0     0     0       NaN 0     0.0345 0      0    
-#> 3 0.833 0.833 0.833   NaN 0.209 0.552  1      0.633
-#> 4 0.167 0.167 0.167   NaN 0.618 0.578  0.0570 1    
-#> 5 0.5   0.5   0.5     NaN 0.458 1      0.174  0.880
-#> 6 1     1     1       NaN 1     0      0.347  0.416
-
    -
  • Applying function to every numeric column in a data frame: We will use iris as example since not all of its columns are of numeric type.
  • -
-
-purrr::modify_if(head(iris), .p = is.numeric, .f = scale01)
-#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
-#> 1        0.625   0.5555556         0.25           0  setosa
-#> 2        0.375   0.0000000         0.25           0  setosa
-#> 3        0.125   0.2222222         0.00           0  setosa
-#> 4        0.000   0.1111111         0.50           0  setosa
-#> 5        0.500   0.6666667         0.25           0  setosa
-#> 6        1.000   1.0000000         1.00           1  setosa
-
-
-
-

-9.4 Base functionals (Exercises 9.7.3) -

-
-

Q1. How does apply() arrange the output? Read the documentation and perform some experiments.

-

A1. Let’s prepare an array and apply a function over different margins:

-
-(m <- as.array(table(mtcars$cyl, mtcars$am, mtcars$vs)))
-#> , ,  = 0
-#> 
-#>    
-#>     auto manual
-#>   4    0      1
-#>   6    0      3
-#>   8   12      2
-#> 
-#> , ,  = 1
-#> 
-#>    
-#>     auto manual
-#>   4    3      7
-#>   6    4      0
-#>   8    0      0
-
-# rows
-apply(m, 1, function(x) x^2)
-#>       
-#>         4  6   8
-#>   [1,]  0  0 144
-#>   [2,]  1  9   4
-#>   [3,]  9 16   0
-#>   [4,] 49  0   0
-
-# columns
-apply(m, 2, function(x) x^2)
-#>       
-#>        auto manual
-#>   [1,]    0      1
-#>   [2,]    0      9
-#>   [3,]  144      4
-#>   [4,]    9     49
-#>   [5,]   16      0
-#>   [6,]    0      0
-
-# rows and columns
-apply(m, c(1, 2), function(x) x^2)
-#> , ,  = auto
-#> 
-#>    
-#>     4  6   8
-#>   0 0  0 144
-#>   1 9 16   0
-#> 
-#> , ,  = manual
-#> 
-#>    
-#>      4 6 8
-#>   0  1 9 4
-#>   1 49 0 0
-

As can be seen, apply() returns outputs organised first by the margins being operated over, and only then the results.

-
-

Q2. What do eapply() and rapply() do? Does purrr have equivalents?

-

A2. Let’s consider them one-by-one.

- -

As mentioned in its documentation:

-
-

eapply() applies FUN to the named values from an environment and returns the results as a list.

-
-

Here is an example:

-
-library(rlang)
-#> 
-#> Attaching package: 'rlang'
-#> The following objects are masked from 'package:purrr':
-#> 
-#>     %@%, as_function, flatten, flatten_chr,
-#>     flatten_dbl, flatten_int, flatten_lgl,
-#>     flatten_raw, invoke, splice
-#> The following object is masked from 'package:magrittr':
-#> 
-#>     set_names
-
-e <- env("x" = 1, "y" = 2)
-rlang::env_print(e)
-#> <environment: 0x1445f4720>
-#> Parent: <environment: global>
-#> Bindings:
-#> β€’ x: <dbl>
-#> β€’ y: <dbl>
-
-eapply(e, as.character)
-#> $x
-#> [1] "1"
-#> 
-#> $y
-#> [1] "2"
-

purrr doesn’t have any function to iterate over environments.

- -
-

rapply() is a recursive version of lapply with flexibility in how the result is structured (how = β€œ..”).

-
-

Here is an example:

-
-X <- list(list(a = TRUE, b = list(c = c(4L, 3.2))), d = 9.0)
-
-rapply(X, as.character, classes = "numeric", how = "replace")
-#> [[1]]
-#> [[1]]$a
-#> [1] TRUE
-#> 
-#> [[1]]$b
-#> [[1]]$b$c
-#> [1] "4"   "3.2"
-#> 
-#> 
-#> 
-#> $d
-#> [1] "9"
-

purrr has something similar in modify_depth().

-
-X <- list(list(a = TRUE, b = list(c = c(4L, 3.2))), d = 9.0)
-
-purrr::modify_depth(X, .depth = 2L, .f = length)
-#> [[1]]
-#> [[1]]$a
-#> [1] 1
-#> 
-#> [[1]]$b
-#> [1] 1
-#> 
-#> 
-#> $d
-#> [1] 1
-
-

Q3. Challenge: read about the fixed point algorithm. Complete the exercises using R.

-

A3. As mentioned in the suggested reading material:

-
-

A number \(x\) is called a fixed point of a function \(f\) if \(x\) satisfies the equation \(f(x) = x\). For some functions \(f\) we can locate a fixed point by beginning with an initial guess and applying \(f\) repeatedly, \(f(x), f(f(x)), f(f(f(x))), ...\) until the value does not change very much. Using this idea, we can devise a procedure fixed-point that takes as inputs a function and an initial guess and produces an approximation to a fixed point of the function.

-
-

Let’s first implement a fixed-point algorithm:

-
-close_enough <- function(x1, x2, tolerance = 0.001) {
-  if (abs(x1 - x2) < tolerance) {
-    return(TRUE)
-  } else {
-    return(FALSE)
-  }
-}
-
-find_fixed_point <- function(.f, .guess, tolerance = 0.001) {
-  .next <- .f(.guess)
-  is_close_enough <- close_enough(.next, .guess, tol = tolerance)
-
-  if (is_close_enough) {
-    return(.next)
-  } else {
-    find_fixed_point(.f, .next, tolerance)
-  }
-}
-

Let’s check if it works as expected:

-
-find_fixed_point(cos, 1.0)
-#> [1] 0.7387603
-
-# cos(x) = x
-cos(find_fixed_point(cos, 1.0))
-#> [1] 0.7393039
-

We will solve only one exercise from the reading material. Rest are beyond the scope of this solution manual.

-
-

Show that the golden ratio \(\phi\) is a fixed point of the transformation \(x \mapsto 1 + 1/x\), and use this fact to compute \(\phi\) by means of the fixed-point procedure.

-
-
-golden_ratio_f <- function(x) 1 + (1 / x)
-
-find_fixed_point(golden_ratio_f, 1.0)
-#> [1] 1.618182
-
-
-
-

-9.5 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    assertthat    0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    DBI           1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    dplyr         1.0.10     2022-09-01 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    highr         0.9        2021-04-16 [1] CRAN (R 4.2.0)
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    purrr       * 0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang       * 1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble        3.1.8.9002 2022-10-16 [1] local
-#>    tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/functions.html b/_book/functions.html deleted file mode 100644 index d5ed8f00..00000000 --- a/_book/functions.html +++ /dev/null @@ -1,1237 +0,0 @@ - - - - - - -Chapter 6 Functions | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-6 Functions -

-

Attaching the needed libraries:

-
-library(tidyverse, warn.conflicts = FALSE)
-
-

-6.1 Function fundamentals (Exercises 6.2.5) -

-

Q1. Given a name, like "mean", match.fun() lets you find a function. Given a function, can you find its name? Why doesn’t that make sense in R?

-

A1. Given a name, match.fun() lets you find a function.

-
-match.fun("mean")
-#> function (x, ...) 
-#> UseMethod("mean")
-#> <bytecode: 0x113d75a30>
-#> <environment: namespace:base>
-

But, given a function, it doesn’t make sense to find its name because there can be multiple names bound to the same function.

-
-f1 <- function(x) mean(x)
-f2 <- f1
-
-match.fun("f1")
-#> function(x) mean(x)
-
-match.fun("f2")
-#> function(x) mean(x)
-

Q2. It’s possible (although typically not useful) to call an anonymous function. Which of the two approaches below is correct? Why?

-
-function(x) 3()
-#> function(x) 3()
-(function(x) 3)()
-#> [1] 3
-

A2. The first expression is not correct since the function will evaluate 3(), which is syntactically not allowed since literals can’t be treated like functions.

-
-f <- (function(x) 3())
-f
-#> function(x) 3()
-f()
-#> Error in f(): attempt to apply non-function
-
-rlang::is_syntactic_literal(3)
-#> [1] TRUE
-

This is the correct way to call an anonymous function.

-
-g <- (function(x) 3)
-g
-#> function(x) 3
-g()
-#> [1] 3
-

Q3. A good rule of thumb is that an anonymous function should fit on one line and shouldn’t need to use {}. Review your code. Where could you have used an anonymous function instead of a named function? Where should you have used a named function instead of an anonymous function?

-

A3. Self activity.

-

Q4. What function allows you to tell if an object is a function? What function allows you to tell if a function is a primitive function?

-

A4. Use is.function() to check if an object is a function:

-
-# these are functions
-f <- function(x) 3
-is.function(mean)
-#> [1] TRUE
-is.function(f)
-#> [1] TRUE
-
-# these aren't
-is.function("x")
-#> [1] FALSE
-is.function(new.env())
-#> [1] FALSE
-

Use is.primitive() to check if a function is primitive:

-
-# primitive
-is.primitive(sum)
-#> [1] TRUE
-is.primitive(`+`)
-#> [1] TRUE
-
-# not primitive
-is.primitive(mean)
-#> [1] FALSE
-is.primitive(read.csv)
-#> [1] FALSE
-

Q5. This code makes a list of all functions in the base package.

-
-objs <- mget(ls("package:base", all = TRUE), inherits = TRUE)
-funs <- Filter(is.function, objs)
-

Use it to answer the following questions:

-
    -
  1. Which base function has the most arguments?

  2. -
  3. How many base functions have no arguments? What’s special about those functions?

  4. -
  5. How could you adapt the code to find all primitive functions?

  6. -
-

A5. The provided code is the following:

-
-objs <- mget(ls("package:base", all = TRUE), inherits = TRUE)
-funs <- Filter(is.function, objs)
-
    -
  1. Which base function has the most arguments?
  2. -
-

We can use formals() to extract number of arguments, but because this function returns NULL for primitive functions.

-
-formals("!")
-#> NULL
-
-length(formals("!"))
-#> [1] 0
-

Therefore, we will focus only on non-primitive functions.

-
-funs <- purrr::discard(funs, is.primitive)
-

scan() function has the most arguments.

-
-df_formals <- purrr::map_df(funs, ~ length(formals(.))) %>%
-  tidyr::pivot_longer(
-    cols = dplyr::everything(),
-    names_to = "function",
-    values_to = "argumentCount"
-  ) %>%
-  dplyr::arrange(desc(argumentCount))
-
-df_formals
-#> # A tibble: 1,125 Γ— 2
-#>    `function`       argumentCount
-#>    <chr>                    <int>
-#>  1 scan                        22
-#>  2 format.default              16
-#>  3 source                      16
-#>  4 formatC                     15
-#>  5 library                     13
-#>  6 merge.data.frame            13
-#>  7 prettyNum                   13
-#>  8 system2                     11
-#>  9 print.default               10
-#> 10 save                        10
-#> # … with 1,115 more rows
-
    -
  1. How many base functions have no arguments? What’s special about those functions?
  2. -
-

At the time of writing, 47 base (non-primitive) functions have no arguments.

-
-dplyr::filter(df_formals, argumentCount == 0)
-#> # A tibble: 47 Γ— 2
-#>    `function`               argumentCount
-#>    <chr>                            <int>
-#>  1 .First.sys                           0
-#>  2 .NotYetImplemented                   0
-#>  3 .OptRequireMethods                   0
-#>  4 .standard_regexps                    0
-#>  5 .tryResumeInterrupt                  0
-#>  6 closeAllConnections                  0
-#>  7 contributors                         0
-#>  8 Cstack_info                          0
-#>  9 date                                 0
-#> 10 default.stringsAsFactors             0
-#> # … with 37 more rows
-
    -
  1. How could you adapt the code to find all primitive functions?
  2. -
-
-objs <- mget(ls("package:base", all = TRUE), inherits = TRUE)
-funs <- Filter(is.function, objs)
-primitives <- Filter(is.primitive, funs)
-
-length(primitives)
-#> [1] 204
-
-names(primitives)
-#>   [1] "-"                    ":"                   
-#>   [3] "::"                   ":::"                 
-#>   [5] "!"                    "!="                  
-#>   [7] "...elt"               "...length"           
-#>   [9] "...names"             ".C"                  
-#>  [11] ".cache_class"         ".Call"               
-#>  [13] ".Call.graphics"       ".class2"             
-#>  [15] ".External"            ".External.graphics"  
-#>  [17] ".External2"           ".Fortran"            
-#>  [19] ".Internal"            ".isMethodsDispatchOn"
-#>  [21] ".Primitive"           ".primTrace"          
-#>  [23] ".primUntrace"         ".subset"             
-#>  [25] ".subset2"             "("                   
-#>  [27] "["                    "[["                  
-#>  [29] "[[<-"                 "[<-"                 
-#>  [31] "{"                    "@"                   
-#>  [33] "@<-"                  "*"                   
-#>  [35] "/"                    "&"                   
-#>  [37] "&&"                   "%*%"                 
-#>  [39] "%/%"                  "%%"                  
-#>  [41] "^"                    "+"                   
-#>  [43] "<"                    "<-"                  
-#>  [45] "<<-"                  "<="                  
-#>  [47] "="                    "=="                  
-#>  [49] ">"                    ">="                  
-#>  [51] "|"                    "||"                  
-#>  [53] "~"                    "$"                   
-#>  [55] "$<-"                  "abs"                 
-#>  [57] "acos"                 "acosh"               
-#>  [59] "all"                  "any"                 
-#>  [61] "anyNA"                "Arg"                 
-#>  [63] "as.call"              "as.character"        
-#>  [65] "as.complex"           "as.double"           
-#>  [67] "as.environment"       "as.integer"          
-#>  [69] "as.logical"           "as.numeric"          
-#>  [71] "as.raw"               "asin"                
-#>  [73] "asinh"                "atan"                
-#>  [75] "atanh"                "attr"                
-#>  [77] "attr<-"               "attributes"          
-#>  [79] "attributes<-"         "baseenv"             
-#>  [81] "break"                "browser"             
-#>  [83] "c"                    "call"                
-#>  [85] "ceiling"              "class"               
-#>  [87] "class<-"              "Conj"                
-#>  [89] "cos"                  "cosh"                
-#>  [91] "cospi"                "cummax"              
-#>  [93] "cummin"               "cumprod"             
-#>  [95] "cumsum"               "digamma"             
-#>  [97] "dim"                  "dim<-"               
-#>  [99] "dimnames"             "dimnames<-"          
-#> [101] "emptyenv"             "enc2native"          
-#> [103] "enc2utf8"             "environment<-"       
-#> [105] "exp"                  "expm1"               
-#> [107] "expression"           "floor"               
-#> [109] "for"                  "forceAndCall"        
-#> [111] "function"             "gamma"               
-#> [113] "gc.time"              "globalenv"           
-#> [115] "if"                   "Im"                  
-#> [117] "interactive"          "invisible"           
-#> [119] "is.array"             "is.atomic"           
-#> [121] "is.call"              "is.character"        
-#> [123] "is.complex"           "is.double"           
-#> [125] "is.environment"       "is.expression"       
-#> [127] "is.finite"            "is.function"         
-#> [129] "is.infinite"          "is.integer"          
-#> [131] "is.language"          "is.list"             
-#> [133] "is.logical"           "is.matrix"           
-#> [135] "is.na"                "is.name"             
-#> [137] "is.nan"               "is.null"             
-#> [139] "is.numeric"           "is.object"           
-#> [141] "is.pairlist"          "is.raw"              
-#> [143] "is.recursive"         "is.single"           
-#> [145] "is.symbol"            "isS4"                
-#> [147] "lazyLoadDBfetch"      "length"              
-#> [149] "length<-"             "levels<-"            
-#> [151] "lgamma"               "list"                
-#> [153] "log"                  "log10"               
-#> [155] "log1p"                "log2"                
-#> [157] "max"                  "min"                 
-#> [159] "missing"              "Mod"                 
-#> [161] "names"                "names<-"             
-#> [163] "nargs"                "next"                
-#> [165] "nzchar"               "oldClass"            
-#> [167] "oldClass<-"           "on.exit"             
-#> [169] "pos.to.env"           "proc.time"           
-#> [171] "prod"                 "quote"               
-#> [173] "range"                "Re"                  
-#> [175] "rep"                  "repeat"              
-#> [177] "retracemem"           "return"              
-#> [179] "round"                "seq_along"           
-#> [181] "seq_len"              "seq.int"             
-#> [183] "sign"                 "signif"              
-#> [185] "sin"                  "sinh"                
-#> [187] "sinpi"                "sqrt"                
-#> [189] "standardGeneric"      "storage.mode<-"      
-#> [191] "substitute"           "sum"                 
-#> [193] "switch"               "tan"                 
-#> [195] "tanh"                 "tanpi"               
-#> [197] "tracemem"             "trigamma"            
-#> [199] "trunc"                "unclass"             
-#> [201] "untracemem"           "UseMethod"           
-#> [203] "while"                "xtfrm"
-

Q6. What are the three important components of a function?

-

A6. Except for primitive functions, all functions have 3 important components:

- -

Q7. When does printing a function not show the environment it was created in?

-

A7. All package functions print their environment:

-
-# base
-mean
-#> function (x, ...) 
-#> UseMethod("mean")
-#> <bytecode: 0x113d75a30>
-#> <environment: namespace:base>
-
-# other package function
-purrr::map
-#> function (.x, .f, ...) 
-#> {
-#>     .f <- as_mapper(.f, ...)
-#>     .Call(map_impl, environment(), ".x", ".f", "list")
-#> }
-#> <bytecode: 0x127099ba0>
-#> <environment: namespace:purrr>
-

There are two exceptions where the enclosing environment won’t be printed:

-
    -
  • primitive functions
  • -
-
-sum
-#> function (..., na.rm = FALSE)  .Primitive("sum")
-
    -
  • functions created in the global environment
  • -
-
-f <- function(x) mean(x)
-f
-#> function(x) mean(x)
-
-
-

-6.2 Lexical scoping (Exercises 6.4.5) -

-

Q1. What does the following code return? Why? Describe how each of the three c’s is interpreted.

-
-c <- 10
-c(c = c)
-

A1. In c(c = c):

-
    -
  • first c is interpreted as a function call c() -
  • -
  • second c as a name for the vector element
  • -
  • third c as a variable with value 10 -
  • -
-
-c <- 10
-c(c = c)
-#>  c 
-#> 10
-

You can also see this in the lexical analysis of this expression:

-
-p_expr <- parse(text = "c(c = c)", keep.source = TRUE)
-getParseData(p_expr) %>% select(token, text)
-#>                   token text
-#> 12                 expr     
-#> 1  SYMBOL_FUNCTION_CALL    c
-#> 3                  expr     
-#> 2                   '('    (
-#> 4            SYMBOL_SUB    c
-#> 5                EQ_SUB    =
-#> 6                SYMBOL    c
-#> 8                  expr     
-#> 7                   ')'    )
-

Q2. What are the four principles that govern how R looks for values?

-

A2. Principles that govern how R looks for values:

-
    -
  1. Name masking (names defined inside a function mask names defined outside a function)

  2. -
  3. Functions vs.Β variables (the rule above also applies to function names)

  4. -
  5. A fresh start (every time a function is called, a new environment is created to host its execution)

  6. -
  7. Dynamic look-up (R looks for values when the function is run, not when the function is created)

  8. -
-

Q3. What does the following function return? Make a prediction before running the code yourself.

-
-f <- function(x) {
-  f <- function(x) {
-    f <- function() {
-      x^2
-    }
-    f() + 1
-  }
-  f(x) * 2
-}
-f(10)
-

A3. Correctly predicted πŸ˜‰

-
-f <- function(x) {
-  f <- function(x) {
-    f <- function() {
-      x^2
-    }
-    f() + 1
-  }
-  f(x) * 2
-}
-
-f(10)
-#> [1] 202
-

Although there are multiple f() functions, the order of evaluation goes from inside to outside with x^2 evaluated first and f(x) * 2 evaluated last. This results in 202 (= ((10 ^ 2) + 1) * 2).

-
-
-

-6.3 Lazy evaluation (Exercises 6.5.4) -

-

Q1. What important property of && makes x_ok() work?

-
-x_ok <- function(x) {
-  !is.null(x) && length(x) == 1 && x > 0
-}
-
-x_ok(NULL)
-x_ok(1)
-x_ok(1:3)
-

What is different with this code? Why is this behaviour undesirable here?

-
-x_ok <- function(x) {
-  !is.null(x) & length(x) == 1 & x > 0
-}
-
-x_ok(NULL)
-x_ok(1)
-x_ok(1:3)
-

A1. && evaluates left to right and has short-circuit evaluation, i.e., if the first operand is TRUE, R will short-circuit and not even look at the second operand.

-
-x_ok <- function(x) {
-  !is.null(x) && length(x) == 1 && x > 0
-}
-
-x_ok(NULL)
-#> [1] FALSE
-
-x_ok(1)
-#> [1] TRUE
-
-x_ok(1:3)
-#> [1] FALSE
-

Replacing && with & is undesirable because it performs element-wise logical comparisons and returns a vector of values that is not always useful for a decision (TRUE, FALSE, or NA).

-
-x_ok <- function(x) {
-  !is.null(x) & length(x) == 1 & x > 0
-}
-
-x_ok(NULL)
-#> logical(0)
-
-x_ok(1)
-#> [1] TRUE
-
-x_ok(1:3)
-#> [1] FALSE FALSE FALSE
-

Q2. What does this function return? Why? Which principle does it illustrate?

-
-f2 <- function(x = z) {
-  z <- 100
-  x
-}
-f2()
-

A2. The function returns 100 due to lazy evaluation.

-

When function execution environment encounters x, it evaluates argument x = z and since the name z is already bound to the value 100 in this environment, x is also bound to the same value.

-

We can check this by looking at the memory addresses:

-
-f2 <- function(x = z) {
-  z <- 100
-  print(lobstr::obj_addrs(list(x, z)))
-  x
-}
-
-f2()
-#> [1] "0x114d77808" "0x114d77808"
-#> [1] 100
-

Q3. What does this function return? Why? Which principle does it illustrate?

-
-y <- 10
-f1 <- function(x =
-                 {
-                   y <- 1
-                   2
-                 },
-               y = 0) {
-  c(x, y)
-}
-f1()
-y
-

A3. Let’s first look at what the function returns:

-
-y <- 10
-f1 <- function(x =
-                 {
-                   y <- 1
-                   2
-                 },
-               y = 0) {
-  c(x, y)
-}
-f1()
-#> [1] 2 1
-y
-#> [1] 10
-

This is because of name masking. In the function call c(x, y), when x is accessed in the function environment, the following promise is evaluated in the function environment:

-
-x <- {
-  y <- 1
-  2
-}
-

And, thus y gets assigned to 1, and x to 2, since its the last value in that scope.

-

Therefore, neither the promise y = 0 nor global assignment y <- 10 is ever consulted to find the value for y.

-

Q4. In hist(), the default value of xlim is range(breaks), the default value for breaks is "Sturges", and

-
-range("Sturges")
-#> [1] "Sturges" "Sturges"
-

Explain how hist() works to get a correct xlim value.

-

A4. The xlim defines the range of the histogram’s x-axis.

-
-hist(mtcars$wt, xlim = c(1, 6))
-
-

The default xlim = range(breaks) and breaks = "Sturges" arguments reveal that the function uses Sturges’ algorithm to compute the number of breaks.

-
-nclass.Sturges(mtcars$wt)
-#> [1] 6
-

To see the implementation, run sloop::s3_get_method("hist.default").

-

hist() ensures that the chosen algorithm returns a numeric vector containing at least two unique elements before xlim is computed.

-

Q5. Explain why this function works. Why is it confusing?

-
-show_time <- function(x = stop("Error!")) {
-  stop <- function(...) Sys.time()
-  print(x)
-}
-
-show_time()
-#> [1] "2022-11-12 11:48:38 CET"
-

A5. Let’s take this step-by-step.

-

The function argument x is missing in the function call. This means that stop("Error!") is evaluated in the function environment, and not global environment.

-

But, due to lazy evaluation, the promise stop("Error!") is evaluated only when x is accessed. This happens only when print(x) is called.

-

print(x) leads to x being evaluated, which evaluates stop in the function environment. But, in function environment, the base::stop() is masked by a locally defined stop() function, which returns Sys.time() output.

-

Q6. How many arguments are required when calling library()?

-

A6. Going solely by its signature,

-
-formals(library)
-#> $package
-#> 
-#> 
-#> $help
-#> 
-#> 
-#> $pos
-#> [1] 2
-#> 
-#> $lib.loc
-#> NULL
-#> 
-#> $character.only
-#> [1] FALSE
-#> 
-#> $logical.return
-#> [1] FALSE
-#> 
-#> $warn.conflicts
-#> 
-#> 
-#> $quietly
-#> [1] FALSE
-#> 
-#> $verbose
-#> getOption("verbose")
-#> 
-#> $mask.ok
-#> 
-#> 
-#> $exclude
-#> 
-#> 
-#> $include.only
-#> 
-#> 
-#> $attach.required
-#> missing(include.only)
-

it looks like the following arguments are required:

-
-formals(library) %>%
-  purrr::discard(is.null) %>%
-  purrr::map_lgl(~ .x == "") %>%
-  purrr::keep(~ isTRUE(.x)) %>%
-  names()
-#> [1] "package"        "help"           "warn.conflicts"
-#> [4] "mask.ok"        "exclude"        "include.only"
-

But, in reality, only one argument is required: package. The function internally checks if the other arguments are missing and adjusts accordingly.

-

It would have been better if there arguments were NULL instead of missing; that would avoid this confusion.

-
-
-

-6.4 ... (dot-dot-dot) (Exercises 6.6.1) -

-

Q1. Explain the following results:

-
-sum(1, 2, 3)
-#> [1] 6
-mean(1, 2, 3)
-#> [1] 1
-
-sum(1, 2, 3, na.omit = TRUE)
-#> [1] 7
-mean(1, 2, 3, na.omit = TRUE)
-#> [1] 1
-

A1. Let’s look at arguments for these functions:

-
-str(sum)
-#> function (..., na.rm = FALSE)
-str(mean)
-#> function (x, ...)
-

As can be seen, sum() function doesn’t have na.omit argument. So, the input na.omit = TRUE is treated as 1 (logical implicitly coerced to numeric), and thus the results. So, the expression evaluates to sum(1, 2, 3, 1).

-

For mean() function, there is only one parameter (x) and it’s matched by the first argument (1). So, the expression evaluates to mean(1).

-

Q2. Explain how to find the documentation for the named arguments in the following function call:

-
-plot(1:10, col = "red", pch = 20, xlab = "x", col.lab = "blue")
-
-

A2. Typing ?plot in the console, we see its documentation, which also shows its signature:

-
#> function (x, y, ...)
-

Since ... are passed to par(), we can look at ?par docs:

-
#> function (..., no.readonly = FALSE)
-

And so on.

-

The docs for all parameters of interest reside there.

-

Q3. Why does plot(1:10, col = "red") only colour the points, not the axes or labels? Read the source code of plot.default() to find out.

-

A3. Source code can be found here.

-

plot.default() passes ... to localTitle(), which passes it to title().

-

title() has four parts: main, sub, xlab, ylab.

-

So having a single argument col would not work as it will be ambiguous as to which element to apply this argument to.

-
-localTitle <- function(..., col, bg, pch, cex, lty, lwd) title(...)
-
-title <- function(main = NULL, sub = NULL, xlab = NULL, ylab = NULL,
-                  line = NA, outer = FALSE, ...) {
-  main <- as.graphicsAnnot(main)
-  sub <- as.graphicsAnnot(sub)
-  xlab <- as.graphicsAnnot(xlab)
-  ylab <- as.graphicsAnnot(ylab)
-  .External.graphics(C_title, main, sub, xlab, ylab, line, outer, ...)
-  invisible()
-}
-
-
-

-6.5 Exiting a function (Exercises 6.7.5) -

-

Q1. What does load() return? Why don’t you normally see these values?

-

A1. The load() function reloads datasets that were saved using the save() function:

-
-save(iris, file = "my_iris.rda")
-load("my_iris.rda")
-

We normally don’t see any value because the function loads the datasets invisibly.

-

We can change this by setting verbose = TRUE:

-
-load("my_iris.rda", verbose = TRUE)
-#> Loading objects:
-#>   iris
-
-# cleanup
-unlink("my_iris.rda")
-

Q2. What does write.table() return? What would be more useful?

-

A2. The write.table() writes a data frame to a file and returns a NULL invisibly.

-
-write.table(BOD, file = "BOD.csv")
-

It would have been more helpful if the function invisibly returned the actual object being written to the file, which could then be further used.

-
-# cleanup
-unlink("BOD.csv")
-

Q3. How does the chdir parameter of source() compare to with_dir()? Why might you prefer one to the other?

-

A3. The chdir parameter of source() is described as:

-
-

if TRUE and file is a pathname, the R working directory is temporarily changed to the directory containing file for evaluating

-
-

That is, chdir allows changing working directory temporarily but only to the directory containing file being sourced:

-

While withr::with_dir() temporarily changes the current working directory:

-
-withr::with_dir
-#> function (new, code) 
-#> {
-#>     old <- setwd(dir = new)
-#>     on.exit(setwd(old))
-#>     force(code)
-#> }
-#> <bytecode: 0x125cced30>
-#> <environment: namespace:withr>
-

More importantly, its parameters dir allows temporarily changing working directory to any directory.

-

Q4. Write a function that opens a graphics device, runs the supplied code, and closes the graphics device (always, regardless of whether or not the plotting code works).

-

A4. Here is a function that opens a graphics device, runs the supplied code, and closes the graphics device:

-
-with_png_device <- function(filename, code, ...) {
-  grDevices::png(filename = filename, ...)
-  on.exit(grDevices::dev.off(), add = TRUE)
-
-  force(code)
-}
-

Q5. We can use on.exit() to implement a simple version of capture.output().

-
-capture.output2 <- function(code) {
-  temp <- tempfile()
-  on.exit(file.remove(temp), add = TRUE, after = TRUE)
-
-  sink(temp)
-  on.exit(sink(), add = TRUE, after = TRUE)
-
-  force(code)
-  readLines(temp)
-}
-
-capture.output2(cat("a", "b", "c", sep = "\n"))
-#> [1] "a" "b" "c"
-

Compare capture.output() to capture.output2(). How do the functions differ? What features have I removed to make the key ideas easier to see? How have I rewritten the key ideas so they’re easier to understand?

-

A5. The capture.output() is significantly more complex, as can be seen by its definition:

-
-capture.output
-#> function (..., file = NULL, append = FALSE, type = c("output", 
-#>     "message"), split = FALSE) 
-#> {
-#>     type <- match.arg(type)
-#>     rval <- NULL
-#>     closeit <- TRUE
-#>     if (is.null(file)) 
-#>         file <- textConnection("rval", "w", local = TRUE)
-#>     else if (is.character(file)) 
-#>         file <- file(file, if (append) 
-#>             "a"
-#>         else "w")
-#>     else if (inherits(file, "connection")) {
-#>         if (!isOpen(file)) 
-#>             open(file, if (append) 
-#>                 "a"
-#>             else "w")
-#>         else closeit <- FALSE
-#>     }
-#>     else stop("'file' must be NULL, a character string or a connection")
-#>     sink(file, type = type, split = split)
-#>     on.exit({
-#>         sink(type = type, split = split)
-#>         if (closeit) close(file)
-#>     })
-#>     for (i in seq_len(...length())) {
-#>         out <- withVisible(...elt(i))
-#>         if (out$visible) 
-#>             print(out$value)
-#>     }
-#>     on.exit()
-#>     sink(type = type, split = split)
-#>     if (closeit) 
-#>         close(file)
-#>     if (is.null(rval)) 
-#>         invisible(NULL)
-#>     else rval
-#> }
-#> <bytecode: 0x122572870>
-#> <environment: namespace:utils>
-

Here are few key differences:

- -
-capture.output(1)
-#> [1] "[1] 1"
-
-capture.output2(1)
-#> character(0)
- -
-capture.output(message("Hi there!"), "a", type = "message")
-#> Hi there!
-#> [1] "a"
-#> character(0)
- -
-capture.output(1, invisible(2), 3)
-#> [1] "[1] 1" "[1] 3"
-
-
-

-6.6 Function forms (Exercises 6.8.6) -

-

Q1. Rewrite the following code snippets into prefix form:

-
-1 + 2 + 3
-
-1 + (2 + 3)
-
-if (length(x) <= 5) x[[5]] else x[[n]]
-

A1. Prefix forms for code snippets:

-
-# The binary `+`  operator has left to right associative property.
-`+`(`+`(1, 2), 3)
-
-`+`(1, `(`(`+`(2, 3)))
-
-`if`(cond = `<=`(length(x), 5), cons.expr = `[[`(x, 5), alt.expr = `[[`(x, n))
-

Q2. Clarify the following list of odd function calls:

-
-x <- sample(replace = TRUE, 20, x = c(1:10, NA))
-y <- runif(min = 0, max = 1, 20)
-cor(m = "k", y = y, u = "p", x = x)
-

A2. These functions don’t have dots (...) as parameters, so the argument matching takes place in the following steps:

-
    -
  • exact matching for named arguments
  • -
  • partial matching
  • -
  • position-based
  • -
-

Q3. Explain why the following code fails:

-
-modify(get("x"), 1) <- 10
-#> Error: target of assignment expands to non-language object
-

A3. As provided in the book, the replacement function is defined as:

-
-`modify<-` <- function(x, position, value) {
-  x[position] <- value
-  x
-}
-

Let’s re-write the provided code in prefix format to understand why it doesn’t work:

-
-get("x") <- `modify<-`(x = get("x"), position = 1, value = 10)
-

Although this works:

-
-x <- 5
-`modify<-`(x = get("x"), position = 1, value = 10)
-#> [1] 10
-

The following doesn’t because the code above evaluates to:

-
-`get<-`("x", 10)
-#> Error in `get<-`("x", 10): could not find function "get<-"
-

And there is no get<- function in R.

-

Q4. Create a replacement function that modifies a random location in a vector.

-

A4. A replacement function that modifies a random location in a vector:

-
-`random_modify<-` <- function(x, value) {
-  random_index <- sample(seq_along(x), size = 1)
-  x[random_index] <- value
-  return(x)
-}
-

Let’s try it out:

-
-x1 <- rep("a", 10)
-random_modify(x1) <- "X"
-x1
-#>  [1] "a" "a" "a" "a" "X" "a" "a" "a" "a" "a"
-
-x2 <- rep("a", 10)
-random_modify(x2) <- "Y"
-x2
-#>  [1] "a" "a" "a" "a" "a" "Y" "a" "a" "a" "a"
-
-x3 <- rep(0, 15)
-random_modify(x3) <- -4
-x3
-#>  [1]  0  0  0  0 -4  0  0  0  0  0  0  0  0  0  0
-
-x4 <- rep(0, 15)
-random_modify(x4) <- -1
-x4
-#>  [1]  0  0  0  0  0  0  0  0  0  0  0  0 -1  0  0
-

Q5. Write your own version of + that pastes its inputs together if they are character vectors but behaves as usual otherwise. In other words, make this code work:

-
-1 + 2
-#> [1] 3
-
-"a" + "b"
-#> [1] "ab"
-

A5. Infix operator to re-create the desired output:

-
-`+` <- function(x, y) {
-  if (is.character(x) || is.character(y)) {
-    paste0(x, y)
-  } else {
-    base::`+`(x, y)
-  }
-}
-
-1 + 2
-#> [1] 3
-
-"a" + "b"
-#> [1] "ab"
-
-rm("+", envir = .GlobalEnv)
-

Q6. Create a list of all the replacement functions found in the base package. Which ones are primitive functions? (Hint: use apropos().)

-

A6. Replacement functions always have <- at the end of their names.

-

So, using apropos(), we can find all replacement functions in search paths and the filter out the ones that don’t belong to {base} package:

-
-ls_replacement <- apropos("<-", where = TRUE, mode = "function")
-
-base_index <- which(grepl("base", searchpaths()))
-
-ls_replacement <- ls_replacement[which(names(ls_replacement) == as.character(base_index))]
-
-unname(ls_replacement)
-#>  [1] ".rowNamesDF<-"           "[[<-"                   
-#>  [3] "[[<-.data.frame"         "[[<-.factor"            
-#>  [5] "[[<-.numeric_version"    "[[<-.POSIXlt"           
-#>  [7] "[<-"                     "[<-.data.frame"         
-#>  [9] "[<-.Date"                "[<-.difftime"           
-#> [11] "[<-.factor"              "[<-.numeric_version"    
-#> [13] "[<-.POSIXct"             "[<-.POSIXlt"            
-#> [15] "@<-"                     "<-"                     
-#> [17] "<<-"                     "$<-"                    
-#> [19] "$<-.data.frame"          "attr<-"                 
-#> [21] "attributes<-"            "body<-"                 
-#> [23] "class<-"                 "colnames<-"             
-#> [25] "comment<-"               "diag<-"                 
-#> [27] "dim<-"                   "dimnames<-"             
-#> [29] "dimnames<-.data.frame"   "Encoding<-"             
-#> [31] "environment<-"           "formals<-"              
-#> [33] "is.na<-"                 "is.na<-.default"        
-#> [35] "is.na<-.factor"          "is.na<-.numeric_version"
-#> [37] "length<-"                "length<-.Date"          
-#> [39] "length<-.difftime"       "length<-.factor"        
-#> [41] "length<-.POSIXct"        "length<-.POSIXlt"       
-#> [43] "levels<-"                "levels<-.factor"        
-#> [45] "mode<-"                  "mostattributes<-"       
-#> [47] "names<-"                 "names<-.POSIXlt"        
-#> [49] "oldClass<-"              "parent.env<-"           
-#> [51] "regmatches<-"            "row.names<-"            
-#> [53] "row.names<-.data.frame"  "row.names<-.default"    
-#> [55] "rownames<-"              "split<-"                
-#> [57] "split<-.data.frame"      "split<-.default"        
-#> [59] "storage.mode<-"          "substr<-"               
-#> [61] "substring<-"             "units<-"                
-#> [63] "units<-.difftime"
-

The primitive replacement functions can be listed using is.primitive():

-
-mget(ls_replacement, envir = baseenv()) %>%
-  purrr::keep(is.primitive) %>%
-  names()
-#>  [1] "[[<-"           "[<-"            "@<-"           
-#>  [4] "<-"             "<<-"            "$<-"           
-#>  [7] "attr<-"         "attributes<-"   "class<-"       
-#> [10] "dim<-"          "dimnames<-"     "environment<-" 
-#> [13] "length<-"       "levels<-"       "names<-"       
-#> [16] "oldClass<-"     "storage.mode<-"
-

Q7. What are valid names for user-created infix functions?

-

A7. As mentioned in the respective section of the book:

-
-

The names of infix functions are more flexible than regular R functions: they can contain any sequence of characters except for %.

-
-

Q8. Create an infix xor() operator.

-

A8. Exclusive OR is a logical operation that is TRUE if and only if its arguments differ (one is TRUE, the other is FALSE).

-
-lv1 <- c(TRUE, FALSE, TRUE, FALSE)
-lv2 <- c(TRUE, TRUE, FALSE, FALSE)
-
-xor(lv1, lv2)
-#> [1] FALSE  TRUE  TRUE FALSE
-

We can create infix operator for exclusive OR like so:

-
-`%xor%` <- function(x, y) {
-  !((x & y) | !(x | y))
-}
-
-lv1 %xor% lv2
-#> [1] FALSE  TRUE  TRUE FALSE
-
-TRUE %xor% TRUE
-#> [1] FALSE
-

The function is vectorized over its inputs because the underlying logical operators themselves are vectorized.

-

Q9. Create infix versions of the set functions intersect(), union(), and setdiff(). You might call them %n%, %u%, and %/% to match conventions from mathematics.

-

A9. The required infix operators can be created as following:

-
-`%n%` <- function(x, y) {
-  intersect(x, y)
-}
-
-`%u%` <- function(x, y) {
-  union(x, y)
-}
-
-`%/%` <- function(x, y) {
-  setdiff(x, y)
-}
-

We can check that the outputs agree with the underlying functions:

-
-(x <- c(sort(sample(1:20, 9)), NA))
-#>  [1]  4  7  8  9 11 13 15 16 20 NA
-(y <- c(sort(sample(3:23, 7)), NA))
-#> [1]  9 10 13 15 17 19 20 NA
-
-identical(intersect(x, y), x %n% y)
-#> [1] TRUE
-identical(union(x, y), x %u% y)
-#> [1] TRUE
-identical(setdiff(x, y), x %/% y)
-#> [1] TRUE
-
-
-

-6.7 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package       * version    date (UTC) lib source
-#>    assertthat      0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    backports       1.4.1      2021-12-13 [1] CRAN (R 4.2.0)
-#>    base          * 4.2.2      2022-10-31 [?] local
-#>    bookdown        0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    broom           1.0.1      2022-08-29 [1] CRAN (R 4.2.0)
-#>    bslib           0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem          1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cellranger      1.1.0      2016-07-27 [1] CRAN (R 4.2.0)
-#>    cli             3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>    colorspace      2.0-3      2022-02-21 [1] CRAN (R 4.2.0)
-#>  P compiler        4.2.2      2022-10-31 [1] local
-#>    crayon          1.5.2      2022-09-29 [1] CRAN (R 4.2.1)
-#>  P datasets      * 4.2.2      2022-10-31 [1] local
-#>    DBI             1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)
-#>    dbplyr          2.2.1      2022-06-27 [1] CRAN (R 4.2.0)
-#>    digest          0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit         0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    dplyr         * 1.0.10     2022-09-01 [1] CRAN (R 4.2.1)
-#>    ellipsis        0.3.2      2021-04-29 [1] CRAN (R 4.2.0)
-#>    evaluate        0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi           1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap         1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    forcats       * 0.5.2      2022-08-19 [1] CRAN (R 4.2.1)
-#>    fs              1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    gargle          1.2.1      2022-09-08 [1] CRAN (R 4.2.1)
-#>    generics        0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    ggplot2       * 3.4.0      2022-11-04 [1] CRAN (R 4.2.2)
-#>    glue            1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>    googledrive     2.0.0      2021-07-08 [1] CRAN (R 4.2.0)
-#>    googlesheets4   1.0.1      2022-08-13 [1] CRAN (R 4.2.0)
-#>  P graphics      * 4.2.2      2022-10-31 [1] local
-#>  P grDevices     * 4.2.2      2022-10-31 [1] local
-#>  P grid            4.2.2      2022-10-31 [1] local
-#>    gtable          0.3.1      2022-09-01 [1] CRAN (R 4.2.1)
-#>    haven           2.5.1      2022-08-22 [1] CRAN (R 4.2.0)
-#>    highr           0.9        2021-04-16 [1] CRAN (R 4.2.0)
-#>    hms             1.1.2      2022-08-19 [1] CRAN (R 4.2.0)
-#>    htmltools       0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    httr            1.4.4      2022-08-17 [1] CRAN (R 4.2.0)
-#>    jquerylib       0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite        1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr           1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle       1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    lobstr          1.1.2      2022-06-22 [1] CRAN (R 4.2.0)
-#>    lubridate       1.9.0      2022-11-06 [1] CRAN (R 4.2.2)
-#>    magrittr      * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise         2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods       * 4.2.2      2022-10-31 [1] local
-#>    modelr          0.1.10     2022-11-11 [1] CRAN (R 4.2.2)
-#>    munsell         0.5.0      2018-06-12 [1] CRAN (R 4.2.0)
-#>    pillar          1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig       2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    purrr         * 0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6              2.5.1.9000 2022-10-27 [1] local
-#>    readr         * 2.1.3      2022-10-01 [1] CRAN (R 4.2.1)
-#>    readxl          1.4.1      2022-08-17 [1] CRAN (R 4.2.0)
-#>    reprex          2.0.2      2022-08-17 [1] CRAN (R 4.2.1)
-#>    rlang           1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown       2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi      0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    rvest           1.0.3      2022-08-19 [1] CRAN (R 4.2.1)
-#>    sass            0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    scales          1.2.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    sessioninfo     1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats         * 4.2.2      2022-10-31 [1] local
-#>    stringi         1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       * 1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble        * 3.1.8.9002 2022-10-16 [1] local
-#>    tidyr         * 1.2.1      2022-09-08 [1] CRAN (R 4.2.1)
-#>    tidyselect      1.2.0      2022-10-10 [1] CRAN (R 4.2.1)
-#>    tidyverse     * 1.3.2      2022-07-18 [1] CRAN (R 4.2.0)
-#>    timechange      0.1.1      2022-11-04 [1] CRAN (R 4.2.2)
-#>  P tools           4.2.2      2022-10-31 [1] local
-#>    tzdb            0.3.0      2022-03-28 [1] CRAN (R 4.2.0)
-#>    utf8            1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils         * 4.2.2      2022-10-31 [1] local
-#>    vctrs           0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr           2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun            0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2            1.3.3.9000 2022-10-10 [1] local
-#>    yaml            2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/improving-performance.html b/_book/improving-performance.html deleted file mode 100644 index 2060e64f..00000000 --- a/_book/improving-performance.html +++ /dev/null @@ -1,643 +0,0 @@ - - - - - - -Chapter 24 Improving performance | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-24 Improving performance -

-

Attaching the needed libraries:

- -
-

-24.1 Exercises 24.3.1 -

-

Q1. What are faster alternatives to lm()? Which are specifically designed to work with larger datasets?

-

A1. Faster alternatives to lm() can be found by visiting CRAN Task View: High-Performance and Parallel Computing with R page.

-

Here are some of the available options:

- -

High performances can be obtained with these packages especially if R is linked against an optimized BLAS, such as ATLAS. You can check this information using sessionInfo():

-
-sessInfo <- sessionInfo()
-sessInfo$matprod
-#> [1] "default"
-sessInfo$LAPACK
-#> [1] "/Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/lib/libRlapack.dylib"
-

Comparing performance of different alternatives:

-
-library(gapminder)
-
-# having a look at the data
-glimpse(gapminder)
-#> Rows: 1,704
-#> Columns: 6
-#> $ country   <fct> "Afghanistan", "Afghanistan", "Afghanist…
-#> $ continent <fct> Asia, Asia, Asia, Asia, Asia, Asia, Asia…
-#> $ year      <int> 1952, 1957, 1962, 1967, 1972, 1977, 1982…
-#> $ lifeExp   <dbl> 28.801, 30.332, 31.997, 34.020, 36.088, …
-#> $ pop       <int> 8425333, 9240934, 10267083, 11537966, 13…
-#> $ gdpPercap <dbl> 779.4453, 820.8530, 853.1007, 836.1971, …
-
-bench::mark(
-  "lm"       = stats::lm(lifeExp ~ continent * gdpPercap, gapminder),
-  "speedglm" = speedglm::speedlm(lifeExp ~ continent * gdpPercap, gapminder),
-  "biglm"    = biglm::biglm(lifeExp ~ continent * gdpPercap, gapminder),
-  "fastLm"   = RcppEigen::fastLm(lifeExp ~ continent * gdpPercap, gapminder),
-  check      = FALSE,
-  iterations = 1000
-)[1:5]
-#> # A tibble: 4 Γ— 5
-#>   expression      min   median `itr/sec` mem_alloc
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 lm            690Β΅s 930.09Β΅s      920.    1.25MB
-#> 2 speedglm      723Β΅s   1.06ms      834.   66.37MB
-#> 3 biglm         580Β΅s 790.93Β΅s     1120.  936.54KB
-#> 4 fastLm        665Β΅s  951.9Β΅s      937.    4.21MB
-

The results might change depending on the size of the dataset, with the performance benefits accruing bigger the dataset.

-

You will have to experiment with different algorithms and find the one that fits the needs of your dataset the best.

-

Q2. What package implements a version of match() that’s faster for repeated look ups? How much faster is it?

-

A2. The package (and the respective function) is fastmatch::fmatch()7.

-

The documentation for this function notes:

-
-

It is slightly faster than the built-in version because it uses more specialized code, but in addition it retains the hash table within the table object such that it can be re-used, dramatically reducing the look-up time especially for large table.

-
-

With a small vector, fmatch() is only slightly faster, but of the same order of magnitude.

-
-library(fastmatch, warn.conflicts = FALSE)
-
-small_vec <- c("a", "b", "x", "m", "n", "y")
-
-length(small_vec)
-#> [1] 6
-
-bench::mark(
-  "base" = match(c("x", "y"), small_vec),
-  "fastmatch" = fmatch(c("x", "y"), small_vec)
-)[1:5]
-#> # A tibble: 2 Γ— 5
-#>   expression      min   median `itr/sec` mem_alloc
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 base          656ns    779ns   509012.    2.77KB
-#> 2 fastmatch     533ns    656ns   639718.    2.66KB
-

But, with a larger vector, fmatch() is orders of magnitude faster! ⚑

-
-large_vec <- c(rep(c("a", "b"), 1e4), "x", rep(c("m", "n"), 1e6), "y")
-
-length(large_vec)
-#> [1] 2020002
-
-bench::mark(
-  "base" = match(c("x", "y"), large_vec),
-  "fastmatch" = fmatch(c("x", "y"), large_vec)
-)[1:5]
-#> # A tibble: 2 Γ— 5
-#>   expression      min   median `itr/sec` mem_alloc
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 base         25.7ms   25.8ms      38.7    31.4MB
-#> 2 fastmatch     451ns    656ns  798860.         0B
-

We can also look at the hash table:

-
-fmatch.hash(c("x", "y"), small_vec)
-#> [1] "a" "b" "x" "m" "n" "y"
-#> attr(,".match.hash")
-#> <hash table>
-

Additionally, fastmatch provides equivalent of the familiar infix operator:

-
-library(fastmatch)
-
-small_vec <- c("a", "b", "x", "m", "n", "y")
-
-c("x", "y") %in% small_vec
-#> [1] TRUE TRUE
-
-c("x", "y") %fin% small_vec
-#> [1] TRUE TRUE
-

Q3. List four functions (not just those in base R) that convert a string into a date time object. What are their strengths and weaknesses?

-

A3. Here are four functions that convert a string into a date time object:

- -
-base::as.POSIXct("2022-05-05 09:23:22")
-#> [1] "2022-05-05 09:23:22 CEST"
- -
-base::as.POSIXlt("2022-05-05 09:23:22")
-#> [1] "2022-05-05 09:23:22 CEST"
- -
-lubridate::ymd_hms("2022-05-05-09-23-22")
-#> [1] "2022-05-05 09:23:22 UTC"
- -
-fasttime::fastPOSIXct("2022-05-05 09:23:22")
-#> [1] "2022-05-05 11:23:22 CEST"
-

We can also compare their performance:

-
-bench::mark(
-  "as.POSIXct" = base::as.POSIXct("2022-05-05 09:23:22"),
-  "as.POSIXlt" = base::as.POSIXlt("2022-05-05 09:23:22"),
-  "ymd_hms" = lubridate::ymd_hms("2022-05-05-09-23-22"),
-  "fastPOSIXct" = fasttime::fastPOSIXct("2022-05-05 09:23:22"),
-  check = FALSE,
-  iterations = 1000
-)
-#> # A tibble: 4 Γ— 6
-#>   expression       min   median `itr/sec` mem_alloc `gc/sec`
-#>   <bch:expr>  <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
-#> 1 as.POSIXct   42.11Β΅s 101.76Β΅s     5378.        0B     0   
-#> 2 as.POSIXlt   31.49Β΅s  46.58Β΅s     8462.        0B     8.47
-#> 3 ymd_hms       1.48ms   2.78ms      293.    21.5KB     2.37
-#> 4 fastPOSIXct 615.02ns 738.07ns   963715.        0B     0
-

There are many more packages that implement a way to convert from string to a date time object. For more, see CRAN Task View: Time Series Analysis

-

Q4. Which packages provide the ability to compute a rolling mean?

-

A4. Here are a few packages and respective functions that provide a way to compute a rolling mean:

- -

Q5. What are the alternatives to optim()?

-

A5. The optim() function provides general-purpose optimization. As noted in its docs:

-
-

General-purpose optimization based on Nelder–Mead, quasi-Newton and conjugate-gradient algorithms. It includes an option for box-constrained optimization and simulated annealing.

-
-

There are many alternatives and the exact one you would want to choose would depend on the type of optimization you would like to do.

-

Most available options can be seen at CRAN Task View: Optimization and Mathematical Programming.

-
-
-

-24.2 Exercises 24.4.3 -

-

Q1. What’s the difference between rowSums() and .rowSums()?

-

A1. The documentation for these functions state:

-
-

The versions with an initial dot in the name (.colSums() etc) are β€˜bare-bones’ versions for use in programming: they apply only to numeric (like) matrices and do not name the result.

-
-

Looking at the source code,

-
    -
  • -rowSums() function does a number of checks to validate if the arguments are acceptable
  • -
-
-rowSums
-#> function (x, na.rm = FALSE, dims = 1L) 
-#> {
-#>     if (is.data.frame(x)) 
-#>         x <- as.matrix(x)
-#>     if (!is.array(x) || length(dn <- dim(x)) < 2L) 
-#>         stop("'x' must be an array of at least two dimensions")
-#>     if (dims < 1L || dims > length(dn) - 1L) 
-#>         stop("invalid 'dims'")
-#>     p <- prod(dn[-(id <- seq_len(dims))])
-#>     dn <- dn[id]
-#>     z <- if (is.complex(x)) 
-#>         .Internal(rowSums(Re(x), prod(dn), p, na.rm)) + (0+1i) * 
-#>             .Internal(rowSums(Im(x), prod(dn), p, na.rm))
-#>     else .Internal(rowSums(x, prod(dn), p, na.rm))
-#>     if (length(dn) > 1L) {
-#>         dim(z) <- dn
-#>         dimnames(z) <- dimnames(x)[id]
-#>     }
-#>     else names(z) <- dimnames(x)[[1L]]
-#>     z
-#> }
-#> <bytecode: 0x10c9f31e8>
-#> <environment: namespace:base>
-
    -
  • -.rowSums() directly proceeds to computation using an internal code which is built in to the R interpreter
  • -
-
-.rowSums
-#> function (x, m, n, na.rm = FALSE) 
-#> .Internal(rowSums(x, m, n, na.rm))
-#> <bytecode: 0x10c9fc340>
-#> <environment: namespace:base>
-

But they have comparable performance:

-
-x <- cbind(x1 = 3, x2 = c(4:1e4, 2:1e5))
-
-bench::mark(
-  "rowSums" = rowSums(x),
-  ".rowSums" = .rowSums(x, dim(x)[[1]], dim(x)[[2]])
-)[1:5]
-#> # A tibble: 2 Γ— 5
-#>   expression      min   median `itr/sec` mem_alloc
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 rowSums       126Β΅s    334Β΅s     1978.     859KB
-#> 2 .rowSums      124Β΅s    240Β΅s     3520.     859KB
-

Q2. Make a faster version of chisq.test() that only computes the chi-square test statistic when the input is two numeric vectors with no missing values. You can try simplifying chisq.test() or by coding from the mathematical definition.

-

A2. If the function is supposed to accept only two numeric vectors without missing values, then we can make chisq.test() do less work by removing code corresponding to the following :

-
    -
  • checks for data frame and matrix inputs
  • -
  • goodness-of-fit test
  • -
  • simulating p-values
  • -
  • checking for missing values
  • -
-

This leaves us with a much simpler, bare bones implementation:

-
-my_chisq_test <- function(x, y) {
-  x <- table(x, y)
-  n <- sum(x)
-
-  nr <- as.integer(nrow(x))
-  nc <- as.integer(ncol(x))
-
-  sr <- rowSums(x)
-  sc <- colSums(x)
-  E <- outer(sr, sc, "*") / n
-  v <- function(r, c, n) c * r * (n - r) * (n - c) / n^3
-  V <- outer(sr, sc, v, n)
-  dimnames(E) <- dimnames(x)
-
-  STATISTIC <- sum((abs(x - E))^2 / E)
-  PARAMETER <- (nr - 1L) * (nc - 1L)
-  PVAL <- pchisq(STATISTIC, PARAMETER, lower.tail = FALSE)
-
-  names(STATISTIC) <- "X-squared"
-  names(PARAMETER) <- "df"
-
-  structure(
-    list(
-      statistic = STATISTIC,
-      parameter = PARAMETER,
-      p.value = PVAL,
-      method = "Pearson's Chi-squared test",
-      observed = x,
-      expected = E,
-      residuals = (x - E) / sqrt(E),
-      stdres = (x - E) / sqrt(V)
-    ),
-    class = "htest"
-  )
-}
-

And, indeed, this custom function performs slightly better8 than its base equivalent:

-
-m <- c(rep("a", 1000), rep("b", 9000))
-n <- c(rep(c("x", "y"), 5000))
-
-bench::mark(
-  "base" = chisq.test(m, n)$statistic[[1]],
-  "custom" = my_chisq_test(m, n)$statistic[[1]]
-)[1:5]
-#> # A tibble: 2 Γ— 5
-#>   expression      min   median `itr/sec` mem_alloc
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 base          839Β΅s   1.18ms      681.    1.47MB
-#> 2 custom        624Β΅s 861.41Β΅s      999.    1.13MB
-

Q3. Can you make a faster version of table() for the case of an input of two integer vectors with no missing values? Can you use it to speed up your chi-square test?

-

A3. In order to make a leaner version of table(), we can take a similar approach and trim the unnecessary input checks in light of our new API of accepting just two vectors without missing values. We can remove the following components from the code:

-
    -
  • extracting data from objects entered in ... argument
  • -
  • dealing with missing values
  • -
  • other input validation checks
  • -
-

In addition to this removal, we can also use fastmatch::fmatch() instead of match():

-
-my_table <- function(x, y) {
-  x_sorted <- sort(unique(x))
-  y_sorted <- sort(unique(y))
-
-  x_length <- length(x_sorted)
-  y_length <- length(y_sorted)
-
-  bin <-
-    fastmatch::fmatch(x, x_sorted) +
-    x_length * fastmatch::fmatch(y, y_sorted) -
-    x_length
-
-  y <- tabulate(bin, x_length * y_length)
-
-  y <- array(
-    y,
-    dim = c(x_length, y_length),
-    dimnames = list(x = x_sorted, y = y_sorted)
-  )
-
-  class(y) <- "table"
-  y
-}
-

The custom function indeed performs slightly better:

-
-x <- c(rep("a", 1000), rep("b", 9000))
-y <- c(rep(c("x", "y"), 5000))
-
-# `check = FALSE` because the custom function has an additional attribute:
-# ".match.hash"
-bench::mark(
-  "base" = table(x, y),
-  "custom" = my_table(x, y),
-  check = FALSE
-)[1:5]
-#> # A tibble: 2 Γ— 5
-#>   expression      min   median `itr/sec` mem_alloc
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 base          590Β΅s    926Β΅s      836.     960KB
-#> 2 custom        319Β΅s    428Β΅s     2042.     488KB
-

We can also use this function in our custom chi-squared test function and see if the performance improves any further:

-
-my_chisq_test2 <- function(x, y) {
-  x <- my_table(x, y)
-  n <- sum(x)
-
-  nr <- as.integer(nrow(x))
-  nc <- as.integer(ncol(x))
-
-  sr <- rowSums(x)
-  sc <- colSums(x)
-  E <- outer(sr, sc, "*") / n
-  v <- function(r, c, n) c * r * (n - r) * (n - c) / n^3
-  V <- outer(sr, sc, v, n)
-  dimnames(E) <- dimnames(x)
-
-  STATISTIC <- sum((abs(x - E))^2 / E)
-  PARAMETER <- (nr - 1L) * (nc - 1L)
-  PVAL <- pchisq(STATISTIC, PARAMETER, lower.tail = FALSE)
-
-  names(STATISTIC) <- "X-squared"
-  names(PARAMETER) <- "df"
-
-  structure(
-    list(
-      statistic = STATISTIC,
-      parameter = PARAMETER,
-      p.value = PVAL,
-      method = "Pearson's Chi-squared test",
-      observed = x,
-      expected = E,
-      residuals = (x - E) / sqrt(E),
-      stdres = (x - E) / sqrt(V)
-    ),
-    class = "htest"
-  )
-}
-

And, indeed, this new version of the custom function performs even better than it previously did:

-
-m <- c(rep("a", 1000), rep("b", 9000))
-n <- c(rep(c("x", "y"), 5000))
-
-bench::mark(
-  "base" = chisq.test(m, n)$statistic[[1]],
-  "custom" = my_chisq_test2(m, n)$statistic[[1]]
-)[1:5]
-#> # A tibble: 2 Γ— 5
-#>   expression      min   median `itr/sec` mem_alloc
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 base          814Β΅s   1.31ms      618.    1.28MB
-#> 2 custom        349Β΅s 483.25Β΅s     1691.  594.45KB
-
-
-

-24.3 Exercises 24.5.1 -

-

Q1. The density functions, e.g., dnorm(), have a common interface. Which arguments are vectorised over? What does rnorm(10, mean = 10:1) do?

-

A1. The density function family has the following interface:

-
-dnorm(x, mean = 0, sd = 1, log = FALSE)
-pnorm(q, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)
-qnorm(p, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)
-rnorm(n, mean = 0, sd = 1)
-

Reading the documentation reveals that the following parameters are vectorized: -x, q, p, mean, sd.

-

This means that something like the following will work:

-
-rnorm(c(1, 2, 3), mean = c(0, -1, 5))
-#> [1] 1.124335 0.930398 3.844935
-

But, for functions that don’t have multiple vectorized parameters, it won’t. For example,

-
-pnorm(c(1, 2, 3), mean = c(0, -1, 5), log.p = c(FALSE, TRUE, TRUE))
-#> [1] 0.84134475 0.99865010 0.02275013
-

The following function call generates 10 random numbers (since n = 10) with 10 different distributions with means supplied by the vector 10:1.

-
-rnorm(n = 10, mean = 10:1)
-#>  [1]  8.2421770  9.3920474  7.1362118  7.5789906  5.2551688
-#>  [6]  6.0143714  4.6147891  1.1096247  2.8759129 -0.6756857
-

Q2. Compare the speed of apply(x, 1, sum) with rowSums(x) for varying sizes of x.

-

A2. We can write a custom function to vary number of rows in a matrix and extract a data frame comparing performance of these two functions.

-
-benc_perform <- function(nRow, nCol = 100) {
-  x <- matrix(data = rnorm(nRow * nCol), nrow = nRow, ncol = nCol)
-
-  bench::mark(
-    rowSums(x),
-    apply(x, 1, sum)
-  )[1:5]
-}
-
-nRowList <- list(10, 100, 500, 1000, 5000, 10000, 50000, 100000)
-
-names(nRowList) <- as.character(nRowList)
-
-benchDF <- map_dfr(
-  .x = nRowList,
-  .f = ~ benc_perform(.x),
-  .id = "nRows"
-) %>%
-  mutate(nRows = as.numeric(nRows))
-

Plotting this data reveals that rowSums(x) has O(1) behavior, while O(n) behavior.

-
-ggplot(
-  benchDF,
-  aes(
-    x = as.numeric(nRows),
-    y = median,
-    group = as.character(expression),
-    color = as.character(expression)
-  )
-) +
-  geom_point() +
-  geom_line() +
-  labs(
-    x = "Number of Rows",
-    y = "Median Execution Time",
-    colour = "Function used"
-  )
-
-

Q3. How can you use crossprod() to compute a weighted sum? How much faster is it than the naive sum(x * w)?

-

A3. Both of these functions provide a way to compute a weighted sum:

-
-x <- c(1:6, 2, 3)
-w <- rnorm(length(x))
-
-crossprod(x, w)[[1]]
-#> [1] 15.94691
-sum(x * w)[[1]]
-#> [1] 15.94691
-

But benchmarking their performance reveals that the latter is significantly faster than the former!

-
-bench::mark(
-  crossprod(x, w)[[1]],
-  sum(x * w)[[1]],
-  iterations = 1e6
-)[1:5]
-#> # A tibble: 2 Γ— 5
-#>   expression                min   median `itr/sec` mem_alloc
-#>   <bch:expr>           <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 crossprod(x, w)[[1]]    328ns    533ns   757816.        0B
-#> 2 sum(x * w)[[1]]         164ns    287ns  1621856.        0B
- -
-
- -
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/index.html b/_book/index.html deleted file mode 100644 index 2979310e..00000000 --- a/_book/index.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - -About | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

About -

-

This book provides solutions to exercises from Hadley Wickham’s Advanced R (2nd edition) book.

-

I started working on this book as part of my process to learn by solving each of the book’s exercises. While comparing solutions to the official solutions manual, I realized that some solutions took different approaches or were at least explained differently. I’m sharing these solutions in case others might find another perspective or explanation than the official solution manual helpful for building understanding.

-

Although I have tried to make sure that all solutions are correct, the blame for any inaccuracies lies solely with me. I’d very much appreciate any suggestions or corrections.

-
- -
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/index.md b/_book/index.md deleted file mode 100644 index eb474a31..00000000 --- a/_book/index.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: "Advanced R Exercises" -author: 'Indrajeet Patil' -date: "2022-11-12" -site: bookdown::bookdown_site -documentclass: book -bibliography: [book.bib, packages.bib] -url: https://bookdown.org/IndrajeetPatil/Advanced-R-exercises/ -cover-image: cover.png -description: | - Solutions to exercises in Hadley Wickham's *Advanced R* (2nd edition) book. -biblio-style: apalike -csl: chicago-fullnote-bibliography.csl ---- - -# About {-} - -This book provides solutions to exercises from Hadley Wickham's _Advanced R_ (2nd edition) [book](https://adv-r.hadley.nz/). - -I started working on this book as part of my process to learn by solving each of the book's exercises. While comparing solutions to the [official solutions manual](https://advanced-r-solutions.rbind.io/index.html), I realized that some solutions took different approaches or were at least explained differently. I'm sharing these solutions in case others might find another perspective or explanation than the official solution manual helpful for building understanding. - -Although I have tried to make sure that all solutions are correct, the blame for any inaccuracies lies solely with me. I'd very much appreciate [any suggestions or corrections](https://github.com/IndrajeetPatil/Advanced-R-exercises/issues). - - diff --git a/_book/introduction.html b/_book/introduction.html deleted file mode 100644 index dbf94685..00000000 --- a/_book/introduction.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - -Chapter 1 Introduction | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/libs/JetBrains_Mono-0.4.2/font.css b/_book/libs/JetBrains_Mono-0.4.2/font.css deleted file mode 100644 index 395dd077..00000000 --- a/_book/libs/JetBrains_Mono-0.4.2/font.css +++ /dev/null @@ -1,7 +0,0 @@ -@font-face { - font-family: 'JetBrains Mono'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(tDbY2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKxjPg.woff) format('woff'); -} diff --git a/_book/libs/JetBrains_Mono-0.4.2/tDbY2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKxjPg.woff b/_book/libs/JetBrains_Mono-0.4.2/tDbY2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKxjPg.woff deleted file mode 100644 index 3ac25e35..00000000 Binary files a/_book/libs/JetBrains_Mono-0.4.2/tDbY2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKxjPg.woff and /dev/null differ diff --git a/_book/libs/Roboto-0.4.2/KFOmCnqEu92Fr1Me5g.woff b/_book/libs/Roboto-0.4.2/KFOmCnqEu92Fr1Me5g.woff deleted file mode 100644 index 46248000..00000000 Binary files a/_book/libs/Roboto-0.4.2/KFOmCnqEu92Fr1Me5g.woff and /dev/null differ diff --git a/_book/libs/Roboto-0.4.2/font.css b/_book/libs/Roboto-0.4.2/font.css deleted file mode 100644 index cddf76e5..00000000 --- a/_book/libs/Roboto-0.4.2/font.css +++ /dev/null @@ -1,7 +0,0 @@ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(KFOmCnqEu92Fr1Me5g.woff) format('woff'); -} diff --git a/_book/libs/Roboto_Slab-0.4.2/BngbUXZYTXPIvIBgJJSb6s3BzlRRfKOFbvjojISWaw.woff b/_book/libs/Roboto_Slab-0.4.2/BngbUXZYTXPIvIBgJJSb6s3BzlRRfKOFbvjojISWaw.woff deleted file mode 100644 index 5b3f21d1..00000000 Binary files a/_book/libs/Roboto_Slab-0.4.2/BngbUXZYTXPIvIBgJJSb6s3BzlRRfKOFbvjojISWaw.woff and /dev/null differ diff --git a/_book/libs/Roboto_Slab-0.4.2/font.css b/_book/libs/Roboto_Slab-0.4.2/font.css deleted file mode 100644 index 9bbd1284..00000000 --- a/_book/libs/Roboto_Slab-0.4.2/font.css +++ /dev/null @@ -1,7 +0,0 @@ -@font-face { - font-family: 'Roboto Slab'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(BngbUXZYTXPIvIBgJJSb6s3BzlRRfKOFbvjojISWaw.woff) format('woff'); -} diff --git a/_book/libs/bootstrap-4.6.0/bootstrap.bundle.min.js b/_book/libs/bootstrap-4.6.0/bootstrap.bundle.min.js deleted file mode 100644 index a9eb3db1..00000000 --- a/_book/libs/bootstrap-4.6.0/bootstrap.bundle.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.6.0 (https://getbootstrap.com/) - * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery)}(this,(function(t,e){"use strict";function n(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var i=n(e);function o(t,e){for(var n=0;n=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};l.jQueryDetection(),i.default.fn.emulateTransitionEnd=s,i.default.event.special[l.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(i.default(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var u="alert",f=i.default.fn[u],d=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){i.default.removeData(this._element,"bs.alert"),this._element=null},e._getRootElement=function(t){var e=l.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=i.default(t).closest(".alert")[0]),n},e._triggerCloseEvent=function(t){var e=i.default.Event("close.bs.alert");return i.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(i.default(t).removeClass("show"),i.default(t).hasClass("fade")){var n=l.getTransitionDurationFromElement(t);i.default(t).one(l.TRANSITION_END,(function(n){return e._destroyElement(t,n)})).emulateTransitionEnd(n)}else this._destroyElement(t)},e._destroyElement=function(t){i.default(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.alert");o||(o=new t(this),n.data("bs.alert",o)),"close"===e&&o[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},r(t,null,[{key:"VERSION",get:function(){return"4.6.0"}}]),t}();i.default(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',d._handleDismiss(new d)),i.default.fn[u]=d._jQueryInterface,i.default.fn[u].Constructor=d,i.default.fn[u].noConflict=function(){return i.default.fn[u]=f,d._jQueryInterface};var c=i.default.fn.button,h=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=i.default(this._element).closest('[data-toggle="buttons"]')[0];if(n){var o=this._element.querySelector('input:not([type="hidden"])');if(o){if("radio"===o.type)if(o.checked&&this._element.classList.contains("active"))t=!1;else{var r=n.querySelector(".active");r&&i.default(r).removeClass("active")}t&&("checkbox"!==o.type&&"radio"!==o.type||(o.checked=!this._element.classList.contains("active")),this.shouldAvoidTriggerChange||i.default(o).trigger("change")),o.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&i.default(this._element).toggleClass("active"))},e.dispose=function(){i.default.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(e,n){return this.each((function(){var o=i.default(this),r=o.data("bs.button");r||(r=new t(this),o.data("bs.button",r)),r.shouldAvoidTriggerChange=n,"toggle"===e&&r[e]()}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.0"}}]),t}();i.default(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=t.target,n=e;if(i.default(e).hasClass("btn")||(e=i.default(e).closest(".btn")[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var o=e.querySelector('input:not([type="hidden"])');if(o&&(o.hasAttribute("disabled")||o.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==n.tagName&&"LABEL"===e.tagName||h._jQueryInterface.call(i.default(e),"toggle","INPUT"===n.tagName)}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=i.default(t.target).closest(".btn")[0];i.default(e).toggleClass("focus",/^focus(in)?$/.test(t.type))})),i.default(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide("next")},e.nextWhenVisible=function(){var t=i.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide("prev")},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(l.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(".active.carousel-item");var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)i.default(this._element).one("slid.bs.carousel",(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var o=t>n?"next":"prev";this._slide(o,this._items[t])}},e.dispose=function(){i.default(this._element).off(m),i.default.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=a({},v,t),l.typeCheckConfig(p,t,_),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&i.default(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&i.default(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&b[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},n=function(e){t._pointerEvent&&b[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};i.default(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(i.default(this._element).on("pointerdown.bs.carousel",(function(t){return e(t)})),i.default(this._element).on("pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(i.default(this._element).on("touchstart.bs.carousel",(function(t){return e(t)})),i.default(this._element).on("touchmove.bs.carousel",(function(e){return function(e){e.originalEvent.touches&&e.originalEvent.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),i.default(this._element).on("touchend.bs.carousel",(function(t){return n(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),r=this._items.length-1;if((i&&0===o||n&&o===r)&&!this._config.wrap)return e;var a=(o+("prev"===t?-1:1))%this._items.length;return-1===a?this._items[this._items.length-1]:this._items[a]},e._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),o=this._getItemIndex(this._element.querySelector(".active.carousel-item")),r=i.default.Event("slide.bs.carousel",{relatedTarget:t,direction:e,from:o,to:n});return i.default(this._element).trigger(r),r},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));i.default(e).removeClass("active");var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&i.default(n).addClass("active")}},e._updateInterval=function(){var t=this._activeElement||this._element.querySelector(".active.carousel-item");if(t){var e=parseInt(t.getAttribute("data-interval"),10);e?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=e):this._config.interval=this._config.defaultInterval||this._config.interval}},e._slide=function(t,e){var n,o,r,a=this,s=this._element.querySelector(".active.carousel-item"),u=this._getItemIndex(s),f=e||s&&this._getItemByDirection(t,s),d=this._getItemIndex(f),c=Boolean(this._interval);if("next"===t?(n="carousel-item-left",o="carousel-item-next",r="left"):(n="carousel-item-right",o="carousel-item-prev",r="right"),f&&i.default(f).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(f,r).isDefaultPrevented()&&s&&f){this._isSliding=!0,c&&this.pause(),this._setActiveIndicatorElement(f),this._activeElement=f;var h=i.default.Event("slid.bs.carousel",{relatedTarget:f,direction:r,from:u,to:d});if(i.default(this._element).hasClass("slide")){i.default(f).addClass(o),l.reflow(f),i.default(s).addClass(n),i.default(f).addClass(n);var p=l.getTransitionDurationFromElement(s);i.default(s).one(l.TRANSITION_END,(function(){i.default(f).removeClass(n+" "+o).addClass("active"),i.default(s).removeClass("active "+o+" "+n),a._isSliding=!1,setTimeout((function(){return i.default(a._element).trigger(h)}),0)})).emulateTransitionEnd(p)}else i.default(s).removeClass("active"),i.default(f).addClass("active"),this._isSliding=!1,i.default(this._element).trigger(h);c&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data("bs.carousel"),o=a({},v,i.default(this).data());"object"==typeof e&&(o=a({},o,e));var r="string"==typeof e?e:o.slide;if(n||(n=new t(this,o),i.default(this).data("bs.carousel",n)),"number"==typeof e)n.to(e);else if("string"==typeof r){if("undefined"==typeof n[r])throw new TypeError('No method named "'+r+'"');n[r]()}else o.interval&&o.ride&&(n.pause(),n.cycle())}))},t._dataApiClickHandler=function(e){var n=l.getSelectorFromElement(this);if(n){var o=i.default(n)[0];if(o&&i.default(o).hasClass("carousel")){var r=a({},i.default(o).data(),i.default(this).data()),s=this.getAttribute("data-slide-to");s&&(r.interval=!1),t._jQueryInterface.call(i.default(o),r),s&&i.default(o).data("bs.carousel").to(s),e.preventDefault()}}},r(t,null,[{key:"VERSION",get:function(){return"4.6.0"}},{key:"Default",get:function(){return v}}]),t}();i.default(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",y._dataApiClickHandler),i.default(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),e=0,n=t.length;e0&&(this._selector=a,this._triggerArray.push(r))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){i.default(this._element).hasClass("show")?this.hide():this.show()},e.show=function(){var e,n,o=this;if(!this._isTransitioning&&!i.default(this._element).hasClass("show")&&(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof o._config.parent?t.getAttribute("data-parent")===o._config.parent:t.classList.contains("collapse")}))).length&&(e=null),!(e&&(n=i.default(e).not(this._selector).data("bs.collapse"))&&n._isTransitioning))){var r=i.default.Event("show.bs.collapse");if(i.default(this._element).trigger(r),!r.isDefaultPrevented()){e&&(t._jQueryInterface.call(i.default(e).not(this._selector),"hide"),n||i.default(e).data("bs.collapse",null));var a=this._getDimension();i.default(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[a]=0,this._triggerArray.length&&i.default(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var s="scroll"+(a[0].toUpperCase()+a.slice(1)),u=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,(function(){i.default(o._element).removeClass("collapsing").addClass("collapse show"),o._element.style[a]="",o.setTransitioning(!1),i.default(o._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(u),this._element.style[a]=this._element[s]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&i.default(this._element).hasClass("show")){var e=i.default.Event("hide.bs.collapse");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",l.reflow(this._element),i.default(this._element).addClass("collapsing").removeClass("collapse show");var o=this._triggerArray.length;if(o>0)for(var r=0;r=0)return 1;return 0}();var k=N&&window.Promise?function(t){var e=!1;return function(){e||(e=!0,window.Promise.resolve().then((function(){e=!1,t()})))}}:function(t){var e=!1;return function(){e||(e=!0,setTimeout((function(){e=!1,t()}),D))}};function A(t){return t&&"[object Function]"==={}.toString.call(t)}function I(t,e){if(1!==t.nodeType)return[];var n=t.ownerDocument.defaultView.getComputedStyle(t,null);return e?n[e]:n}function O(t){return"HTML"===t.nodeName?t:t.parentNode||t.host}function x(t){if(!t)return document.body;switch(t.nodeName){case"HTML":case"BODY":return t.ownerDocument.body;case"#document":return t.body}var e=I(t),n=e.overflow,i=e.overflowX,o=e.overflowY;return/(auto|scroll|overlay)/.test(n+o+i)?t:x(O(t))}function j(t){return t&&t.referenceNode?t.referenceNode:t}var L=N&&!(!window.MSInputMethodContext||!document.documentMode),P=N&&/MSIE 10/.test(navigator.userAgent);function F(t){return 11===t?L:10===t?P:L||P}function R(t){if(!t)return document.documentElement;for(var e=F(10)?document.body:null,n=t.offsetParent||null;n===e&&t.nextElementSibling;)n=(t=t.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&"BODY"!==i&&"HTML"!==i?-1!==["TH","TD","TABLE"].indexOf(n.nodeName)&&"static"===I(n,"position")?R(n):n:t?t.ownerDocument.documentElement:document.documentElement}function H(t){return null!==t.parentNode?H(t.parentNode):t}function M(t,e){if(!(t&&t.nodeType&&e&&e.nodeType))return document.documentElement;var n=t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING,i=n?t:e,o=n?e:t,r=document.createRange();r.setStart(i,0),r.setEnd(o,0);var a,s,l=r.commonAncestorContainer;if(t!==l&&e!==l||i.contains(o))return"BODY"===(s=(a=l).nodeName)||"HTML"!==s&&R(a.firstElementChild)!==a?R(l):l;var u=H(t);return u.host?M(u.host,e):M(t,H(e).host)}function q(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"top",n="top"===e?"scrollTop":"scrollLeft",i=t.nodeName;if("BODY"===i||"HTML"===i){var o=t.ownerDocument.documentElement,r=t.ownerDocument.scrollingElement||o;return r[n]}return t[n]}function B(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=q(e,"top"),o=q(e,"left"),r=n?-1:1;return t.top+=i*r,t.bottom+=i*r,t.left+=o*r,t.right+=o*r,t}function Q(t,e){var n="x"===e?"Left":"Top",i="Left"===n?"Right":"Bottom";return parseFloat(t["border"+n+"Width"])+parseFloat(t["border"+i+"Width"])}function W(t,e,n,i){return Math.max(e["offset"+t],e["scroll"+t],n["client"+t],n["offset"+t],n["scroll"+t],F(10)?parseInt(n["offset"+t])+parseInt(i["margin"+("Height"===t?"Top":"Left")])+parseInt(i["margin"+("Height"===t?"Bottom":"Right")]):0)}function U(t){var e=t.body,n=t.documentElement,i=F(10)&&getComputedStyle(n);return{height:W("Height",e,n,i),width:W("Width",e,n,i)}}var V=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},Y=function(){function t(t,e){for(var n=0;n2&&void 0!==arguments[2]&&arguments[2],i=F(10),o="HTML"===e.nodeName,r=G(t),a=G(e),s=x(t),l=I(e),u=parseFloat(l.borderTopWidth),f=parseFloat(l.borderLeftWidth);n&&o&&(a.top=Math.max(a.top,0),a.left=Math.max(a.left,0));var d=K({top:r.top-a.top-u,left:r.left-a.left-f,width:r.width,height:r.height});if(d.marginTop=0,d.marginLeft=0,!i&&o){var c=parseFloat(l.marginTop),h=parseFloat(l.marginLeft);d.top-=u-c,d.bottom-=u-c,d.left-=f-h,d.right-=f-h,d.marginTop=c,d.marginLeft=h}return(i&&!n?e.contains(s):e===s&&"BODY"!==s.nodeName)&&(d=B(d,e)),d}function J(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=t.ownerDocument.documentElement,i=$(t,n),o=Math.max(n.clientWidth,window.innerWidth||0),r=Math.max(n.clientHeight,window.innerHeight||0),a=e?0:q(n),s=e?0:q(n,"left"),l={top:a-i.top+i.marginTop,left:s-i.left+i.marginLeft,width:o,height:r};return K(l)}function Z(t){var e=t.nodeName;if("BODY"===e||"HTML"===e)return!1;if("fixed"===I(t,"position"))return!0;var n=O(t);return!!n&&Z(n)}function tt(t){if(!t||!t.parentElement||F())return document.documentElement;for(var e=t.parentElement;e&&"none"===I(e,"transform");)e=e.parentElement;return e||document.documentElement}function et(t,e,n,i){var o=arguments.length>4&&void 0!==arguments[4]&&arguments[4],r={top:0,left:0},a=o?tt(t):M(t,j(e));if("viewport"===i)r=J(a,o);else{var s=void 0;"scrollParent"===i?"BODY"===(s=x(O(e))).nodeName&&(s=t.ownerDocument.documentElement):s="window"===i?t.ownerDocument.documentElement:i;var l=$(s,a,o);if("HTML"!==s.nodeName||Z(a))r=l;else{var u=U(t.ownerDocument),f=u.height,d=u.width;r.top+=l.top-l.marginTop,r.bottom=f+l.top,r.left+=l.left-l.marginLeft,r.right=d+l.left}}var c="number"==typeof(n=n||0);return r.left+=c?n:n.left||0,r.top+=c?n:n.top||0,r.right-=c?n:n.right||0,r.bottom-=c?n:n.bottom||0,r}function nt(t){return t.width*t.height}function it(t,e,n,i,o){var r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;if(-1===t.indexOf("auto"))return t;var a=et(n,i,r,o),s={top:{width:a.width,height:e.top-a.top},right:{width:a.right-e.right,height:a.height},bottom:{width:a.width,height:a.bottom-e.bottom},left:{width:e.left-a.left,height:a.height}},l=Object.keys(s).map((function(t){return X({key:t},s[t],{area:nt(s[t])})})).sort((function(t,e){return e.area-t.area})),u=l.filter((function(t){var e=t.width,i=t.height;return e>=n.clientWidth&&i>=n.clientHeight})),f=u.length>0?u[0].key:l[0].key,d=t.split("-")[1];return f+(d?"-"+d:"")}function ot(t,e,n){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=i?tt(e):M(e,j(n));return $(n,o,i)}function rt(t){var e=t.ownerDocument.defaultView.getComputedStyle(t),n=parseFloat(e.marginTop||0)+parseFloat(e.marginBottom||0),i=parseFloat(e.marginLeft||0)+parseFloat(e.marginRight||0);return{width:t.offsetWidth+i,height:t.offsetHeight+n}}function at(t){var e={left:"right",right:"left",bottom:"top",top:"bottom"};return t.replace(/left|right|bottom|top/g,(function(t){return e[t]}))}function st(t,e,n){n=n.split("-")[0];var i=rt(t),o={width:i.width,height:i.height},r=-1!==["right","left"].indexOf(n),a=r?"top":"left",s=r?"left":"top",l=r?"height":"width",u=r?"width":"height";return o[a]=e[a]+e[l]/2-i[l]/2,o[s]=n===s?e[s]-i[u]:e[at(s)],o}function lt(t,e){return Array.prototype.find?t.find(e):t.filter(e)[0]}function ut(t,e,n){return(void 0===n?t:t.slice(0,function(t,e,n){if(Array.prototype.findIndex)return t.findIndex((function(t){return t[e]===n}));var i=lt(t,(function(t){return t[e]===n}));return t.indexOf(i)}(t,"name",n))).forEach((function(t){t.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var n=t.function||t.fn;t.enabled&&A(n)&&(e.offsets.popper=K(e.offsets.popper),e.offsets.reference=K(e.offsets.reference),e=n(e,t))})),e}function ft(){if(!this.state.isDestroyed){var t={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};t.offsets.reference=ot(this.state,this.popper,this.reference,this.options.positionFixed),t.placement=it(this.options.placement,t.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),t.originalPlacement=t.placement,t.positionFixed=this.options.positionFixed,t.offsets.popper=st(this.popper,t.offsets.reference,t.placement),t.offsets.popper.position=this.options.positionFixed?"fixed":"absolute",t=ut(this.modifiers,t),this.state.isCreated?this.options.onUpdate(t):(this.state.isCreated=!0,this.options.onCreate(t))}}function dt(t,e){return t.some((function(t){var n=t.name;return t.enabled&&n===e}))}function ct(t){for(var e=[!1,"ms","Webkit","Moz","O"],n=t.charAt(0).toUpperCase()+t.slice(1),i=0;i1&&void 0!==arguments[1]&&arguments[1],n=Tt.indexOf(t),i=Tt.slice(n+1).concat(Tt.slice(0,n));return e?i.reverse():i}var St="flip",Nt="clockwise",Dt="counterclockwise";function kt(t,e,n,i){var o=[0,0],r=-1!==["right","left"].indexOf(i),a=t.split(/(\+|\-)/).map((function(t){return t.trim()})),s=a.indexOf(lt(a,(function(t){return-1!==t.search(/,|\s/)})));a[s]&&-1===a[s].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var l=/\s*,\s*|\s+/,u=-1!==s?[a.slice(0,s).concat([a[s].split(l)[0]]),[a[s].split(l)[1]].concat(a.slice(s+1))]:[a];return(u=u.map((function(t,i){var o=(1===i?!r:r)?"height":"width",a=!1;return t.reduce((function(t,e){return""===t[t.length-1]&&-1!==["+","-"].indexOf(e)?(t[t.length-1]=e,a=!0,t):a?(t[t.length-1]+=e,a=!1,t):t.concat(e)}),[]).map((function(t){return function(t,e,n,i){var o=t.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+o[1],a=o[2];if(!r)return t;if(0===a.indexOf("%")){var s=void 0;switch(a){case"%p":s=n;break;case"%":case"%r":default:s=i}return K(s)[e]/100*r}if("vh"===a||"vw"===a)return("vh"===a?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*r;return r}(t,o,e,n)}))}))).forEach((function(t,e){t.forEach((function(n,i){_t(n)&&(o[e]+=n*("-"===t[i-1]?-1:1))}))})),o}var At={placement:"bottom",positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(t){var e=t.placement,n=e.split("-")[0],i=e.split("-")[1];if(i){var o=t.offsets,r=o.reference,a=o.popper,s=-1!==["bottom","top"].indexOf(n),l=s?"left":"top",u=s?"width":"height",f={start:z({},l,r[l]),end:z({},l,r[l]+r[u]-a[u])};t.offsets.popper=X({},a,f[i])}return t}},offset:{order:200,enabled:!0,fn:function(t,e){var n=e.offset,i=t.placement,o=t.offsets,r=o.popper,a=o.reference,s=i.split("-")[0],l=void 0;return l=_t(+n)?[+n,0]:kt(n,r,a,s),"left"===s?(r.top+=l[0],r.left-=l[1]):"right"===s?(r.top+=l[0],r.left+=l[1]):"top"===s?(r.left+=l[0],r.top-=l[1]):"bottom"===s&&(r.left+=l[0],r.top+=l[1]),t.popper=r,t},offset:0},preventOverflow:{order:300,enabled:!0,fn:function(t,e){var n=e.boundariesElement||R(t.instance.popper);t.instance.reference===n&&(n=R(n));var i=ct("transform"),o=t.instance.popper.style,r=o.top,a=o.left,s=o[i];o.top="",o.left="",o[i]="";var l=et(t.instance.popper,t.instance.reference,e.padding,n,t.positionFixed);o.top=r,o.left=a,o[i]=s,e.boundaries=l;var u=e.priority,f=t.offsets.popper,d={primary:function(t){var n=f[t];return f[t]l[t]&&!e.escapeWithReference&&(i=Math.min(f[n],l[t]-("right"===t?f.width:f.height))),z({},n,i)}};return u.forEach((function(t){var e=-1!==["left","top"].indexOf(t)?"primary":"secondary";f=X({},f,d[e](t))})),t.offsets.popper=f,t},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(t){var e=t.offsets,n=e.popper,i=e.reference,o=t.placement.split("-")[0],r=Math.floor,a=-1!==["top","bottom"].indexOf(o),s=a?"right":"bottom",l=a?"left":"top",u=a?"width":"height";return n[s]r(i[s])&&(t.offsets.popper[l]=r(i[s])),t}},arrow:{order:500,enabled:!0,fn:function(t,e){var n;if(!wt(t.instance.modifiers,"arrow","keepTogether"))return t;var i=e.element;if("string"==typeof i){if(!(i=t.instance.popper.querySelector(i)))return t}else if(!t.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),t;var o=t.placement.split("-")[0],r=t.offsets,a=r.popper,s=r.reference,l=-1!==["left","right"].indexOf(o),u=l?"height":"width",f=l?"Top":"Left",d=f.toLowerCase(),c=l?"left":"top",h=l?"bottom":"right",p=rt(i)[u];s[h]-pa[h]&&(t.offsets.popper[d]+=s[d]+p-a[h]),t.offsets.popper=K(t.offsets.popper);var m=s[d]+s[u]/2-p/2,g=I(t.instance.popper),v=parseFloat(g["margin"+f]),_=parseFloat(g["border"+f+"Width"]),b=m-t.offsets.popper[d]-v-_;return b=Math.max(Math.min(a[u]-p,b),0),t.arrowElement=i,t.offsets.arrow=(z(n={},d,Math.round(b)),z(n,c,""),n),t},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(t,e){if(dt(t.instance.modifiers,"inner"))return t;if(t.flipped&&t.placement===t.originalPlacement)return t;var n=et(t.instance.popper,t.instance.reference,e.padding,e.boundariesElement,t.positionFixed),i=t.placement.split("-")[0],o=at(i),r=t.placement.split("-")[1]||"",a=[];switch(e.behavior){case St:a=[i,o];break;case Nt:a=Ct(i);break;case Dt:a=Ct(i,!0);break;default:a=e.behavior}return a.forEach((function(s,l){if(i!==s||a.length===l+1)return t;i=t.placement.split("-")[0],o=at(i);var u=t.offsets.popper,f=t.offsets.reference,d=Math.floor,c="left"===i&&d(u.right)>d(f.left)||"right"===i&&d(u.left)d(f.top)||"bottom"===i&&d(u.top)d(n.right),m=d(u.top)d(n.bottom),v="left"===i&&h||"right"===i&&p||"top"===i&&m||"bottom"===i&&g,_=-1!==["top","bottom"].indexOf(i),b=!!e.flipVariations&&(_&&"start"===r&&h||_&&"end"===r&&p||!_&&"start"===r&&m||!_&&"end"===r&&g),y=!!e.flipVariationsByContent&&(_&&"start"===r&&p||_&&"end"===r&&h||!_&&"start"===r&&g||!_&&"end"===r&&m),w=b||y;(c||v||w)&&(t.flipped=!0,(c||v)&&(i=a[l+1]),w&&(r=function(t){return"end"===t?"start":"start"===t?"end":t}(r)),t.placement=i+(r?"-"+r:""),t.offsets.popper=X({},t.offsets.popper,st(t.instance.popper,t.offsets.reference,t.placement)),t=ut(t.instance.modifiers,t,"flip"))})),t},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(t){var e=t.placement,n=e.split("-")[0],i=t.offsets,o=i.popper,r=i.reference,a=-1!==["left","right"].indexOf(n),s=-1===["top","left"].indexOf(n);return o[a?"left":"top"]=r[n]-(s?o[a?"width":"height"]:0),t.placement=at(e),t.offsets.popper=K(o),t}},hide:{order:800,enabled:!0,fn:function(t){if(!wt(t.instance.modifiers,"hide","preventOverflow"))return t;var e=t.offsets.reference,n=lt(t.instance.modifiers,(function(t){return"preventOverflow"===t.name})).boundaries;if(e.bottomn.right||e.top>n.bottom||e.right2&&void 0!==arguments[2]?arguments[2]:{};V(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=k(this.update.bind(this)),this.options=X({},t.Defaults,o),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=e&&e.jquery?e[0]:e,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(X({},t.Defaults.modifiers,o.modifiers)).forEach((function(e){i.options.modifiers[e]=X({},t.Defaults.modifiers[e]||{},o.modifiers?o.modifiers[e]:{})})),this.modifiers=Object.keys(this.options.modifiers).map((function(t){return X({name:t},i.options.modifiers[t])})).sort((function(t,e){return t.order-e.order})),this.modifiers.forEach((function(t){t.enabled&&A(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)})),this.update();var r=this.options.eventsEnabled;r&&this.enableEventListeners(),this.state.eventsEnabled=r}return Y(t,[{key:"update",value:function(){return ft.call(this)}},{key:"destroy",value:function(){return ht.call(this)}},{key:"enableEventListeners",value:function(){return gt.call(this)}},{key:"disableEventListeners",value:function(){return vt.call(this)}}]),t}();It.Utils=("undefined"!=typeof window?window:global).PopperUtils,It.placements=Et,It.Defaults=At;var Ot="dropdown",xt=i.default.fn[Ot],jt=new RegExp("38|40|27"),Lt={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic",popperConfig:null},Pt={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string",popperConfig:"(null|object)"},Ft=function(){function t(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var e=t.prototype;return e.toggle=function(){if(!this._element.disabled&&!i.default(this._element).hasClass("disabled")){var e=i.default(this._menu).hasClass("show");t._clearMenus(),e||this.show(!0)}},e.show=function(e){if(void 0===e&&(e=!1),!(this._element.disabled||i.default(this._element).hasClass("disabled")||i.default(this._menu).hasClass("show"))){var n={relatedTarget:this._element},o=i.default.Event("show.bs.dropdown",n),r=t._getParentFromElement(this._element);if(i.default(r).trigger(o),!o.isDefaultPrevented()){if(!this._inNavbar&&e){if("undefined"==typeof It)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");var a=this._element;"parent"===this._config.reference?a=r:l.isElement(this._config.reference)&&(a=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(a=this._config.reference[0])),"scrollParent"!==this._config.boundary&&i.default(r).addClass("position-static"),this._popper=new It(a,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===i.default(r).closest(".navbar-nav").length&&i.default(document.body).children().on("mouseover",null,i.default.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),i.default(this._menu).toggleClass("show"),i.default(r).toggleClass("show").trigger(i.default.Event("shown.bs.dropdown",n))}}},e.hide=function(){if(!this._element.disabled&&!i.default(this._element).hasClass("disabled")&&i.default(this._menu).hasClass("show")){var e={relatedTarget:this._element},n=i.default.Event("hide.bs.dropdown",e),o=t._getParentFromElement(this._element);i.default(o).trigger(n),n.isDefaultPrevented()||(this._popper&&this._popper.destroy(),i.default(this._menu).toggleClass("show"),i.default(o).toggleClass("show").trigger(i.default.Event("hidden.bs.dropdown",e)))}},e.dispose=function(){i.default.removeData(this._element,"bs.dropdown"),i.default(this._element).off(".bs.dropdown"),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},e.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},e._addEventListeners=function(){var t=this;i.default(this._element).on("click.bs.dropdown",(function(e){e.preventDefault(),e.stopPropagation(),t.toggle()}))},e._getConfig=function(t){return t=a({},this.constructor.Default,i.default(this._element).data(),t),l.typeCheckConfig(Ot,t,this.constructor.DefaultType),t},e._getMenuElement=function(){if(!this._menu){var e=t._getParentFromElement(this._element);e&&(this._menu=e.querySelector(".dropdown-menu"))}return this._menu},e._getPlacement=function(){var t=i.default(this._element.parentNode),e="bottom-start";return t.hasClass("dropup")?e=i.default(this._menu).hasClass("dropdown-menu-right")?"top-end":"top-start":t.hasClass("dropright")?e="right-start":t.hasClass("dropleft")?e="left-start":i.default(this._menu).hasClass("dropdown-menu-right")&&(e="bottom-end"),e},e._detectNavbar=function(){return i.default(this._element).closest(".navbar").length>0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=a({},e.offsets,t._config.offset(e.offsets,t._element)||{}),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),a({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data("bs.dropdown");if(n||(n=new t(this,"object"==typeof e?e:null),i.default(this).data("bs.dropdown",n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},t._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),o=0,r=n.length;o0&&a--,40===e.which&&adocument.documentElement.clientHeight;n||(this._element.style.overflowY="hidden"),this._element.classList.add("modal-static");var o=l.getTransitionDurationFromElement(this._dialog);i.default(this._element).off(l.TRANSITION_END),i.default(this._element).one(l.TRANSITION_END,(function(){t._element.classList.remove("modal-static"),n||i.default(t._element).one(l.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,o)})).emulateTransitionEnd(o),this._element.focus()}},e._showElement=function(t){var e=this,n=i.default(this._element).hasClass("fade"),o=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),i.default(this._dialog).hasClass("modal-dialog-scrollable")&&o?o.scrollTop=0:this._element.scrollTop=0,n&&l.reflow(this._element),i.default(this._element).addClass("show"),this._config.focus&&this._enforceFocus();var r=i.default.Event("shown.bs.modal",{relatedTarget:t}),a=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,i.default(e._element).trigger(r)};if(n){var s=l.getTransitionDurationFromElement(this._dialog);i.default(this._dialog).one(l.TRANSITION_END,a).emulateTransitionEnd(s)}else a()},e._enforceFocus=function(){var t=this;i.default(document).off("focusin.bs.modal").on("focusin.bs.modal",(function(e){document!==e.target&&t._element!==e.target&&0===i.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?i.default(this._element).on("keydown.dismiss.bs.modal",(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||i.default(this._element).off("keydown.dismiss.bs.modal")},e._setResizeEvent=function(){var t=this;this._isShown?i.default(window).on("resize.bs.modal",(function(e){return t.handleUpdate(e)})):i.default(window).off("resize.bs.modal")},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){i.default(document.body).removeClass("modal-open"),t._resetAdjustments(),t._resetScrollbar(),i.default(t._element).trigger("hidden.bs.modal")}))},e._removeBackdrop=function(){this._backdrop&&(i.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,n=i.default(this._element).hasClass("fade")?"fade":"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",n&&this._backdrop.classList.add(n),i.default(this._backdrop).appendTo(document.body),i.default(this._element).on("click.dismiss.bs.modal",(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&("static"===e._config.backdrop?e._triggerBackdropTransition():e.hide())})),n&&l.reflow(this._backdrop),i.default(this._backdrop).addClass("show"),!t)return;if(!n)return void t();var o=l.getTransitionDurationFromElement(this._backdrop);i.default(this._backdrop).one(l.TRANSITION_END,t).emulateTransitionEnd(o)}else if(!this._isShown&&this._backdrop){i.default(this._backdrop).removeClass("show");var r=function(){e._removeBackdrop(),t&&t()};if(i.default(this._element).hasClass("fade")){var a=l.getTransitionDurationFromElement(this._backdrop);i.default(this._backdrop).one(l.TRANSITION_END,r).emulateTransitionEnd(a)}else r()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",customClass:"",sanitize:!0,sanitizeFn:null,whiteList:Qt,popperConfig:null},Zt={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},te=function(){function t(t,e){if("undefined"==typeof It)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=i.default(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(i.default(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),i.default.removeData(this.element,this.constructor.DATA_KEY),i.default(this.element).off(this.constructor.EVENT_KEY),i.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&i.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===i.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=i.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){i.default(this.element).trigger(e);var n=l.findShadowRoot(this.element),o=i.default.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!o)return;var r=this.getTipElement(),a=l.getUID(this.constructor.NAME);r.setAttribute("id",a),this.element.setAttribute("aria-describedby",a),this.setContent(),this.config.animation&&i.default(r).addClass("fade");var s="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,u=this._getAttachment(s);this.addAttachmentClass(u);var f=this._getContainer();i.default(r).data(this.constructor.DATA_KEY,this),i.default.contains(this.element.ownerDocument.documentElement,this.tip)||i.default(r).appendTo(f),i.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new It(this.element,r,this._getPopperConfig(u)),i.default(r).addClass("show"),i.default(r).addClass(this.config.customClass),"ontouchstart"in document.documentElement&&i.default(document.body).children().on("mouseover",null,i.default.noop);var d=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,i.default(t.element).trigger(t.constructor.Event.SHOWN),"out"===e&&t._leave(null,t)};if(i.default(this.tip).hasClass("fade")){var c=l.getTransitionDurationFromElement(this.tip);i.default(this.tip).one(l.TRANSITION_END,d).emulateTransitionEnd(c)}else d()}},e.hide=function(t){var e=this,n=this.getTipElement(),o=i.default.Event(this.constructor.Event.HIDE),r=function(){"show"!==e._hoverState&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),i.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(i.default(this.element).trigger(o),!o.isDefaultPrevented()){if(i.default(n).removeClass("show"),"ontouchstart"in document.documentElement&&i.default(document.body).children().off("mouseover",null,i.default.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,i.default(this.tip).hasClass("fade")){var a=l.getTransitionDurationFromElement(n);i.default(n).one(l.TRANSITION_END,r).emulateTransitionEnd(a)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){i.default(this.getTipElement()).addClass("bs-tooltip-"+t)},e.getTipElement=function(){return this.tip=this.tip||i.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(i.default(t.querySelectorAll(".tooltip-inner")),this.getTitle()),i.default(t).removeClass("fade show")},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=Vt(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?i.default(e).parent().is(t)||t.empty().append(e):t.text(i.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return a({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=a({},e.offsets,t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:l.isElement(this.config.container)?i.default(this.config.container):i.default(document).find(this.config.container)},e._getAttachment=function(t){return $t[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)i.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n="hover"===e?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,o="hover"===e?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;i.default(t.element).on(n,t.config.selector,(function(e){return t._enter(e)})).on(o,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},i.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=a({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||i.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),i.default(e.getTipElement()).hasClass("show")||"show"===e._hoverState?e._hoverState="show":(clearTimeout(e._timeout),e._hoverState="show",e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){"show"===e._hoverState&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||i.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState="out",e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){"out"===e._hoverState&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=i.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==Kt.indexOf(t)&&delete e[t]})),"number"==typeof(t=a({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),l.typeCheckConfig(Yt,t,this.constructor.DefaultType),t.sanitize&&(t.template=Vt(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=i.default(this.getTipElement()),e=t.attr("class").match(Xt);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(i.default(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.tooltip"),r="object"==typeof e&&e;if((o||!/dispose|hide/.test(e))&&(o||(o=new t(this,r),n.data("bs.tooltip",o)),"string"==typeof e)){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.0"}},{key:"Default",get:function(){return Jt}},{key:"NAME",get:function(){return Yt}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return Zt}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return Gt}}]),t}();i.default.fn[Yt]=te._jQueryInterface,i.default.fn[Yt].Constructor=te,i.default.fn[Yt].noConflict=function(){return i.default.fn[Yt]=zt,te._jQueryInterface};var ee="popover",ne=i.default.fn[ee],ie=new RegExp("(^|\\s)bs-popover\\S+","g"),oe=a({},te.Default,{placement:"right",trigger:"click",content:"",template:''}),re=a({},te.DefaultType,{content:"(string|element|function)"}),ae={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},se=function(t){var e,n;function o(){return t.apply(this,arguments)||this}n=t,(e=o).prototype=Object.create(n.prototype),e.prototype.constructor=e,e.__proto__=n;var a=o.prototype;return a.isWithContent=function(){return this.getTitle()||this._getContent()},a.addAttachmentClass=function(t){i.default(this.getTipElement()).addClass("bs-popover-"+t)},a.getTipElement=function(){return this.tip=this.tip||i.default(this.config.template)[0],this.tip},a.setContent=function(){var t=i.default(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(".popover-body"),e),t.removeClass("fade show")},a._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},a._cleanTipClass=function(){var t=i.default(this.getTipElement()),e=t.attr("class").match(ie);null!==e&&e.length>0&&t.removeClass(e.join(""))},o._jQueryInterface=function(t){return this.each((function(){var e=i.default(this).data("bs.popover"),n="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new o(this,n),i.default(this).data("bs.popover",e)),"string"==typeof t)){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},r(o,null,[{key:"VERSION",get:function(){return"4.6.0"}},{key:"Default",get:function(){return oe}},{key:"NAME",get:function(){return ee}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return ae}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return re}}]),o}(te);i.default.fn[ee]=se._jQueryInterface,i.default.fn[ee].Constructor=se,i.default.fn[ee].noConflict=function(){return i.default.fn[ee]=ne,se._jQueryInterface};var le="scrollspy",ue=i.default.fn[le],fe={offset:10,method:"auto",target:""},de={offset:"number",method:"string",target:"(string|element)"},ce=function(){function t(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,i.default(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return n._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":"position",n="auto"===this._config.method?e:this._config.method,o="position"===n?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,r=l.getSelectorFromElement(t);if(r&&(e=document.querySelector(r)),e){var a=e.getBoundingClientRect();if(a.width||a.height)return[i.default(e)[n]().top+o,r]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){i.default.removeData(this._element,"bs.scrollspy"),i.default(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=a({},fe,"object"==typeof t&&t?t:{})).target&&l.isElement(t.target)){var e=i.default(t.target).attr("id");e||(e=l.getUID(le),i.default(t.target).attr("id",e)),t.target="#"+e}return l.typeCheckConfig(le,t,de),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t li > .active":".active";n=(n=i.default.makeArray(i.default(o).find(a)))[n.length-1]}var s=i.default.Event("hide.bs.tab",{relatedTarget:this._element}),u=i.default.Event("show.bs.tab",{relatedTarget:n});if(n&&i.default(n).trigger(s),i.default(this._element).trigger(u),!u.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(e=document.querySelector(r)),this._activate(this._element,o);var f=function(){var e=i.default.Event("hidden.bs.tab",{relatedTarget:t._element}),o=i.default.Event("shown.bs.tab",{relatedTarget:n});i.default(n).trigger(e),i.default(t._element).trigger(o)};e?this._activate(e,e.parentNode,f):f()}}},e.dispose=function(){i.default.removeData(this._element,"bs.tab"),this._element=null},e._activate=function(t,e,n){var o=this,r=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?i.default(e).children(".active"):i.default(e).find("> li > .active"))[0],a=n&&r&&i.default(r).hasClass("fade"),s=function(){return o._transitionComplete(t,r,n)};if(r&&a){var u=l.getTransitionDurationFromElement(r);i.default(r).removeClass("show").one(l.TRANSITION_END,s).emulateTransitionEnd(u)}else s()},e._transitionComplete=function(t,e,n){if(e){i.default(e).removeClass("active");var o=i.default(e.parentNode).find("> .dropdown-menu .active")[0];o&&i.default(o).removeClass("active"),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}i.default(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),l.reflow(t),t.classList.contains("fade")&&t.classList.add("show");var r=t.parentNode;if(r&&"LI"===r.nodeName&&(r=r.parentNode),r&&i.default(r).hasClass("dropdown-menu")){var a=i.default(t).closest(".dropdown")[0];if(a){var s=[].slice.call(a.querySelectorAll(".dropdown-toggle"));i.default(s).addClass("active")}t.setAttribute("aria-expanded",!0)}n&&n()},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.tab");if(o||(o=new t(this),n.data("bs.tab",o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.0"}}]),t}();i.default(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),pe._jQueryInterface.call(i.default(this),"show")})),i.default.fn.tab=pe._jQueryInterface,i.default.fn.tab.Constructor=pe,i.default.fn.tab.noConflict=function(){return i.default.fn.tab=he,pe._jQueryInterface};var me=i.default.fn.toast,ge={animation:"boolean",autohide:"boolean",delay:"number"},ve={animation:!0,autohide:!0,delay:500},_e=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var e=t.prototype;return e.show=function(){var t=this,e=i.default.Event("show.bs.toast");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var n=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),i.default(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),l.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var o=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,n).emulateTransitionEnd(o)}else n()}},e.hide=function(){if(this._element.classList.contains("show")){var t=i.default.Event("hide.bs.toast");i.default(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},e.dispose=function(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),i.default(this._element).off("click.dismiss.bs.toast"),i.default.removeData(this._element,"bs.toast"),this._element=null,this._config=null},e._getConfig=function(t){return t=a({},ve,i.default(this._element).data(),"object"==typeof t&&t?t:{}),l.typeCheckConfig("toast",t,this.constructor.DefaultType),t},e._setListeners=function(){var t=this;i.default(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},e._close=function(){var t=this,e=function(){t._element.classList.add("hide"),i.default(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var n=l.getTransitionDurationFromElement(this._element);i.default(this._element).one(l.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},e._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data("bs.toast");if(o||(o=new t(this,"object"==typeof e&&e),n.data("bs.toast",o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e](this)}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.0"}},{key:"DefaultType",get:function(){return ge}},{key:"Default",get:function(){return ve}}]),t}();i.default.fn.toast=_e._jQueryInterface,i.default.fn.toast.Constructor=_e,i.default.fn.toast.noConflict=function(){return i.default.fn.toast=me,_e._jQueryInterface},t.Alert=d,t.Button=h,t.Carousel=y,t.Collapse=S,t.Dropdown=Ft,t.Modal=qt,t.Popover=se,t.Scrollspy=ce,t.Tab=pe,t.Toast=_e,t.Tooltip=te,t.Util=l,Object.defineProperty(t,"__esModule",{value:!0})})); -//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/_book/libs/bootstrap-4.6.0/bootstrap.min.css b/_book/libs/bootstrap-4.6.0/bootstrap.min.css deleted file mode 100644 index 380916e2..00000000 --- a/_book/libs/bootstrap-4.6.0/bootstrap.min.css +++ /dev/null @@ -1 +0,0 @@ -:root{--blue: #007bff;--indigo: #6610f2;--purple: #6f42c1;--pink: #e83e8c;--red: #dc3545;--orange: #fd7e14;--yellow: #ffc107;--green: #28a745;--teal: #20c997;--cyan: #17a2b8;--white: #fff;--gray: #6c757d;--gray-dark: #343a40;--default: #dee2e6;--primary: #096B72;--secondary: #6c757d;--success: #28a745;--info: #17a2b8;--warning: #ffc107;--danger: #dc3545;--light: #f8f9fa;--dark: #343a40;--breakpoint-xs: 0;--breakpoint-sm: 576px;--breakpoint-md: 768px;--breakpoint-lg: 992px;--breakpoint-xl: 1200px;--font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-family-monospace: "JetBrains Mono"}*,*::before,*::after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:Roboto;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0 !important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-original-title]{text-decoration:underline;text-decoration:underline dotted;-webkit-text-decoration:underline dotted;-moz-text-decoration:underline dotted;-ms-text-decoration:underline dotted;-o-text-decoration:underline dotted;cursor:help;border-bottom:0;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem;padding:.625rem 1.25rem;border-left:.25rem solid #e9ecef}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#096B72;text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none;background-color:transparent}a:hover{color:#03282b;text-decoration:underline;-webkit-text-decoration:underline;-moz-text-decoration:underline;-ms-text-decoration:underline;-o-text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:"JetBrains Mono";font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role="button"]{cursor:pointer}select{word-wrap:normal}button,[type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button:not(:disabled),[type="button"]:not(:disabled),[type="reset"]:not(:disabled),[type="submit"]:not(:disabled){cursor:pointer}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{padding:0;border-style:none}input[type="radio"],input[type="checkbox"]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{outline-offset:-2px;-webkit-appearance:none}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none !important}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{margin-bottom:.5rem;font-family:"Roboto Slab";font-weight:500;line-height:1.2}h1,.h1{font-size:2.5rem}h2,.h2{font-size:2rem}h3,.h3{font-size:1.75rem}h4,.h4{font-size:1.5rem}h5,.h5{font-size:1.25rem}h6,.h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,0.1)}small,.small{font-size:80%;font-weight:400}mark,.mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#000;background-color:#f6f6f6;border-radius:.25rem;padding:.125rem .25rem;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#000;background-color:#f6f6f6;padding:.5rem;border:1px solid #dee2e6;border-radius:.25rem}pre code{background-color:transparent;font-size:inherit;color:inherit;word-break:normal;padding:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-xl,.container-lg,.container-md,.container-sm{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width: 576px){.container-sm,.container{max-width:540px}}@media (min-width: 768px){.container-md,.container-sm,.container{max-width:720px}}@media (min-width: 992px){.container-lg,.container-md,.container-sm,.container{max-width:960px}}@media (min-width: 1200px){.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1140px}}.row{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*="col-"]{padding-right:0;padding-left:0}.col-xl,.col-xl-auto,.col-xl-12,.col-xl-11,.col-xl-10,.col-xl-9,.col-xl-8,.col-xl-7,.col-xl-6,.col-xl-5,.col-xl-4,.col-xl-3,.col-xl-2,.col-xl-1,.col-lg,.col-lg-auto,.col-lg-12,.col-lg-11,.col-lg-10,.col-lg-9,.col-lg-8,.col-lg-7,.col-lg-6,.col-lg-5,.col-lg-4,.col-lg-3,.col-lg-2,.col-lg-1,.col-md,.col-md-auto,.col-md-12,.col-md-11,.col-md-10,.col-md-9,.col-md-8,.col-md-7,.col-md-6,.col-md-5,.col-md-4,.col-md-3,.col-md-2,.col-md-1,.col-sm,.col-sm-auto,.col-sm-12,.col-sm-11,.col-sm-10,.col-sm-9,.col-sm-8,.col-sm-7,.col-sm-6,.col-sm-5,.col-sm-4,.col-sm-3,.col-sm-2,.col-sm-1,.col,.col-auto,.col-12,.col-11,.col-10,.col-9,.col-8,.col-7,.col-6,.col-5,.col-4,.col-3,.col-2,.col-1{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{flex-basis:0;-webkit-flex-basis:0;flex-grow:1;-webkit-flex-grow:1;max-width:100%}.row-cols-1>*{flex:0 0 100%;-webkit-flex:0 0 100%;max-width:100%}.row-cols-2>*{flex:0 0 50%;-webkit-flex:0 0 50%;max-width:50%}.row-cols-3>*{flex:0 0 33.33333%;-webkit-flex:0 0 33.33333%;max-width:33.33333%}.row-cols-4>*{flex:0 0 25%;-webkit-flex:0 0 25%;max-width:25%}.row-cols-5>*{flex:0 0 20%;-webkit-flex:0 0 20%;max-width:20%}.row-cols-6>*{flex:0 0 16.66667%;-webkit-flex:0 0 16.66667%;max-width:16.66667%}.col-auto{flex:0 0 auto;-webkit-flex:0 0 auto;width:auto;max-width:100%}.col-1{flex:0 0 8.33333%;-webkit-flex:0 0 8.33333%;max-width:8.33333%}.col-2{flex:0 0 16.66667%;-webkit-flex:0 0 16.66667%;max-width:16.66667%}.col-3{flex:0 0 25%;-webkit-flex:0 0 25%;max-width:25%}.col-4{flex:0 0 33.33333%;-webkit-flex:0 0 33.33333%;max-width:33.33333%}.col-5{flex:0 0 41.66667%;-webkit-flex:0 0 41.66667%;max-width:41.66667%}.col-6{flex:0 0 50%;-webkit-flex:0 0 50%;max-width:50%}.col-7{flex:0 0 58.33333%;-webkit-flex:0 0 58.33333%;max-width:58.33333%}.col-8{flex:0 0 66.66667%;-webkit-flex:0 0 66.66667%;max-width:66.66667%}.col-9{flex:0 0 75%;-webkit-flex:0 0 75%;max-width:75%}.col-10{flex:0 0 83.33333%;-webkit-flex:0 0 83.33333%;max-width:83.33333%}.col-11{flex:0 0 91.66667%;-webkit-flex:0 0 91.66667%;max-width:91.66667%}.col-12{flex:0 0 100%;-webkit-flex:0 0 100%;max-width:100%}.order-first{order:-1}.order-last{order:13}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-9{order:9}.order-10{order:10}.order-11{order:11}.order-12{order:12}.offset-1{margin-left:8.33333%}.offset-2{margin-left:16.66667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333%}.offset-5{margin-left:41.66667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333%}.offset-8{margin-left:66.66667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333%}.offset-11{margin-left:91.66667%}@media (min-width: 576px){.col-sm{flex-basis:0;-webkit-flex-basis:0;flex-grow:1;-webkit-flex-grow:1;max-width:100%}.row-cols-sm-1>*{flex:0 0 100%;-webkit-flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{flex:0 0 50%;-webkit-flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{flex:0 0 33.33333%;-webkit-flex:0 0 33.33333%;max-width:33.33333%}.row-cols-sm-4>*{flex:0 0 25%;-webkit-flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{flex:0 0 20%;-webkit-flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{flex:0 0 16.66667%;-webkit-flex:0 0 16.66667%;max-width:16.66667%}.col-sm-auto{flex:0 0 auto;-webkit-flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{flex:0 0 8.33333%;-webkit-flex:0 0 8.33333%;max-width:8.33333%}.col-sm-2{flex:0 0 16.66667%;-webkit-flex:0 0 16.66667%;max-width:16.66667%}.col-sm-3{flex:0 0 25%;-webkit-flex:0 0 25%;max-width:25%}.col-sm-4{flex:0 0 33.33333%;-webkit-flex:0 0 33.33333%;max-width:33.33333%}.col-sm-5{flex:0 0 41.66667%;-webkit-flex:0 0 41.66667%;max-width:41.66667%}.col-sm-6{flex:0 0 50%;-webkit-flex:0 0 50%;max-width:50%}.col-sm-7{flex:0 0 58.33333%;-webkit-flex:0 0 58.33333%;max-width:58.33333%}.col-sm-8{flex:0 0 66.66667%;-webkit-flex:0 0 66.66667%;max-width:66.66667%}.col-sm-9{flex:0 0 75%;-webkit-flex:0 0 75%;max-width:75%}.col-sm-10{flex:0 0 83.33333%;-webkit-flex:0 0 83.33333%;max-width:83.33333%}.col-sm-11{flex:0 0 91.66667%;-webkit-flex:0 0 91.66667%;max-width:91.66667%}.col-sm-12{flex:0 0 100%;-webkit-flex:0 0 100%;max-width:100%}.order-sm-first{order:-1}.order-sm-last{order:13}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333%}.offset-sm-2{margin-left:16.66667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333%}.offset-sm-5{margin-left:41.66667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333%}.offset-sm-8{margin-left:66.66667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333%}.offset-sm-11{margin-left:91.66667%}}@media (min-width: 768px){.col-md{flex-basis:0;-webkit-flex-basis:0;flex-grow:1;-webkit-flex-grow:1;max-width:100%}.row-cols-md-1>*{flex:0 0 100%;-webkit-flex:0 0 100%;max-width:100%}.row-cols-md-2>*{flex:0 0 50%;-webkit-flex:0 0 50%;max-width:50%}.row-cols-md-3>*{flex:0 0 33.33333%;-webkit-flex:0 0 33.33333%;max-width:33.33333%}.row-cols-md-4>*{flex:0 0 25%;-webkit-flex:0 0 25%;max-width:25%}.row-cols-md-5>*{flex:0 0 20%;-webkit-flex:0 0 20%;max-width:20%}.row-cols-md-6>*{flex:0 0 16.66667%;-webkit-flex:0 0 16.66667%;max-width:16.66667%}.col-md-auto{flex:0 0 auto;-webkit-flex:0 0 auto;width:auto;max-width:100%}.col-md-1{flex:0 0 8.33333%;-webkit-flex:0 0 8.33333%;max-width:8.33333%}.col-md-2{flex:0 0 16.66667%;-webkit-flex:0 0 16.66667%;max-width:16.66667%}.col-md-3{flex:0 0 25%;-webkit-flex:0 0 25%;max-width:25%}.col-md-4{flex:0 0 33.33333%;-webkit-flex:0 0 33.33333%;max-width:33.33333%}.col-md-5{flex:0 0 41.66667%;-webkit-flex:0 0 41.66667%;max-width:41.66667%}.col-md-6{flex:0 0 50%;-webkit-flex:0 0 50%;max-width:50%}.col-md-7{flex:0 0 58.33333%;-webkit-flex:0 0 58.33333%;max-width:58.33333%}.col-md-8{flex:0 0 66.66667%;-webkit-flex:0 0 66.66667%;max-width:66.66667%}.col-md-9{flex:0 0 75%;-webkit-flex:0 0 75%;max-width:75%}.col-md-10{flex:0 0 83.33333%;-webkit-flex:0 0 83.33333%;max-width:83.33333%}.col-md-11{flex:0 0 91.66667%;-webkit-flex:0 0 91.66667%;max-width:91.66667%}.col-md-12{flex:0 0 100%;-webkit-flex:0 0 100%;max-width:100%}.order-md-first{order:-1}.order-md-last{order:13}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333%}.offset-md-2{margin-left:16.66667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333%}.offset-md-5{margin-left:41.66667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333%}.offset-md-8{margin-left:66.66667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333%}.offset-md-11{margin-left:91.66667%}}@media (min-width: 992px){.col-lg{flex-basis:0;-webkit-flex-basis:0;flex-grow:1;-webkit-flex-grow:1;max-width:100%}.row-cols-lg-1>*{flex:0 0 100%;-webkit-flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{flex:0 0 50%;-webkit-flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{flex:0 0 33.33333%;-webkit-flex:0 0 33.33333%;max-width:33.33333%}.row-cols-lg-4>*{flex:0 0 25%;-webkit-flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{flex:0 0 20%;-webkit-flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{flex:0 0 16.66667%;-webkit-flex:0 0 16.66667%;max-width:16.66667%}.col-lg-auto{flex:0 0 auto;-webkit-flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{flex:0 0 8.33333%;-webkit-flex:0 0 8.33333%;max-width:8.33333%}.col-lg-2{flex:0 0 16.66667%;-webkit-flex:0 0 16.66667%;max-width:16.66667%}.col-lg-3{flex:0 0 25%;-webkit-flex:0 0 25%;max-width:25%}.col-lg-4{flex:0 0 33.33333%;-webkit-flex:0 0 33.33333%;max-width:33.33333%}.col-lg-5{flex:0 0 41.66667%;-webkit-flex:0 0 41.66667%;max-width:41.66667%}.col-lg-6{flex:0 0 50%;-webkit-flex:0 0 50%;max-width:50%}.col-lg-7{flex:0 0 58.33333%;-webkit-flex:0 0 58.33333%;max-width:58.33333%}.col-lg-8{flex:0 0 66.66667%;-webkit-flex:0 0 66.66667%;max-width:66.66667%}.col-lg-9{flex:0 0 75%;-webkit-flex:0 0 75%;max-width:75%}.col-lg-10{flex:0 0 83.33333%;-webkit-flex:0 0 83.33333%;max-width:83.33333%}.col-lg-11{flex:0 0 91.66667%;-webkit-flex:0 0 91.66667%;max-width:91.66667%}.col-lg-12{flex:0 0 100%;-webkit-flex:0 0 100%;max-width:100%}.order-lg-first{order:-1}.order-lg-last{order:13}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333%}.offset-lg-2{margin-left:16.66667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333%}.offset-lg-5{margin-left:41.66667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333%}.offset-lg-8{margin-left:66.66667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333%}.offset-lg-11{margin-left:91.66667%}}@media (min-width: 1200px){.col-xl{flex-basis:0;-webkit-flex-basis:0;flex-grow:1;-webkit-flex-grow:1;max-width:100%}.row-cols-xl-1>*{flex:0 0 100%;-webkit-flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{flex:0 0 50%;-webkit-flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{flex:0 0 33.33333%;-webkit-flex:0 0 33.33333%;max-width:33.33333%}.row-cols-xl-4>*{flex:0 0 25%;-webkit-flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{flex:0 0 20%;-webkit-flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{flex:0 0 16.66667%;-webkit-flex:0 0 16.66667%;max-width:16.66667%}.col-xl-auto{flex:0 0 auto;-webkit-flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{flex:0 0 8.33333%;-webkit-flex:0 0 8.33333%;max-width:8.33333%}.col-xl-2{flex:0 0 16.66667%;-webkit-flex:0 0 16.66667%;max-width:16.66667%}.col-xl-3{flex:0 0 25%;-webkit-flex:0 0 25%;max-width:25%}.col-xl-4{flex:0 0 33.33333%;-webkit-flex:0 0 33.33333%;max-width:33.33333%}.col-xl-5{flex:0 0 41.66667%;-webkit-flex:0 0 41.66667%;max-width:41.66667%}.col-xl-6{flex:0 0 50%;-webkit-flex:0 0 50%;max-width:50%}.col-xl-7{flex:0 0 58.33333%;-webkit-flex:0 0 58.33333%;max-width:58.33333%}.col-xl-8{flex:0 0 66.66667%;-webkit-flex:0 0 66.66667%;max-width:66.66667%}.col-xl-9{flex:0 0 75%;-webkit-flex:0 0 75%;max-width:75%}.col-xl-10{flex:0 0 83.33333%;-webkit-flex:0 0 83.33333%;max-width:83.33333%}.col-xl-11{flex:0 0 91.66667%;-webkit-flex:0 0 91.66667%;max-width:91.66667%}.col-xl-12{flex:0 0 100%;-webkit-flex:0 0 100%;max-width:100%}.order-xl-first{order:-1}.order-xl-last{order:13}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333%}.offset-xl-2{margin-left:16.66667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333%}.offset-xl-5{margin-left:41.66667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333%}.offset-xl-8{margin-left:66.66667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333%}.offset-xl-11{margin-left:91.66667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table th,.table td{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm th,.table-sm td{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered th,.table-bordered td{border:1px solid #dee2e6}.table-bordered thead th,.table-bordered thead td{border-bottom-width:2px}.table-borderless th,.table-borderless td,.table-borderless thead th,.table-borderless tbody+tbody{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,0.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,0.075)}.table-default,.table-default>th,.table-default>td{background-color:#f6f7f8}.table-default th,.table-default td,.table-default thead th,.table-default tbody+tbody{border-color:#eef0f2}.table-hover .table-default:hover{background-color:#e8eaed}.table-hover .table-default:hover>td,.table-hover .table-default:hover>th{background-color:#e8eaed}.table-primary,.table-primary>th,.table-primary>td{background-color:#bad6d8}.table-primary th,.table-primary td,.table-primary thead th,.table-primary tbody+tbody{border-color:#7fb2b6}.table-hover .table-primary:hover{background-color:#aacccf}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#aacccf}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#d6d8db}.table-secondary th,.table-secondary td,.table-secondary thead th,.table-secondary tbody+tbody{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>th,.table-success>td{background-color:#c3e6cb}.table-success th,.table-success td,.table-success thead th,.table-success tbody+tbody{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>th,.table-info>td{background-color:#bee5eb}.table-info th,.table-info td,.table-info thead th,.table-info tbody+tbody{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>th,.table-warning>td{background-color:#ffeeba}.table-warning th,.table-warning td,.table-warning thead th,.table-warning tbody+tbody{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>th,.table-danger>td{background-color:#f5c6cb}.table-danger th,.table-danger td,.table-danger thead th,.table-danger tbody+tbody{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>th,.table-light>td{background-color:#fdfdfe}.table-light th,.table-light td,.table-light thead th,.table-light tbody+tbody{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>th,.table-dark>td{background-color:#c6c8ca}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>th,.table-active>td{background-color:rgba(0,0,0,0.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,0.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,0.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark th,.table-dark td,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,0.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,0.075)}@media (max-width: 575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width: 767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width: 991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width: 1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#12dae8;outline:0;box-shadow:0 0 0 .2rem rgba(9,107,114,0.25)}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input.form-control[type="date"],input.form-control[type="time"],input.form-control[type="datetime-local"],input.form-control[type="month"]{appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[size],select.form-control[multiple]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text,.help-text,.help-block{display:block;margin-top:.25rem}.form-row{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*="col-"]{padding-right:5px;padding-left:5px}.form-check,.shiny-input-checkboxgroup .checkbox,.shiny-input-checkboxgroup .radio,.shiny-input-radiogroup .checkbox,.shiny-input-radiogroup .radio{position:relative;display:block;padding-left:1.25rem}.form-check-input,.shiny-input-checkboxgroup .checkbox label>input,.shiny-input-checkboxgroup .radio label>input,.shiny-input-radiogroup .checkbox label>input,.shiny-input-radiogroup .radio label>input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input[disabled]~.form-check-label,.shiny-input-checkboxgroup .checkbox label>input[disabled]~.form-check-label,.shiny-input-checkboxgroup .radio label>input[disabled]~.form-check-label,.shiny-input-radiogroup .checkbox label>input[disabled]~.form-check-label,.shiny-input-radiogroup .radio label>input[disabled]~.form-check-label,.shiny-input-checkboxgroup .checkbox .form-check-input[disabled]~label,.shiny-input-checkboxgroup .checkbox label>input[disabled]~label,.shiny-input-checkboxgroup .radio .form-check-input[disabled]~label,.shiny-input-checkboxgroup .radio label>input[disabled]~label,.shiny-input-radiogroup .checkbox .form-check-input[disabled]~label,.shiny-input-radiogroup .checkbox label>input[disabled]~label,.shiny-input-radiogroup .radio .form-check-input[disabled]~label,.shiny-input-radiogroup .radio label>input[disabled]~label,.form-check-input:disabled~.form-check-label,.shiny-input-checkboxgroup .checkbox label>input:disabled~.form-check-label,.shiny-input-checkboxgroup .radio label>input:disabled~.form-check-label,.shiny-input-radiogroup .checkbox label>input:disabled~.form-check-label,.shiny-input-radiogroup .radio label>input:disabled~.form-check-label,.shiny-input-checkboxgroup .checkbox .form-check-input:disabled~label,.shiny-input-checkboxgroup .checkbox label>input:disabled~label,.shiny-input-checkboxgroup .radio .form-check-input:disabled~label,.shiny-input-checkboxgroup .radio label>input:disabled~label,.shiny-input-radiogroup .checkbox .form-check-input:disabled~label,.shiny-input-radiogroup .checkbox label>input:disabled~label,.shiny-input-radiogroup .radio .form-check-input:disabled~label,.shiny-input-radiogroup .radio label>input:disabled~label{color:#6c757d}.form-check-label,.shiny-input-checkboxgroup .checkbox label,.shiny-input-checkboxgroup .radio label,.shiny-input-radiogroup .checkbox label,.shiny-input-radiogroup .radio label{margin-bottom:0}.form-check-inline{display:inline-flex;align-items:center;-webkit-align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input,.form-check-inline .shiny-input-checkboxgroup .checkbox label>input,.shiny-input-checkboxgroup .checkbox .form-check-inline label>input,.form-check-inline .shiny-input-checkboxgroup .radio label>input,.shiny-input-checkboxgroup .radio .form-check-inline label>input,.form-check-inline .shiny-input-radiogroup .checkbox label>input,.shiny-input-radiogroup .checkbox .form-check-inline label>input,.form-check-inline .shiny-input-radiogroup .radio label>input,.shiny-input-radiogroup .radio .form-check-inline label>input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,0.9);border-radius:.25rem}.form-row>.col>.valid-tooltip,.form-row>[class*="col-"]>.valid-tooltip{left:5px}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,0.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.was-validated .custom-select:valid,.custom-select.is-valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.was-validated .custom-select:valid:focus,.custom-select.is-valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,0.25)}.was-validated .form-check-input:valid~.form-check-label,.was-validated .shiny-input-checkboxgroup .checkbox label>input:valid~.form-check-label,.shiny-input-checkboxgroup .checkbox .was-validated label>input:valid~.form-check-label,.was-validated .shiny-input-checkboxgroup .radio label>input:valid~.form-check-label,.shiny-input-checkboxgroup .radio .was-validated label>input:valid~.form-check-label,.was-validated .shiny-input-radiogroup .checkbox label>input:valid~.form-check-label,.shiny-input-radiogroup .checkbox .was-validated label>input:valid~.form-check-label,.was-validated .shiny-input-radiogroup .radio label>input:valid~.form-check-label,.shiny-input-radiogroup .radio .was-validated label>input:valid~.form-check-label,.was-validated .shiny-input-checkboxgroup .checkbox .form-check-input:valid~label,.was-validated .shiny-input-checkboxgroup .checkbox label>input:valid~label,.was-validated .shiny-input-checkboxgroup .checkbox .radio label>input:valid~label,.was-validated .shiny-input-checkboxgroup .radio .checkbox label>input:valid~label,.was-validated .shiny-input-checkboxgroup .shiny-input-radiogroup .checkbox label>input:valid~label,.shiny-input-radiogroup .was-validated .shiny-input-checkboxgroup .checkbox label>input:valid~label,.was-validated .shiny-input-checkboxgroup .checkbox .shiny-input-radiogroup .radio label>input:valid~label,.shiny-input-radiogroup .radio .was-validated .shiny-input-checkboxgroup .checkbox label>input:valid~label,.shiny-input-checkboxgroup .checkbox .was-validated .form-check-input:valid~label,.shiny-input-checkboxgroup .checkbox .was-validated label>input:valid~label,.shiny-input-checkboxgroup .checkbox .was-validated .radio label>input:valid~label,.shiny-input-checkboxgroup .radio .checkbox .was-validated label>input:valid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .checkbox .was-validated label>input:valid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .checkbox .was-validated label>input:valid~label,.shiny-input-checkboxgroup .checkbox .was-validated .shiny-input-radiogroup .radio label>input:valid~label,.shiny-input-radiogroup .radio .shiny-input-checkboxgroup .checkbox .was-validated label>input:valid~label,.was-validated .shiny-input-checkboxgroup .radio .form-check-input:valid~label,.was-validated .shiny-input-checkboxgroup .radio .checkbox label>input:valid~label,.was-validated .shiny-input-checkboxgroup .checkbox .radio label>input:valid~label,.was-validated .shiny-input-checkboxgroup .radio label>input:valid~label,.was-validated .shiny-input-checkboxgroup .radio .shiny-input-radiogroup .checkbox label>input:valid~label,.shiny-input-radiogroup .checkbox .was-validated .shiny-input-checkboxgroup .radio label>input:valid~label,.was-validated .shiny-input-checkboxgroup .shiny-input-radiogroup .radio label>input:valid~label,.shiny-input-radiogroup .was-validated .shiny-input-checkboxgroup .radio label>input:valid~label,.shiny-input-checkboxgroup .radio .was-validated .form-check-input:valid~label,.shiny-input-checkboxgroup .radio .was-validated .checkbox label>input:valid~label,.shiny-input-checkboxgroup .checkbox .radio .was-validated label>input:valid~label,.shiny-input-checkboxgroup .radio .was-validated label>input:valid~label,.shiny-input-checkboxgroup .radio .was-validated .shiny-input-radiogroup .checkbox label>input:valid~label,.shiny-input-radiogroup .checkbox .shiny-input-checkboxgroup .radio .was-validated label>input:valid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .radio .was-validated label>input:valid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .radio .was-validated label>input:valid~label,.was-validated .shiny-input-radiogroup .checkbox .form-check-input:valid~label,.was-validated .shiny-input-radiogroup .shiny-input-checkboxgroup .checkbox label>input:valid~label,.shiny-input-checkboxgroup .was-validated .shiny-input-radiogroup .checkbox label>input:valid~label,.was-validated .shiny-input-radiogroup .checkbox .shiny-input-checkboxgroup .radio label>input:valid~label,.shiny-input-checkboxgroup .radio .was-validated .shiny-input-radiogroup .checkbox label>input:valid~label,.was-validated .shiny-input-radiogroup .checkbox label>input:valid~label,.was-validated .shiny-input-radiogroup .checkbox .radio label>input:valid~label,.was-validated .shiny-input-radiogroup .radio .checkbox label>input:valid~label,.shiny-input-radiogroup .checkbox .was-validated .form-check-input:valid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .checkbox .was-validated label>input:valid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .checkbox .was-validated label>input:valid~label,.shiny-input-radiogroup .checkbox .was-validated .shiny-input-checkboxgroup .radio label>input:valid~label,.shiny-input-checkboxgroup .radio .shiny-input-radiogroup .checkbox .was-validated label>input:valid~label,.shiny-input-radiogroup .checkbox .was-validated label>input:valid~label,.shiny-input-radiogroup .checkbox .was-validated .radio label>input:valid~label,.shiny-input-radiogroup .radio .checkbox .was-validated label>input:valid~label,.was-validated .shiny-input-radiogroup .radio .form-check-input:valid~label,.was-validated .shiny-input-radiogroup .radio .shiny-input-checkboxgroup .checkbox label>input:valid~label,.shiny-input-checkboxgroup .checkbox .was-validated .shiny-input-radiogroup .radio label>input:valid~label,.was-validated .shiny-input-radiogroup .shiny-input-checkboxgroup .radio label>input:valid~label,.shiny-input-checkboxgroup .was-validated .shiny-input-radiogroup .radio label>input:valid~label,.was-validated .shiny-input-radiogroup .radio .checkbox label>input:valid~label,.was-validated .shiny-input-radiogroup .checkbox .radio label>input:valid~label,.was-validated .shiny-input-radiogroup .radio label>input:valid~label,.shiny-input-radiogroup .radio .was-validated .form-check-input:valid~label,.shiny-input-radiogroup .radio .was-validated .shiny-input-checkboxgroup .checkbox label>input:valid~label,.shiny-input-checkboxgroup .checkbox .shiny-input-radiogroup .radio .was-validated label>input:valid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .radio .was-validated label>input:valid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .radio .was-validated label>input:valid~label,.shiny-input-radiogroup .radio .was-validated .checkbox label>input:valid~label,.shiny-input-radiogroup .checkbox .radio .was-validated label>input:valid~label,.shiny-input-radiogroup .radio .was-validated label>input:valid~label,.form-check-input.is-valid~.form-check-label,.shiny-input-checkboxgroup .checkbox label>input.is-valid~.form-check-label,.shiny-input-checkboxgroup .radio label>input.is-valid~.form-check-label,.shiny-input-radiogroup .checkbox label>input.is-valid~.form-check-label,.shiny-input-radiogroup .radio label>input.is-valid~.form-check-label,.shiny-input-checkboxgroup .checkbox .form-check-input.is-valid~label,.shiny-input-checkboxgroup .checkbox label>input.is-valid~label,.shiny-input-checkboxgroup .checkbox .radio label>input.is-valid~label,.shiny-input-checkboxgroup .radio .checkbox label>input.is-valid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .checkbox label>input.is-valid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .checkbox label>input.is-valid~label,.shiny-input-checkboxgroup .checkbox .shiny-input-radiogroup .radio label>input.is-valid~label,.shiny-input-radiogroup .radio .shiny-input-checkboxgroup .checkbox label>input.is-valid~label,.shiny-input-checkboxgroup .radio .form-check-input.is-valid~label,.shiny-input-checkboxgroup .radio .checkbox label>input.is-valid~label,.shiny-input-checkboxgroup .checkbox .radio label>input.is-valid~label,.shiny-input-checkboxgroup .radio label>input.is-valid~label,.shiny-input-checkboxgroup .radio .shiny-input-radiogroup .checkbox label>input.is-valid~label,.shiny-input-radiogroup .checkbox .shiny-input-checkboxgroup .radio label>input.is-valid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .radio label>input.is-valid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .radio label>input.is-valid~label,.shiny-input-radiogroup .checkbox .form-check-input.is-valid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .checkbox label>input.is-valid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .checkbox label>input.is-valid~label,.shiny-input-radiogroup .checkbox .shiny-input-checkboxgroup .radio label>input.is-valid~label,.shiny-input-checkboxgroup .radio .shiny-input-radiogroup .checkbox label>input.is-valid~label,.shiny-input-radiogroup .checkbox label>input.is-valid~label,.shiny-input-radiogroup .checkbox .radio label>input.is-valid~label,.shiny-input-radiogroup .radio .checkbox label>input.is-valid~label,.shiny-input-radiogroup .radio .form-check-input.is-valid~label,.shiny-input-radiogroup .radio .shiny-input-checkboxgroup .checkbox label>input.is-valid~label,.shiny-input-checkboxgroup .checkbox .shiny-input-radiogroup .radio label>input.is-valid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .radio label>input.is-valid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .radio label>input.is-valid~label,.shiny-input-radiogroup .radio .checkbox label>input.is-valid~label,.shiny-input-radiogroup .checkbox .radio label>input.is-valid~label,.shiny-input-radiogroup .radio label>input.is-valid~label{color:#28a745}.was-validated .form-check-input:valid~.valid-feedback,.was-validated .shiny-input-checkboxgroup .checkbox label>input:valid~.valid-feedback,.shiny-input-checkboxgroup .checkbox .was-validated label>input:valid~.valid-feedback,.was-validated .shiny-input-checkboxgroup .radio label>input:valid~.valid-feedback,.shiny-input-checkboxgroup .radio .was-validated label>input:valid~.valid-feedback,.was-validated .shiny-input-radiogroup .checkbox label>input:valid~.valid-feedback,.shiny-input-radiogroup .checkbox .was-validated label>input:valid~.valid-feedback,.was-validated .shiny-input-radiogroup .radio label>input:valid~.valid-feedback,.shiny-input-radiogroup .radio .was-validated label>input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip,.was-validated .shiny-input-checkboxgroup .checkbox label>input:valid~.valid-tooltip,.shiny-input-checkboxgroup .checkbox .was-validated label>input:valid~.valid-tooltip,.was-validated .shiny-input-checkboxgroup .radio label>input:valid~.valid-tooltip,.shiny-input-checkboxgroup .radio .was-validated label>input:valid~.valid-tooltip,.was-validated .shiny-input-radiogroup .checkbox label>input:valid~.valid-tooltip,.shiny-input-radiogroup .checkbox .was-validated label>input:valid~.valid-tooltip,.was-validated .shiny-input-radiogroup .radio label>input:valid~.valid-tooltip,.shiny-input-radiogroup .radio .was-validated label>input:valid~.valid-tooltip,.form-check-input.is-valid~.valid-feedback,.shiny-input-checkboxgroup .checkbox label>input.is-valid~.valid-feedback,.shiny-input-checkboxgroup .radio label>input.is-valid~.valid-feedback,.shiny-input-radiogroup .checkbox label>input.is-valid~.valid-feedback,.shiny-input-radiogroup .radio label>input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.shiny-input-checkboxgroup .checkbox label>input.is-valid~.valid-tooltip,.shiny-input-checkboxgroup .radio label>input.is-valid~.valid-tooltip,.shiny-input-radiogroup .checkbox label>input.is-valid~.valid-tooltip,.shiny-input-radiogroup .radio label>input.is-valid~.valid-tooltip{display:block}.was-validated .custom-control-input:valid~.custom-control-label,.custom-control-input.is-valid~.custom-control-label{color:#28a745}.was-validated .custom-control-input:valid~.custom-control-label::before,.custom-control-input.is-valid~.custom-control-label::before{border-color:#28a745}.was-validated .custom-control-input:valid:checked~.custom-control-label::before,.custom-control-input.is-valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.was-validated .custom-control-input:valid:focus~.custom-control-label::before,.custom-control-input.is-valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,0.25)}.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before,.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.was-validated .custom-file-input:valid~.custom-file-label,.custom-file-input.is-valid~.custom-file-label{border-color:#28a745}.was-validated .custom-file-input:valid:focus~.custom-file-label,.custom-file-input.is-valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,0.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,0.9);border-radius:.25rem}.form-row>.col>.invalid-tooltip,.form-row>[class*="col-"]>.invalid-tooltip{left:5px}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,0.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.was-validated .custom-select:invalid,.custom-select.is-invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.was-validated .custom-select:invalid:focus,.custom-select.is-invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,0.25)}.was-validated .form-check-input:invalid~.form-check-label,.was-validated .shiny-input-checkboxgroup .checkbox label>input:invalid~.form-check-label,.shiny-input-checkboxgroup .checkbox .was-validated label>input:invalid~.form-check-label,.was-validated .shiny-input-checkboxgroup .radio label>input:invalid~.form-check-label,.shiny-input-checkboxgroup .radio .was-validated label>input:invalid~.form-check-label,.was-validated .shiny-input-radiogroup .checkbox label>input:invalid~.form-check-label,.shiny-input-radiogroup .checkbox .was-validated label>input:invalid~.form-check-label,.was-validated .shiny-input-radiogroup .radio label>input:invalid~.form-check-label,.shiny-input-radiogroup .radio .was-validated label>input:invalid~.form-check-label,.was-validated .shiny-input-checkboxgroup .checkbox .form-check-input:invalid~label,.was-validated .shiny-input-checkboxgroup .checkbox label>input:invalid~label,.was-validated .shiny-input-checkboxgroup .checkbox .radio label>input:invalid~label,.was-validated .shiny-input-checkboxgroup .radio .checkbox label>input:invalid~label,.was-validated .shiny-input-checkboxgroup .shiny-input-radiogroup .checkbox label>input:invalid~label,.shiny-input-radiogroup .was-validated .shiny-input-checkboxgroup .checkbox label>input:invalid~label,.was-validated .shiny-input-checkboxgroup .checkbox .shiny-input-radiogroup .radio label>input:invalid~label,.shiny-input-radiogroup .radio .was-validated .shiny-input-checkboxgroup .checkbox label>input:invalid~label,.shiny-input-checkboxgroup .checkbox .was-validated .form-check-input:invalid~label,.shiny-input-checkboxgroup .checkbox .was-validated label>input:invalid~label,.shiny-input-checkboxgroup .checkbox .was-validated .radio label>input:invalid~label,.shiny-input-checkboxgroup .radio .checkbox .was-validated label>input:invalid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .checkbox .was-validated label>input:invalid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .checkbox .was-validated label>input:invalid~label,.shiny-input-checkboxgroup .checkbox .was-validated .shiny-input-radiogroup .radio label>input:invalid~label,.shiny-input-radiogroup .radio .shiny-input-checkboxgroup .checkbox .was-validated label>input:invalid~label,.was-validated .shiny-input-checkboxgroup .radio .form-check-input:invalid~label,.was-validated .shiny-input-checkboxgroup .radio .checkbox label>input:invalid~label,.was-validated .shiny-input-checkboxgroup .checkbox .radio label>input:invalid~label,.was-validated .shiny-input-checkboxgroup .radio label>input:invalid~label,.was-validated .shiny-input-checkboxgroup .radio .shiny-input-radiogroup .checkbox label>input:invalid~label,.shiny-input-radiogroup .checkbox .was-validated .shiny-input-checkboxgroup .radio label>input:invalid~label,.was-validated .shiny-input-checkboxgroup .shiny-input-radiogroup .radio label>input:invalid~label,.shiny-input-radiogroup .was-validated .shiny-input-checkboxgroup .radio label>input:invalid~label,.shiny-input-checkboxgroup .radio .was-validated .form-check-input:invalid~label,.shiny-input-checkboxgroup .radio .was-validated .checkbox label>input:invalid~label,.shiny-input-checkboxgroup .checkbox .radio .was-validated label>input:invalid~label,.shiny-input-checkboxgroup .radio .was-validated label>input:invalid~label,.shiny-input-checkboxgroup .radio .was-validated .shiny-input-radiogroup .checkbox label>input:invalid~label,.shiny-input-radiogroup .checkbox .shiny-input-checkboxgroup .radio .was-validated label>input:invalid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .radio .was-validated label>input:invalid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .radio .was-validated label>input:invalid~label,.was-validated .shiny-input-radiogroup .checkbox .form-check-input:invalid~label,.was-validated .shiny-input-radiogroup .shiny-input-checkboxgroup .checkbox label>input:invalid~label,.shiny-input-checkboxgroup .was-validated .shiny-input-radiogroup .checkbox label>input:invalid~label,.was-validated .shiny-input-radiogroup .checkbox .shiny-input-checkboxgroup .radio label>input:invalid~label,.shiny-input-checkboxgroup .radio .was-validated .shiny-input-radiogroup .checkbox label>input:invalid~label,.was-validated .shiny-input-radiogroup .checkbox label>input:invalid~label,.was-validated .shiny-input-radiogroup .checkbox .radio label>input:invalid~label,.was-validated .shiny-input-radiogroup .radio .checkbox label>input:invalid~label,.shiny-input-radiogroup .checkbox .was-validated .form-check-input:invalid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .checkbox .was-validated label>input:invalid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .checkbox .was-validated label>input:invalid~label,.shiny-input-radiogroup .checkbox .was-validated .shiny-input-checkboxgroup .radio label>input:invalid~label,.shiny-input-checkboxgroup .radio .shiny-input-radiogroup .checkbox .was-validated label>input:invalid~label,.shiny-input-radiogroup .checkbox .was-validated label>input:invalid~label,.shiny-input-radiogroup .checkbox .was-validated .radio label>input:invalid~label,.shiny-input-radiogroup .radio .checkbox .was-validated label>input:invalid~label,.was-validated .shiny-input-radiogroup .radio .form-check-input:invalid~label,.was-validated .shiny-input-radiogroup .radio .shiny-input-checkboxgroup .checkbox label>input:invalid~label,.shiny-input-checkboxgroup .checkbox .was-validated .shiny-input-radiogroup .radio label>input:invalid~label,.was-validated .shiny-input-radiogroup .shiny-input-checkboxgroup .radio label>input:invalid~label,.shiny-input-checkboxgroup .was-validated .shiny-input-radiogroup .radio label>input:invalid~label,.was-validated .shiny-input-radiogroup .radio .checkbox label>input:invalid~label,.was-validated .shiny-input-radiogroup .checkbox .radio label>input:invalid~label,.was-validated .shiny-input-radiogroup .radio label>input:invalid~label,.shiny-input-radiogroup .radio .was-validated .form-check-input:invalid~label,.shiny-input-radiogroup .radio .was-validated .shiny-input-checkboxgroup .checkbox label>input:invalid~label,.shiny-input-checkboxgroup .checkbox .shiny-input-radiogroup .radio .was-validated label>input:invalid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .radio .was-validated label>input:invalid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .radio .was-validated label>input:invalid~label,.shiny-input-radiogroup .radio .was-validated .checkbox label>input:invalid~label,.shiny-input-radiogroup .checkbox .radio .was-validated label>input:invalid~label,.shiny-input-radiogroup .radio .was-validated label>input:invalid~label,.form-check-input.is-invalid~.form-check-label,.shiny-input-checkboxgroup .checkbox label>input.is-invalid~.form-check-label,.shiny-input-checkboxgroup .radio label>input.is-invalid~.form-check-label,.shiny-input-radiogroup .checkbox label>input.is-invalid~.form-check-label,.shiny-input-radiogroup .radio label>input.is-invalid~.form-check-label,.shiny-input-checkboxgroup .checkbox .form-check-input.is-invalid~label,.shiny-input-checkboxgroup .checkbox label>input.is-invalid~label,.shiny-input-checkboxgroup .checkbox .radio label>input.is-invalid~label,.shiny-input-checkboxgroup .radio .checkbox label>input.is-invalid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .checkbox label>input.is-invalid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .checkbox label>input.is-invalid~label,.shiny-input-checkboxgroup .checkbox .shiny-input-radiogroup .radio label>input.is-invalid~label,.shiny-input-radiogroup .radio .shiny-input-checkboxgroup .checkbox label>input.is-invalid~label,.shiny-input-checkboxgroup .radio .form-check-input.is-invalid~label,.shiny-input-checkboxgroup .radio .checkbox label>input.is-invalid~label,.shiny-input-checkboxgroup .checkbox .radio label>input.is-invalid~label,.shiny-input-checkboxgroup .radio label>input.is-invalid~label,.shiny-input-checkboxgroup .radio .shiny-input-radiogroup .checkbox label>input.is-invalid~label,.shiny-input-radiogroup .checkbox .shiny-input-checkboxgroup .radio label>input.is-invalid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .radio label>input.is-invalid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .radio label>input.is-invalid~label,.shiny-input-radiogroup .checkbox .form-check-input.is-invalid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .checkbox label>input.is-invalid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .checkbox label>input.is-invalid~label,.shiny-input-radiogroup .checkbox .shiny-input-checkboxgroup .radio label>input.is-invalid~label,.shiny-input-checkboxgroup .radio .shiny-input-radiogroup .checkbox label>input.is-invalid~label,.shiny-input-radiogroup .checkbox label>input.is-invalid~label,.shiny-input-radiogroup .checkbox .radio label>input.is-invalid~label,.shiny-input-radiogroup .radio .checkbox label>input.is-invalid~label,.shiny-input-radiogroup .radio .form-check-input.is-invalid~label,.shiny-input-radiogroup .radio .shiny-input-checkboxgroup .checkbox label>input.is-invalid~label,.shiny-input-checkboxgroup .checkbox .shiny-input-radiogroup .radio label>input.is-invalid~label,.shiny-input-radiogroup .shiny-input-checkboxgroup .radio label>input.is-invalid~label,.shiny-input-checkboxgroup .shiny-input-radiogroup .radio label>input.is-invalid~label,.shiny-input-radiogroup .radio .checkbox label>input.is-invalid~label,.shiny-input-radiogroup .checkbox .radio label>input.is-invalid~label,.shiny-input-radiogroup .radio label>input.is-invalid~label{color:#dc3545}.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .shiny-input-checkboxgroup .checkbox label>input:invalid~.invalid-feedback,.shiny-input-checkboxgroup .checkbox .was-validated label>input:invalid~.invalid-feedback,.was-validated .shiny-input-checkboxgroup .radio label>input:invalid~.invalid-feedback,.shiny-input-checkboxgroup .radio .was-validated label>input:invalid~.invalid-feedback,.was-validated .shiny-input-radiogroup .checkbox label>input:invalid~.invalid-feedback,.shiny-input-radiogroup .checkbox .was-validated label>input:invalid~.invalid-feedback,.was-validated .shiny-input-radiogroup .radio label>input:invalid~.invalid-feedback,.shiny-input-radiogroup .radio .was-validated label>input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip,.was-validated .shiny-input-checkboxgroup .checkbox label>input:invalid~.invalid-tooltip,.shiny-input-checkboxgroup .checkbox .was-validated label>input:invalid~.invalid-tooltip,.was-validated .shiny-input-checkboxgroup .radio label>input:invalid~.invalid-tooltip,.shiny-input-checkboxgroup .radio .was-validated label>input:invalid~.invalid-tooltip,.was-validated .shiny-input-radiogroup .checkbox label>input:invalid~.invalid-tooltip,.shiny-input-radiogroup .checkbox .was-validated label>input:invalid~.invalid-tooltip,.was-validated .shiny-input-radiogroup .radio label>input:invalid~.invalid-tooltip,.shiny-input-radiogroup .radio .was-validated label>input:invalid~.invalid-tooltip,.form-check-input.is-invalid~.invalid-feedback,.shiny-input-checkboxgroup .checkbox label>input.is-invalid~.invalid-feedback,.shiny-input-checkboxgroup .radio label>input.is-invalid~.invalid-feedback,.shiny-input-radiogroup .checkbox label>input.is-invalid~.invalid-feedback,.shiny-input-radiogroup .radio label>input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.shiny-input-checkboxgroup .checkbox label>input.is-invalid~.invalid-tooltip,.shiny-input-checkboxgroup .radio label>input.is-invalid~.invalid-tooltip,.shiny-input-radiogroup .checkbox label>input.is-invalid~.invalid-tooltip,.shiny-input-radiogroup .radio label>input.is-invalid~.invalid-tooltip{display:block}.was-validated .custom-control-input:invalid~.custom-control-label,.custom-control-input.is-invalid~.custom-control-label{color:#dc3545}.was-validated .custom-control-input:invalid~.custom-control-label::before,.custom-control-input.is-invalid~.custom-control-label::before{border-color:#dc3545}.was-validated .custom-control-input:invalid:checked~.custom-control-label::before,.custom-control-input.is-invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.was-validated .custom-control-input:invalid:focus~.custom-control-label::before,.custom-control-input.is-invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,0.25)}.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before,.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.was-validated .custom-file-input:invalid~.custom-file-label,.custom-file-input.is-invalid~.custom-file-label{border-color:#dc3545}.was-validated .custom-file-input:invalid:focus~.custom-file-label,.custom-file-input.is-invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,0.25)}.form-inline{display:flex;display:-webkit-flex;flex-flow:row wrap;-webkit-flex-flow:row wrap;align-items:center;-webkit-align-items:center}.form-inline .form-check,.form-inline .shiny-input-checkboxgroup .checkbox,.shiny-input-checkboxgroup .form-inline .checkbox,.form-inline .shiny-input-checkboxgroup .radio,.shiny-input-checkboxgroup .form-inline .radio,.form-inline .shiny-input-radiogroup .checkbox,.shiny-input-radiogroup .form-inline .checkbox,.form-inline .shiny-input-radiogroup .radio,.shiny-input-radiogroup .form-inline .radio{width:100%}@media (min-width: 576px){.form-inline label{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;justify-content:center;-webkit-justify-content:center;margin-bottom:0}.form-inline .form-group{display:flex;display:-webkit-flex;flex:0 0 auto;-webkit-flex:0 0 auto;flex-flow:row wrap;-webkit-flex-flow:row wrap;align-items:center;-webkit-align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .input-group,.form-inline .custom-select{width:auto}.form-inline .form-check,.form-inline .shiny-input-checkboxgroup .checkbox,.shiny-input-checkboxgroup .form-inline .checkbox,.form-inline .shiny-input-checkboxgroup .radio,.shiny-input-checkboxgroup .form-inline .radio,.form-inline .shiny-input-radiogroup .checkbox,.shiny-input-radiogroup .form-inline .checkbox,.form-inline .shiny-input-radiogroup .radio,.shiny-input-radiogroup .form-inline .radio{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;justify-content:center;-webkit-justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input,.form-inline .shiny-input-checkboxgroup .checkbox label>input,.shiny-input-checkboxgroup .checkbox .form-inline label>input,.form-inline .shiny-input-checkboxgroup .radio label>input,.shiny-input-checkboxgroup .radio .form-inline label>input,.form-inline .shiny-input-radiogroup .checkbox label>input,.shiny-input-radiogroup .checkbox .form-inline label>input,.form-inline .shiny-input-radiogroup .radio label>input,.shiny-input-radiogroup .radio .form-inline label>input{position:relative;flex-shrink:0;-webkit-flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{align-items:center;-webkit-align-items:center;justify-content:center;-webkit-justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn:focus,.btn.focus{outline:0;box-shadow:0 0 0 .2rem rgba(9,107,114,0.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-default{color:#000;background-color:#dee2e6;border-color:#dee2e6}.btn-default:hover{color:#000;background-color:#c8cfd6;border-color:#c1c9d0}.btn-default:focus,.btn-default.focus{color:#000;background-color:#c8cfd6;border-color:#c1c9d0;box-shadow:0 0 0 .2rem rgba(189,192,196,0.5)}.btn-default.disabled,.btn-default:disabled{color:#000;background-color:#dee2e6;border-color:#dee2e6}.btn-default:not(:disabled):not(.disabled):active,.btn-default.active:not(:disabled):not(.disabled),.show>.btn-default.dropdown-toggle,.in>.btn-default.dropdown-toggle{color:#000;background-color:#c1c9d0;border-color:#bac2cb}.btn-default:not(:disabled):not(.disabled):active:focus,.btn-default.active:not(:disabled):not(.disabled):focus,.show>.btn-default.dropdown-toggle:focus,.in>.btn-default.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(189,192,196,0.5)}.btn-primary{color:#fff;background-color:#096B72;border-color:#096B72}.btn-primary:hover{color:#fff;background-color:#064a4f;border-color:#053f43}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#064a4f;border-color:#053f43;box-shadow:0 0 0 .2rem rgba(46,129,135,0.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#096B72;border-color:#096B72}.btn-primary:not(:disabled):not(.disabled):active,.btn-primary.active:not(:disabled):not(.disabled),.show>.btn-primary.dropdown-toggle,.in>.btn-primary.dropdown-toggle{color:#fff;background-color:#053f43;border-color:#043437}.btn-primary:not(:disabled):not(.disabled):active:focus,.btn-primary.active:not(:disabled):not(.disabled):focus,.show>.btn-primary.dropdown-toggle:focus,.in>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(46,129,135,0.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary:focus,.btn-secondary.focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,0.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled):active,.btn-secondary.active:not(:disabled):not(.disabled),.show>.btn-secondary.dropdown-toggle,.in>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled):active:focus,.btn-secondary.active:not(:disabled):not(.disabled):focus,.show>.btn-secondary.dropdown-toggle:focus,.in>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,0.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,0.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled):active,.btn-success.active:not(:disabled):not(.disabled),.show>.btn-success.dropdown-toggle,.in>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled):active:focus,.btn-success.active:not(:disabled):not(.disabled):focus,.show>.btn-success.dropdown-toggle:focus,.in>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,0.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,0.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled):active,.btn-info.active:not(:disabled):not(.disabled),.show>.btn-info.dropdown-toggle,.in>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled):active:focus,.btn-info.active:not(:disabled):not(.disabled):focus,.show>.btn-info.dropdown-toggle:focus,.in>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,0.5)}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#e0a800;border-color:#d39e00}.btn-warning:focus,.btn-warning.focus{color:#000;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(217,164,6,0.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled):active,.btn-warning.active:not(:disabled):not(.disabled),.show>.btn-warning.dropdown-toggle,.in>.btn-warning.dropdown-toggle{color:#000;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled):active:focus,.btn-warning.active:not(:disabled):not(.disabled):focus,.show>.btn-warning.dropdown-toggle:focus,.in>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(217,164,6,0.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,0.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled):active,.btn-danger.active:not(:disabled):not(.disabled),.show>.btn-danger.dropdown-toggle,.in>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled):active:focus,.btn-danger.active:not(:disabled):not(.disabled):focus,.show>.btn-danger.dropdown-toggle:focus,.in>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,0.5)}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#e2e6ea;border-color:#dae0e5}.btn-light:focus,.btn-light.focus{color:#000;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(211,212,213,0.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled):active,.btn-light.active:not(:disabled):not(.disabled),.show>.btn-light.dropdown-toggle,.in>.btn-light.dropdown-toggle{color:#000;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled):active:focus,.btn-light.active:not(:disabled):not(.disabled):focus,.show>.btn-light.dropdown-toggle:focus,.in>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(211,212,213,0.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark:focus,.btn-dark.focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,0.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled):active,.btn-dark.active:not(:disabled):not(.disabled),.show>.btn-dark.dropdown-toggle,.in>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled):active:focus,.btn-dark.active:not(:disabled):not(.disabled):focus,.show>.btn-dark.dropdown-toggle:focus,.in>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,0.5)}.btn-outline-default{color:#dee2e6;border-color:#dee2e6;background-color:transparent}.btn-outline-default:hover{color:#000;background-color:#dee2e6;border-color:#dee2e6}.btn-outline-default:focus,.btn-outline-default.focus{box-shadow:0 0 0 .2rem rgba(222,226,230,0.5)}.btn-outline-default.disabled,.btn-outline-default:disabled{color:#dee2e6;background-color:transparent}.btn-outline-default:not(:disabled):not(.disabled):active,.btn-outline-default.active:not(:disabled):not(.disabled),.show>.btn-outline-default.dropdown-toggle,.in>.btn-outline-default.dropdown-toggle{color:#000;background-color:#dee2e6;border-color:#dee2e6}.btn-outline-default:not(:disabled):not(.disabled):active:focus,.btn-outline-default.active:not(:disabled):not(.disabled):focus,.show>.btn-outline-default.dropdown-toggle:focus,.in>.btn-outline-default.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,226,230,0.5)}.btn-outline-primary{color:#096B72;border-color:#096B72;background-color:transparent}.btn-outline-primary:hover{color:#fff;background-color:#096B72;border-color:#096B72}.btn-outline-primary:focus,.btn-outline-primary.focus{box-shadow:0 0 0 .2rem rgba(9,107,114,0.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#096B72;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled):active,.btn-outline-primary.active:not(:disabled):not(.disabled),.show>.btn-outline-primary.dropdown-toggle,.in>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#096B72;border-color:#096B72}.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.btn-outline-primary.active:not(:disabled):not(.disabled):focus,.show>.btn-outline-primary.dropdown-toggle:focus,.in>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(9,107,114,0.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d;background-color:transparent}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:focus,.btn-outline-secondary.focus{box-shadow:0 0 0 .2rem rgba(108,117,125,0.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled):active,.btn-outline-secondary.active:not(:disabled):not(.disabled),.show>.btn-outline-secondary.dropdown-toggle,.in>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.btn-outline-secondary.active:not(:disabled):not(.disabled):focus,.show>.btn-outline-secondary.dropdown-toggle:focus,.in>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,0.5)}.btn-outline-success{color:#28a745;border-color:#28a745;background-color:transparent}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:focus,.btn-outline-success.focus{box-shadow:0 0 0 .2rem rgba(40,167,69,0.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled):active,.btn-outline-success.active:not(:disabled):not(.disabled),.show>.btn-outline-success.dropdown-toggle,.in>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled):active:focus,.btn-outline-success.active:not(:disabled):not(.disabled):focus,.show>.btn-outline-success.dropdown-toggle:focus,.in>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,0.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8;background-color:transparent}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:focus,.btn-outline-info.focus{box-shadow:0 0 0 .2rem rgba(23,162,184,0.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled):active,.btn-outline-info.active:not(:disabled):not(.disabled),.show>.btn-outline-info.dropdown-toggle,.in>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled):active:focus,.btn-outline-info.active:not(:disabled):not(.disabled):focus,.show>.btn-outline-info.dropdown-toggle:focus,.in>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,0.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107;background-color:transparent}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:focus,.btn-outline-warning.focus{box-shadow:0 0 0 .2rem rgba(255,193,7,0.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled):active,.btn-outline-warning.active:not(:disabled):not(.disabled),.show>.btn-outline-warning.dropdown-toggle,.in>.btn-outline-warning.dropdown-toggle{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.btn-outline-warning.active:not(:disabled):not(.disabled):focus,.show>.btn-outline-warning.dropdown-toggle:focus,.in>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,0.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545;background-color:transparent}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:focus,.btn-outline-danger.focus{box-shadow:0 0 0 .2rem rgba(220,53,69,0.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled):active,.btn-outline-danger.active:not(:disabled):not(.disabled),.show>.btn-outline-danger.dropdown-toggle,.in>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.btn-outline-danger.active:not(:disabled):not(.disabled):focus,.show>.btn-outline-danger.dropdown-toggle:focus,.in>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,0.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa;background-color:transparent}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:focus,.btn-outline-light.focus{box-shadow:0 0 0 .2rem rgba(248,249,250,0.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled):active,.btn-outline-light.active:not(:disabled):not(.disabled),.show>.btn-outline-light.dropdown-toggle,.in>.btn-outline-light.dropdown-toggle{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled):active:focus,.btn-outline-light.active:not(:disabled):not(.disabled):focus,.show>.btn-outline-light.dropdown-toggle:focus,.in>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,0.5)}.btn-outline-dark{color:#343a40;border-color:#343a40;background-color:transparent}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:focus,.btn-outline-dark.focus{box-shadow:0 0 0 .2rem rgba(52,58,64,0.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled):active,.btn-outline-dark.active:not(:disabled):not(.disabled),.show>.btn-outline-dark.dropdown-toggle,.in>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.btn-outline-dark.active:not(:disabled):not(.disabled):focus,.show>.btn-outline-dark.dropdown-toggle:focus,.in>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,0.5)}.btn-link{font-weight:400;color:#096B72;text-decoration:none;-webkit-text-decoration:none;-moz-text-decoration:none;-ms-text-decoration:none;-o-text-decoration:none}.btn-link:hover{color:#03282b;text-decoration:underline;-webkit-text-decoration:underline;-moz-text-decoration:underline;-ms-text-decoration:underline;-o-text-decoration:underline}.btn-link:focus,.btn-link.focus{text-decoration:underline;-webkit-text-decoration:underline;-moz-text-decoration:underline;-ms-text-decoration:underline;-o-text-decoration:underline}.btn-link:disabled,.btn-link.disabled{color:#6c757d;pointer-events:none}.btn-lg,.btn-group-lg>.btn{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-sm,.btn-group-sm>.btn{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input.btn-block[type="submit"],input.btn-block[type="reset"],input.btn-block[type="button"]{width:100%}.fade{transition:opacity 0.15s linear}@media (prefers-reduced-motion: reduce){.fade{transition:none}}.fade:not(.show):not(.in){opacity:0}.collapse:not(.show):not(.in){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height 0.35s ease}@media (prefers-reduced-motion: reduce){.collapsing{transition:none}}.dropup,.dropright,.dropdown,.dropleft{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,0.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width: 576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width: 768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width: 992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width: 1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^="top"],.dropdown-menu[x-placement^="right"],.dropdown-menu[x-placement^="bottom"],.dropdown-menu[x-placement^="left"]{right:auto;bottom:auto}.dropdown-divider,.dropdown-menu>li.divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item,.dropdown-menu>li>a{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:hover,.dropdown-menu>li>a:hover,.dropdown-item:focus,.dropdown-menu>li>a:focus{color:#16181b;text-decoration:none;background-color:#e9ecef}.dropdown-item.active,.dropdown-menu>li>a.active,.dropdown-item:active,.dropdown-menu>li>a:active{color:#fff;text-decoration:none;background-color:#096B72}.dropdown-item.disabled,.dropdown-menu>li>a.disabled,.dropdown-item:disabled,.dropdown-menu>li>a:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show,.dropdown-menu.in{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;flex:1 1 auto;-webkit-flex:1 1 auto}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover{z-index:1}.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-toolbar{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;justify-content:flex-start;-webkit-justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child){margin-left:-1px}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;-webkit-flex-direction:column;align-items:flex-start;-webkit-align-items:flex-start;justify-content:center;-webkit-justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type="radio"],.btn-group-toggle>.btn input[type="checkbox"],.btn-group-toggle>.btn-group>.btn input[type="radio"],.btn-group-toggle>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;align-items:stretch;-webkit-align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-control-plaintext,.input-group>.custom-select,.input-group>.custom-file{position:relative;flex:1 1 auto;-webkit-flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.form-control+.form-control,.input-group>.form-control+.custom-select,.input-group>.form-control+.custom-file,.input-group>.form-control-plaintext+.form-control,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.custom-file,.input-group>.custom-select+.form-control,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.custom-file,.input-group>.custom-file+.form-control,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.custom-file{margin-left:-1px}.input-group>.form-control:focus,.input-group>.custom-select:focus,.input-group>.custom-file .custom-file-input:focus~.custom-file-label{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.form-control:not(:first-child),.input-group>.custom-select:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group:not(.has-validation)>.form-control:not(:last-child),.input-group:not(.has-validation)>.custom-select:not(:last-child),.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.form-control:nth-last-child(n + 3),.input-group.has-validation>.custom-select:nth-last-child(n + 3),.input-group.has-validation>.custom-file:nth-last-child(n + 3) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-prepend,.input-group-append{display:flex;display:-webkit-flex}.input-group-prepend .btn,.input-group-append .btn{position:relative;z-index:2}.input-group-prepend .btn:focus,.input-group-append .btn:focus{z-index:3}.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.input-group-text,.input-group-append .input-group-text+.btn{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type="radio"],.input-group-text input[type="checkbox"]{margin-top:0}.input-group-lg>.form-control:not(textarea),.input-group-lg>.custom-select{height:calc(1.5em + 1rem + 2px)}.input-group-lg>.form-control,.input-group-lg>.custom-select,.input-group-lg>.input-group-prepend>.input-group-text,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-append>.btn{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.form-control:not(textarea),.input-group-sm>.custom-select{height:calc(1.5em + .5rem + 2px)}.input-group-sm>.form-control,.input-group-sm>.custom-select,.input-group-sm>.input-group-prepend>.input-group-text,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-append>.btn{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.btn,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.input-group-text,.input-group.has-validation>.input-group-append:nth-last-child(n + 3)>.btn,.input-group.has-validation>.input-group-append:nth-last-child(n + 3)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem;color-adjust:exact;-webkit-print-color-adjust:exact}.custom-control-inline{display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#096B72;background-color:#096B72}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(9,107,114,0.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#12dae8}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#000;background-color:#3ee4f0;border-color:#3ee4f0}.custom-control-input[disabled]~.custom-control-label,.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input[disabled]~.custom-control-label::before,.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:50% / 50% 50% no-repeat}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#096B72;background-color:#096B72}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(9,107,114,0.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(9,107,114,0.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(9,107,114,0.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:transform 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(9,107,114,0.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat;border:1px solid #ced4da;border-radius:.25rem;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none}.custom-select:focus{border-color:#12dae8;outline:0;box-shadow:0 0 0 .2rem rgba(9,107,114,0.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;overflow:hidden;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#12dae8;box-shadow:0 0 0 .2rem rgba(9,107,114,0.25)}.custom-file-input[disabled]~.custom-file-label,.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;overflow:hidden;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(9,107,114,0.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(9,107,114,0.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(9,107,114,0.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#096B72;border:0;border-radius:1rem;transition:background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#3ee4f0}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#096B72;border:0;border-radius:1rem;transition:background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#3ee4f0}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#096B72;border:0;border-radius:1rem;transition:background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#3ee4f0}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link,.nav-tabs>li>a,.nav-pills>li>a,ul.nav.navbar-nav>li>a{display:block;padding:.5rem 1rem}.nav-link:hover,.nav-tabs>li>a:hover,.nav-pills>li>a:hover,ul.nav.navbar-nav>li>a:hover,.nav-link:focus,.nav-tabs>li>a:focus,.nav-pills>li>a:focus,ul.nav.navbar-nav>li>a:focus{text-decoration:none}.nav-link.disabled,.nav-tabs>li>a.disabled,.nav-pills>li>a.disabled,ul.nav.navbar-nav>li>a.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link,.nav-tabs>li>a,.nav-tabs .nav-pills>li>a,.nav-tabs ul.nav.navbar-nav>li>a{margin-bottom:-1px;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:hover,.nav-tabs>li>a:hover,.nav-tabs .nav-pills>li>a:hover,.nav-tabs ul.nav.navbar-nav>li>a:hover,.nav-tabs .nav-link:focus,.nav-tabs>li>a:focus,.nav-tabs .nav-pills>li>a:focus,.nav-tabs ul.nav.navbar-nav>li>a:focus{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled,.nav-tabs>li>a.disabled,.nav-tabs .nav-pills>li>a.disabled,.nav-tabs ul.nav.navbar-nav>li>a.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs>li>a.active,.nav-tabs .nav-pills>li>a.active,.nav-tabs ul.nav.navbar-nav>li>a.active,.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-item.in .nav-link,.nav-tabs .nav-item.show .nav-tabs>li>a,.nav-tabs .nav-item.in .nav-tabs>li>a,.nav-tabs .nav-item.show .nav-pills>li>a,.nav-tabs .nav-item.in .nav-pills>li>a,.nav-tabs>li.show .nav-link,.nav-tabs>li.in .nav-link,.nav-tabs>li.show .nav-tabs>li>a,.nav-tabs>li.in .nav-tabs>li>a,.nav-tabs>li.show .nav-pills>li>a,.nav-tabs>li.in .nav-pills>li>a,.nav-tabs .nav-pills>li.show .nav-link,.nav-tabs .nav-pills>li.in .nav-link,.nav-tabs .nav-pills>li.show .nav-tabs>li>a,.nav-tabs .nav-pills>li.in .nav-tabs>li>a,.nav-tabs .nav-pills>li.show .nav-pills>li>a,.nav-tabs .nav-pills>li.in .nav-pills>li>a,.nav-tabs .nav-item.show ul.nav.navbar-nav>li>a,.nav-tabs .nav-item.in ul.nav.navbar-nav>li>a,.nav-tabs>li.show ul.nav.navbar-nav>li>a,.nav-tabs>li.in ul.nav.navbar-nav>li>a,.nav-tabs .nav-pills>li.show ul.nav.navbar-nav>li>a,.nav-tabs .nav-pills>li.in ul.nav.navbar-nav>li>a,.nav-tabs ul.nav.navbar-nav>li.show:not(.dropdown) .nav-link,.nav-tabs ul.nav.navbar-nav>li.in:not(.dropdown) .nav-link,.nav-tabs ul.nav.navbar-nav>li.show:not(.dropdown) .nav-tabs>li>a,.nav-tabs ul.nav.navbar-nav>li.in:not(.dropdown) .nav-tabs>li>a,.nav-tabs ul.nav.navbar-nav>li.show:not(.dropdown) .nav-pills>li>a,.nav-tabs ul.nav.navbar-nav>li.in:not(.dropdown) .nav-pills>li>a,.nav-tabs ul.nav.navbar-nav>li.show:not(.dropdown) ul.nav.navbar-nav>li>a,.nav-tabs ul.nav.navbar-nav>li.in:not(.dropdown) ul.nav.navbar-nav>li>a{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link,.nav-pills .nav-tabs>li>a,.nav-pills>li>a,.nav-pills ul.nav.navbar-nav>li>a{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .nav-tabs>li>a.active,.nav-pills>li>a.active,.nav-pills ul.nav.navbar-nav>li>a.active,.nav-pills .show>.nav-link,.nav-pills .in>.nav-link,.nav-pills .nav-tabs>li.show>a,.nav-pills .nav-tabs>li.in>a,.nav-pills>li.show>a,.nav-pills>li.in>a,.nav-pills ul.nav.navbar-nav>li.show>a,.nav-pills ul.nav.navbar-nav>li.in>a{color:#fff;background-color:#096B72}.nav-fill>.nav-link,.nav-tabs>li.nav-fill>a,.nav-pills>li.nav-fill>a,ul.nav.navbar-nav>li.nav-fill>a,.nav-fill .nav-item,.nav-fill .nav-tabs>li,.nav-fill .nav-pills>li,.nav-fill ul.nav.navbar-nav>li:not(.dropdown){flex:1 1 auto;-webkit-flex:1 1 auto;text-align:center}.nav-justified>.nav-link,.nav-tabs>li.nav-justified>a,.nav-pills>li.nav-justified>a,ul.nav.navbar-nav>li.nav-justified>a,.nav-justified .nav-item,.nav-justified .nav-tabs>li,.nav-justified .nav-pills>li,.nav-justified ul.nav.navbar-nav>li:not(.dropdown){flex-basis:0;-webkit-flex-basis:0;flex-grow:1;-webkit-flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;align-items:center;-webkit-align-items:center;justify-content:space-between;-webkit-justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-sm,.navbar .container-md,.navbar .container-lg,.navbar .container-xl{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;align-items:center;-webkit-align-items:center;justify-content:space-between;-webkit-justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-nav{display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link,.navbar-nav .nav-tabs>li>a,.navbar-nav .nav-pills>li>a,ul.nav.navbar-nav>li>a{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;-webkit-flex-basis:100%;flex-grow:1;-webkit-flex-grow:1;align-items:center;-webkit-align-items:center}.navbar-toggler,.navbar-toggle{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:hover,.navbar-toggle:hover,.navbar-toggler:focus,.navbar-toggle:focus{text-decoration:none}.navbar-toggler-icon,.navbar-toggle>.icon-bar:last-child{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:50% / 100% 100% no-repeat}.navbar-nav-scroll{max-height:75vh;overflow-y:auto}@media (max-width: 575.98px){.navbar-expand-sm>.container,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container,.navbar-expand-sm>.container-fluid,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container-fluid,.navbar-expand-sm>.container-sm,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container-sm,.navbar-expand-sm>.container-md,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container-md,.navbar-expand-sm>.container-lg,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container-lg,.navbar-expand-sm>.container-xl,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 576px){.navbar-expand-sm,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl){flex-flow:row nowrap;-webkit-flex-flow:row nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand-sm .navbar-nav,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link,.navbar-expand-sm .navbar-nav .nav-tabs>li>a,.navbar-expand-sm .navbar-nav .nav-pills>li>a,.navbar-expand-sm ul.nav.navbar-nav>li>a,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) .navbar-nav .nav-link,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) .navbar-nav .nav-tabs>li>a,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) .navbar-nav .nav-pills>li>a,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) ul.nav.navbar-nav>li>a{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container,.navbar-expand-sm>.container-fluid,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container-fluid,.navbar-expand-sm>.container-sm,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container-sm,.navbar-expand-sm>.container-md,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container-md,.navbar-expand-sm>.container-lg,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container-lg,.navbar-expand-sm>.container-xl,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl)>.container-xl{flex-wrap:nowrap;-webkit-flex-wrap:nowrap}.navbar-expand-sm .navbar-nav-scroll,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand-sm .navbar-toggler,.navbar-expand-sm .navbar-toggle,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) .navbar-toggler,.navbar:not(.navbar-expand):not(.navbar-expand-sm):not(.navbar-expand-md):not(.navbar-expand-lg):not(.navbar-expand-xl) .navbar-toggle{display:none}}@media (max-width: 767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-md,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 768px){.navbar-expand-md{flex-flow:row nowrap;-webkit-flex-flow:row nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link,.navbar-expand-md .navbar-nav .nav-tabs>li>a,.navbar-expand-md .navbar-nav .nav-pills>li>a,.navbar-expand-md ul.nav.navbar-nav>li>a{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-md,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-xl{flex-wrap:nowrap;-webkit-flex-wrap:nowrap}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand-md .navbar-toggler,.navbar-expand-md .navbar-toggle{display:none}}@media (max-width: 991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 992px){.navbar-expand-lg{flex-flow:row nowrap;-webkit-flex-flow:row nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link,.navbar-expand-lg .navbar-nav .nav-tabs>li>a,.navbar-expand-lg .navbar-nav .nav-pills>li>a,.navbar-expand-lg ul.nav.navbar-nav>li>a{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-xl{flex-wrap:nowrap;-webkit-flex-wrap:nowrap}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand-lg .navbar-toggler,.navbar-expand-lg .navbar-toggle{display:none}}@media (max-width: 1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 1200px){.navbar-expand-xl{flex-flow:row nowrap;-webkit-flex-flow:row nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link,.navbar-expand-xl .navbar-nav .nav-tabs>li>a,.navbar-expand-xl .navbar-nav .nav-pills>li>a,.navbar-expand-xl ul.nav.navbar-nav>li>a{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-xl{flex-wrap:nowrap;-webkit-flex-wrap:nowrap}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand-xl .navbar-toggler,.navbar-expand-xl .navbar-toggle{display:none}}.navbar-expand{flex-flow:row nowrap;-webkit-flex-flow:row nowrap;justify-content:flex-start;-webkit-justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-sm,.navbar-expand>.container-md,.navbar-expand>.container-lg,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{flex-direction:row;-webkit-flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link,.navbar-expand .navbar-nav .nav-tabs>li>a,.navbar-expand .navbar-nav .nav-pills>li>a,.navbar-expand ul.nav.navbar-nav>li>a{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-sm,.navbar-expand>.container-md,.navbar-expand>.container-lg,.navbar-expand>.container-xl{flex-wrap:nowrap;-webkit-flex-wrap:nowrap}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex !important;display:-webkit-flex !important;flex-basis:auto;-webkit-flex-basis:auto}.navbar-expand .navbar-toggler,.navbar-expand .navbar-toggle{display:none}.navbar-light,.navbar.navbar-default{background-color:#f8f9fa}.navbar-light .navbar-brand,.navbar.navbar-default .navbar-brand{color:#000}.navbar-light .navbar-brand:hover,.navbar.navbar-default .navbar-brand:hover,.navbar-light .navbar-brand:focus,.navbar.navbar-default .navbar-brand:focus{color:#000}.navbar-light .navbar-nav .nav-link,.navbar-light .navbar-nav .nav-tabs>li>a,.navbar-light .navbar-nav .nav-pills>li>a,.navbar.navbar-default .navbar-nav .nav-link,.navbar.navbar-default .navbar-nav .nav-tabs>li>a,.navbar.navbar-default .navbar-nav .nav-pills>li>a,.navbar-light ul.nav.navbar-nav>li>a,.navbar.navbar-default ul.nav.navbar-nav>li>a{color:rgba(0,0,0,0.5)}.navbar-light .navbar-nav .nav-link:hover,.navbar-light .navbar-nav .nav-tabs>li>a:hover,.navbar-light .navbar-nav .nav-pills>li>a:hover,.navbar.navbar-default .navbar-nav .nav-link:hover,.navbar.navbar-default .navbar-nav .nav-tabs>li>a:hover,.navbar.navbar-default .navbar-nav .nav-pills>li>a:hover,.navbar-light ul.nav.navbar-nav>li>a:hover,.navbar.navbar-default ul.nav.navbar-nav>li>a:hover,.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-tabs>li>a:focus,.navbar-light .navbar-nav .nav-pills>li>a:focus,.navbar.navbar-default .navbar-nav .nav-link:focus,.navbar.navbar-default .navbar-nav .nav-tabs>li>a:focus,.navbar.navbar-default .navbar-nav .nav-pills>li>a:focus,.navbar-light ul.nav.navbar-nav>li>a:focus,.navbar.navbar-default ul.nav.navbar-nav>li>a:focus{color:rgba(0,0,0,0.75)}.navbar-light .navbar-nav .nav-link.disabled,.navbar-light .navbar-nav .nav-tabs>li>a.disabled,.navbar-light .navbar-nav .nav-pills>li>a.disabled,.navbar.navbar-default .navbar-nav .nav-link.disabled,.navbar.navbar-default .navbar-nav .nav-tabs>li>a.disabled,.navbar.navbar-default .navbar-nav .nav-pills>li>a.disabled,.navbar-light ul.nav.navbar-nav>li>a.disabled,.navbar.navbar-default ul.nav.navbar-nav>li>a.disabled{color:rgba(0,0,0,0.25)}.navbar-light .navbar-nav .show>.nav-link,.navbar-light .navbar-nav .in>.nav-link,.navbar-light .navbar-nav .nav-tabs>li.show>a,.navbar-light .navbar-nav .nav-tabs>li.in>a,.navbar-light .navbar-nav .nav-pills>li.show>a,.navbar-light .navbar-nav .nav-pills>li.in>a,.navbar.navbar-default .navbar-nav .show>.nav-link,.navbar.navbar-default .navbar-nav .in>.nav-link,.navbar.navbar-default .navbar-nav .nav-tabs>li.show>a,.navbar.navbar-default .navbar-nav .nav-tabs>li.in>a,.navbar.navbar-default .navbar-nav .nav-pills>li.show>a,.navbar.navbar-default .navbar-nav .nav-pills>li.in>a,.navbar-light ul.nav.navbar-nav>li.show>a,.navbar-light ul.nav.navbar-nav>li.in>a,.navbar.navbar-default ul.nav.navbar-nav>li.show>a,.navbar.navbar-default ul.nav.navbar-nav>li.in>a,.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-tabs>li.active>a,.navbar-light .navbar-nav .nav-pills>li.active>a,.navbar.navbar-default .navbar-nav .active>.nav-link,.navbar.navbar-default .navbar-nav .nav-tabs>li.active>a,.navbar.navbar-default .navbar-nav .nav-pills>li.active>a,.navbar-light ul.nav.navbar-nav>li.active>a,.navbar.navbar-default ul.nav.navbar-nav>li.active>a,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .nav-link.in,.navbar-light .navbar-nav .nav-tabs>li>a.show,.navbar-light .navbar-nav .nav-tabs>li>a.in,.navbar-light .navbar-nav .nav-pills>li>a.show,.navbar-light .navbar-nav .nav-pills>li>a.in,.navbar.navbar-default .navbar-nav .nav-link.show,.navbar.navbar-default .navbar-nav .nav-link.in,.navbar.navbar-default .navbar-nav .nav-tabs>li>a.show,.navbar.navbar-default .navbar-nav .nav-tabs>li>a.in,.navbar.navbar-default .navbar-nav .nav-pills>li>a.show,.navbar.navbar-default .navbar-nav .nav-pills>li>a.in,.navbar-light ul.nav.navbar-nav>li>a.show,.navbar-light ul.nav.navbar-nav>li>a.in,.navbar.navbar-default ul.nav.navbar-nav>li>a.show,.navbar.navbar-default ul.nav.navbar-nav>li>a.in,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-tabs>li>a.active,.navbar-light .navbar-nav .nav-pills>li>a.active,.navbar.navbar-default .navbar-nav .nav-link.active,.navbar.navbar-default .navbar-nav .nav-tabs>li>a.active,.navbar.navbar-default .navbar-nav .nav-pills>li>a.active,.navbar-light ul.nav.navbar-nav>li>a.active,.navbar.navbar-default ul.nav.navbar-nav>li>a.active{color:#000}.navbar-light .navbar-toggler,.navbar-light .navbar-toggle,.navbar.navbar-default .navbar-toggler,.navbar.navbar-default .navbar-toggle{color:rgba(0,0,0,0.5);border-color:rgba(0,0,0,0.1)}.navbar-light .navbar-toggler-icon,.navbar-light .navbar-toggle>.icon-bar:last-child,.navbar.navbar-default .navbar-toggler-icon,.navbar.navbar-default .navbar-toggle>.icon-bar:last-child{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280,0,0,0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text,.navbar.navbar-default .navbar-text{color:rgba(0,0,0,0.5)}.navbar-light .navbar-text a,.navbar.navbar-default .navbar-text a{color:#000}.navbar-light .navbar-text a:hover,.navbar.navbar-default .navbar-text a:hover,.navbar-light .navbar-text a:focus,.navbar.navbar-default .navbar-text a:focus{color:#000}.navbar-dark,.navbar.navbar-inverse{background-color:#343a40}.navbar-dark .navbar-brand,.navbar.navbar-inverse .navbar-brand{color:#fff}.navbar-dark .navbar-brand:hover,.navbar.navbar-inverse .navbar-brand:hover,.navbar-dark .navbar-brand:focus,.navbar.navbar-inverse .navbar-brand:focus{color:#fff}.navbar-dark .navbar-nav .nav-link,.navbar-dark .navbar-nav .nav-tabs>li>a,.navbar-dark .navbar-nav .nav-pills>li>a,.navbar.navbar-inverse .navbar-nav .nav-link,.navbar.navbar-inverse .navbar-nav .nav-tabs>li>a,.navbar.navbar-inverse .navbar-nav .nav-pills>li>a,.navbar-dark ul.nav.navbar-nav>li>a,.navbar.navbar-inverse ul.nav.navbar-nav>li>a{color:rgba(255,255,255,0.5)}.navbar-dark .navbar-nav .nav-link:hover,.navbar-dark .navbar-nav .nav-tabs>li>a:hover,.navbar-dark .navbar-nav .nav-pills>li>a:hover,.navbar.navbar-inverse .navbar-nav .nav-link:hover,.navbar.navbar-inverse .navbar-nav .nav-tabs>li>a:hover,.navbar.navbar-inverse .navbar-nav .nav-pills>li>a:hover,.navbar-dark ul.nav.navbar-nav>li>a:hover,.navbar.navbar-inverse ul.nav.navbar-nav>li>a:hover,.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-tabs>li>a:focus,.navbar-dark .navbar-nav .nav-pills>li>a:focus,.navbar.navbar-inverse .navbar-nav .nav-link:focus,.navbar.navbar-inverse .navbar-nav .nav-tabs>li>a:focus,.navbar.navbar-inverse .navbar-nav .nav-pills>li>a:focus,.navbar-dark ul.nav.navbar-nav>li>a:focus,.navbar.navbar-inverse ul.nav.navbar-nav>li>a:focus{color:rgba(255,255,255,0.75)}.navbar-dark .navbar-nav .nav-link.disabled,.navbar-dark .navbar-nav .nav-tabs>li>a.disabled,.navbar-dark .navbar-nav .nav-pills>li>a.disabled,.navbar.navbar-inverse .navbar-nav .nav-link.disabled,.navbar.navbar-inverse .navbar-nav .nav-tabs>li>a.disabled,.navbar.navbar-inverse .navbar-nav .nav-pills>li>a.disabled,.navbar-dark ul.nav.navbar-nav>li>a.disabled,.navbar.navbar-inverse ul.nav.navbar-nav>li>a.disabled{color:rgba(255,255,255,0.25)}.navbar-dark .navbar-nav .show>.nav-link,.navbar-dark .navbar-nav .in>.nav-link,.navbar-dark .navbar-nav .nav-tabs>li.show>a,.navbar-dark .navbar-nav .nav-tabs>li.in>a,.navbar-dark .navbar-nav .nav-pills>li.show>a,.navbar-dark .navbar-nav .nav-pills>li.in>a,.navbar.navbar-inverse .navbar-nav .show>.nav-link,.navbar.navbar-inverse .navbar-nav .in>.nav-link,.navbar.navbar-inverse .navbar-nav .nav-tabs>li.show>a,.navbar.navbar-inverse .navbar-nav .nav-tabs>li.in>a,.navbar.navbar-inverse .navbar-nav .nav-pills>li.show>a,.navbar.navbar-inverse .navbar-nav .nav-pills>li.in>a,.navbar-dark ul.nav.navbar-nav>li.show>a,.navbar-dark ul.nav.navbar-nav>li.in>a,.navbar.navbar-inverse ul.nav.navbar-nav>li.show>a,.navbar.navbar-inverse ul.nav.navbar-nav>li.in>a,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-tabs>li.active>a,.navbar-dark .navbar-nav .nav-pills>li.active>a,.navbar.navbar-inverse .navbar-nav .active>.nav-link,.navbar.navbar-inverse .navbar-nav .nav-tabs>li.active>a,.navbar.navbar-inverse .navbar-nav .nav-pills>li.active>a,.navbar-dark ul.nav.navbar-nav>li.active>a,.navbar.navbar-inverse ul.nav.navbar-nav>li.active>a,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .nav-link.in,.navbar-dark .navbar-nav .nav-tabs>li>a.show,.navbar-dark .navbar-nav .nav-tabs>li>a.in,.navbar-dark .navbar-nav .nav-pills>li>a.show,.navbar-dark .navbar-nav .nav-pills>li>a.in,.navbar.navbar-inverse .navbar-nav .nav-link.show,.navbar.navbar-inverse .navbar-nav .nav-link.in,.navbar.navbar-inverse .navbar-nav .nav-tabs>li>a.show,.navbar.navbar-inverse .navbar-nav .nav-tabs>li>a.in,.navbar.navbar-inverse .navbar-nav .nav-pills>li>a.show,.navbar.navbar-inverse .navbar-nav .nav-pills>li>a.in,.navbar-dark ul.nav.navbar-nav>li>a.show,.navbar-dark ul.nav.navbar-nav>li>a.in,.navbar.navbar-inverse ul.nav.navbar-nav>li>a.show,.navbar.navbar-inverse ul.nav.navbar-nav>li>a.in,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-tabs>li>a.active,.navbar-dark .navbar-nav .nav-pills>li>a.active,.navbar.navbar-inverse .navbar-nav .nav-link.active,.navbar.navbar-inverse .navbar-nav .nav-tabs>li>a.active,.navbar.navbar-inverse .navbar-nav .nav-pills>li>a.active,.navbar-dark ul.nav.navbar-nav>li>a.active,.navbar.navbar-inverse ul.nav.navbar-nav>li>a.active{color:#fff}.navbar-dark .navbar-toggler,.navbar-dark .navbar-toggle,.navbar.navbar-inverse .navbar-toggler,.navbar.navbar-inverse .navbar-toggle{color:rgba(255,255,255,0.5);border-color:rgba(255,255,255,0.1)}.navbar-dark .navbar-toggler-icon,.navbar-dark .navbar-toggle>.icon-bar:last-child,.navbar.navbar-inverse .navbar-toggler-icon,.navbar.navbar-inverse .navbar-toggle>.icon-bar:last-child{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255,255,255,0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text,.navbar.navbar-inverse .navbar-text{color:rgba(255,255,255,0.5)}.navbar-dark .navbar-text a,.navbar.navbar-inverse .navbar-text a{color:#fff}.navbar-dark .navbar-text a:hover,.navbar.navbar-inverse .navbar-text a:hover,.navbar-dark .navbar-text a:focus,.navbar.navbar-inverse .navbar-text a:focus{color:#fff}.card,.well{position:relative;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,0.125);border-radius:.25rem}.card>hr,.well>hr{margin-right:0;margin-left:0}.card>.list-group,.well>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child,.well>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child,.well>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.well>.card-header+.list-group,.card>.list-group+.card-footer,.well>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;-webkit-flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,0.03);border-bottom:1px solid rgba(0,0,0,0.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,0.03);border-top:1px solid rgba(0,0,0,0.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-top,.card-img-bottom{flex-shrink:0;-webkit-flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card,.card-deck .well{margin-bottom:15px}@media (min-width: 576px){.card-deck{display:flex;display:-webkit-flex;flex-flow:row wrap;-webkit-flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card,.card-deck .well{flex:1 0 0%;-webkit-flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card,.card-group>.well{margin-bottom:15px}@media (min-width: 576px){.card-group{display:flex;display:-webkit-flex;flex-flow:row wrap;-webkit-flex-flow:row wrap}.card-group>.card,.card-group>.well{flex:1 0 0%;-webkit-flex:1 0 0%;margin-bottom:0}.card-group>.card+.card,.card-group>.well+.card,.card-group>.card+.well,.card-group>.well+.well{margin-left:0;border-left:0}.card-group>.card:not(:last-child),.card-group>.well:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-img-top,.card-group>.well:not(:last-child) .card-img-top,.card-group>.card:not(:last-child) .card-header,.card-group>.well:not(:last-child) .card-header{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-img-bottom,.card-group>.well:not(:last-child) .card-img-bottom,.card-group>.card:not(:last-child) .card-footer,.card-group>.well:not(:last-child) .card-footer{border-bottom-right-radius:0}.card-group>.card:not(:first-child),.card-group>.well:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-img-top,.card-group>.well:not(:first-child) .card-img-top,.card-group>.card:not(:first-child) .card-header,.card-group>.well:not(:first-child) .card-header{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-img-bottom,.card-group>.well:not(:first-child) .card-img-bottom,.card-group>.card:not(:first-child) .card-footer,.card-group>.well:not(:first-child) .card-footer{border-bottom-left-radius:0}}.card-columns .card,.card-columns .well{margin-bottom:.75rem}@media (min-width: 576px){.card-columns{column-count:3;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card,.card-columns .well{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card,.accordion>.well{overflow:hidden}.accordion>.card:not(:last-of-type),.accordion>.well:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type),.accordion>.well:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header,.accordion>.well>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;display:-webkit-flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#096B72;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#03282b;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(9,107,114,0.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#096B72;border-color:#096B72}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.badge{transition:none}}a.badge:hover,a.badge:focus{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-default{color:#000;background-color:#dee2e6}a.badge-default:hover,a.badge-default:focus{color:#000;background-color:#c1c9d0}a.badge-default:focus,a.badge-default.focus{outline:0;box-shadow:0 0 0 .2rem rgba(222,226,230,0.5)}.badge-primary{color:#fff;background-color:#096B72}a.badge-primary:hover,a.badge-primary:focus{color:#fff;background-color:#053f43}a.badge-primary:focus,a.badge-primary.focus{outline:0;box-shadow:0 0 0 .2rem rgba(9,107,114,0.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:hover,a.badge-secondary:focus{color:#fff;background-color:#545b62}a.badge-secondary:focus,a.badge-secondary.focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,0.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:hover,a.badge-success:focus{color:#fff;background-color:#1e7e34}a.badge-success:focus,a.badge-success.focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,0.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:hover,a.badge-info:focus{color:#fff;background-color:#117a8b}a.badge-info:focus,a.badge-info.focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,0.5)}.badge-warning{color:#000;background-color:#ffc107}a.badge-warning:hover,a.badge-warning:focus{color:#000;background-color:#d39e00}a.badge-warning:focus,a.badge-warning.focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,0.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:hover,a.badge-danger:focus{color:#fff;background-color:#bd2130}a.badge-danger:focus,a.badge-danger.focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,0.5)}.badge-light{color:#000;background-color:#f8f9fa}a.badge-light:hover,a.badge-light:focus{color:#000;background-color:#dae0e5}a.badge-light:focus,a.badge-light.focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,0.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:hover,a.badge-dark:focus{color:#fff;background-color:#1d2124}a.badge-dark:focus,a.badge-dark.focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,0.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width: 576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-default{color:#737678;background-color:#f8f9fa;border-color:#f6f7f8}.alert-default hr{border-top-color:#e8eaed}.alert-default .alert-link{color:#5a5c5e}.alert-primary{color:#05383b;background-color:#cee1e3;border-color:#bad6d8}.alert-primary hr{border-top-color:#aacccf}.alert-primary .alert-link{color:#010b0c}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:flex;display:-webkit-flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;justify-content:center;-webkit-justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#096B72;transition:width 0.6s ease}@media (prefers-reduced-motion: reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:1rem 1rem}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion: reduce){.progress-bar-animated{animation:none}}.media{display:flex;display:-webkit-flex;align-items:flex-start;-webkit-align-items:flex-start}.media-body{flex:1;-webkit-flex:1}.list-group{display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,0.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#096B72;border-color:#096B72}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width: 576px){.list-group-horizontal-sm{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width: 768px){.list-group-horizontal-md{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width: 992px){.list-group-horizontal-lg{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width: 1200px){.list-group-horizontal-xl{flex-direction:row;-webkit-flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-default{color:#737678;background-color:#f6f7f8}.list-group-item-default.list-group-item-action:hover,.list-group-item-default.list-group-item-action:focus{color:#737678;background-color:#e8eaed}.list-group-item-default.list-group-item-action.active{color:#fff;background-color:#737678;border-color:#737678}.list-group-item-primary{color:#05383b;background-color:#bad6d8}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#05383b;background-color:#aacccf}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#05383b;border-color:#05383b}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):hover,.close:not(:disabled):not(.disabled):focus{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{flex-basis:350px;-webkit-flex-basis:350px;max-width:350px;font-size:.875rem;background-color:rgba(255,255,255,0.85);background-clip:padding-box;border:1px solid rgba(0,0,0,0.1);box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show,.toast.in{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,0.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,0.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform 0.3s ease-out;transform:translate(0, -50px)}@media (prefers-reduced-motion: reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog,.modal.in .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{display:flex;display:-webkit-flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-header,.modal-dialog-scrollable .modal-footer{flex-shrink:0;-webkit-flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:min-content;height:-webkit-min-content;height:-moz-min-content;height:-ms-min-content;height:-o-min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{flex-direction:column;-webkit-flex-direction:column;justify-content:center;-webkit-justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:flex;display:-webkit-flex;flex-direction:column;-webkit-flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show,.modal-backdrop.in{opacity:.5}.modal-header{display:flex;display:-webkit-flex;align-items:flex-start;-webkit-align-items:flex-start;justify-content:space-between;-webkit-justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;-webkit-flex:1 1 auto;padding:1rem}.modal-footer{display:flex;display:-webkit-flex;flex-wrap:wrap;-webkit-flex-wrap:wrap;align-items:center;-webkit-align-items:center;justify-content:flex-end;-webkit-justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width: 576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:min-content;height:-webkit-min-content;height:-moz-min-content;height:-ms-min-content;height:-o-min-content}.modal-sm{max-width:300px}}@media (min-width: 992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width: 1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:Roboto;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show,.tooltip.in{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top,.bs-tooltip-auto[x-placement^="top"]{padding:.4rem 0}.bs-tooltip-top .arrow,.bs-tooltip-auto[x-placement^="top"] .arrow{bottom:0}.bs-tooltip-top .arrow::before,.bs-tooltip-auto[x-placement^="top"] .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-right,.bs-tooltip-auto[x-placement^="right"]{padding:0 .4rem}.bs-tooltip-right .arrow,.bs-tooltip-auto[x-placement^="right"] .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-right .arrow::before,.bs-tooltip-auto[x-placement^="right"] .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-bottom,.bs-tooltip-auto[x-placement^="bottom"]{padding:.4rem 0}.bs-tooltip-bottom .arrow,.bs-tooltip-auto[x-placement^="bottom"] .arrow{top:0}.bs-tooltip-bottom .arrow::before,.bs-tooltip-auto[x-placement^="bottom"] .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-left,.bs-tooltip-auto[x-placement^="left"]{padding:0 .4rem}.bs-tooltip-left .arrow,.bs-tooltip-auto[x-placement^="left"] .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-left .arrow::before,.bs-tooltip-auto[x-placement^="left"] .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:Roboto;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::before,.popover .arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-top,.bs-popover-auto[x-placement^="top"]{margin-bottom:.5rem}.bs-popover-top>.arrow,.bs-popover-auto[x-placement^="top"]>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-top>.arrow::before,.bs-popover-auto[x-placement^="top"]>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,0.25)}.bs-popover-top>.arrow::after,.bs-popover-auto[x-placement^="top"]>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-right,.bs-popover-auto[x-placement^="right"]{margin-left:.5rem}.bs-popover-right>.arrow,.bs-popover-auto[x-placement^="right"]>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-right>.arrow::before,.bs-popover-auto[x-placement^="right"]>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,0.25)}.bs-popover-right>.arrow::after,.bs-popover-auto[x-placement^="right"]>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-bottom,.bs-popover-auto[x-placement^="bottom"]{margin-top:.5rem}.bs-popover-bottom>.arrow,.bs-popover-auto[x-placement^="bottom"]>.arrow{top:calc(-.5rem - 1px)}.bs-popover-bottom>.arrow::before,.bs-popover-auto[x-placement^="bottom"]>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,0.25)}.bs-popover-bottom>.arrow::after,.bs-popover-auto[x-placement^="bottom"]>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-bottom .popover-header::before,.bs-popover-auto[x-placement^="bottom"] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-left,.bs-popover-auto[x-placement^="left"]{margin-right:.5rem}.bs-popover-left>.arrow,.bs-popover-auto[x-placement^="left"]>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-left>.arrow::before,.bs-popover-auto[x-placement^="left"]>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,0.25)}.bs-popover-left>.arrow::after,.bs-popover-auto[x-placement^="left"]>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y;-webkit-touch-action:pan-y;-moz-touch-action:pan-y;-ms-touch-action:pan-y;-o-touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;-o-backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion: reduce){.carousel-item{transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-left),.active.carousel-item-right{transform:translateX(100%)}.carousel-item-prev:not(.carousel-item-right),.active.carousel-item-left{transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion: reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:flex;display:-webkit-flex;align-items:center;-webkit-align-items:center;justify-content:center;-webkit-justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity 0.15s ease}@media (prefers-reduced-motion: reduce){.carousel-control-prev,.carousel-control-next{transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:20px;height:20px;background:50% / 100% 100% no-repeat}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:flex;display:-webkit-flex;justify-content:center;-webkit-justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;flex:0 1 auto;-webkit-flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity 0.6s ease}@media (prefers-reduced-motion: reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion: reduce){.spinner-border,.spinner-grow{animation-duration:1.5s;-webkit-animation-duration:1.5s;-moz-animation-duration:1.5s;-ms-animation-duration:1.5s;-o-animation-duration:1.5s}}.align-baseline{vertical-align:baseline !important}.align-top{vertical-align:top !important}.align-middle{vertical-align:middle !important}.align-bottom{vertical-align:bottom !important}.align-text-bottom{vertical-align:text-bottom !important}.align-text-top{vertical-align:text-top !important}.bg-default{background-color:#dee2e6 !important;color:#000}a.bg-default:hover,a.bg-default:focus,button.bg-default:hover,button.bg-default:focus{background-color:#c1c9d0 !important}.bg-primary{background-color:#096B72 !important;color:#fff}a.bg-primary:hover,a.bg-primary:focus,button.bg-primary:hover,button.bg-primary:focus{background-color:#053f43 !important}.bg-secondary{background-color:#6c757d !important;color:#fff}a.bg-secondary:hover,a.bg-secondary:focus,button.bg-secondary:hover,button.bg-secondary:focus{background-color:#545b62 !important}.bg-success{background-color:#28a745 !important;color:#fff}a.bg-success:hover,a.bg-success:focus,button.bg-success:hover,button.bg-success:focus{background-color:#1e7e34 !important}.bg-info{background-color:#17a2b8 !important;color:#fff}a.bg-info:hover,a.bg-info:focus,button.bg-info:hover,button.bg-info:focus{background-color:#117a8b !important}.bg-warning{background-color:#ffc107 !important;color:#000}a.bg-warning:hover,a.bg-warning:focus,button.bg-warning:hover,button.bg-warning:focus{background-color:#d39e00 !important}.bg-danger{background-color:#dc3545 !important;color:#fff}a.bg-danger:hover,a.bg-danger:focus,button.bg-danger:hover,button.bg-danger:focus{background-color:#bd2130 !important}.bg-light{background-color:#f8f9fa !important;color:#000}a.bg-light:hover,a.bg-light:focus,button.bg-light:hover,button.bg-light:focus{background-color:#dae0e5 !important}.bg-dark{background-color:#343a40 !important;color:#fff}a.bg-dark:hover,a.bg-dark:focus,button.bg-dark:hover,button.bg-dark:focus{background-color:#1d2124 !important}.bg-white{background-color:#fff !important;color:#000}.bg-transparent{background-color:transparent !important}.border{border:1px solid #dee2e6 !important}.border-top{border-top:1px solid #dee2e6 !important}.border-right{border-right:1px solid #dee2e6 !important}.border-bottom{border-bottom:1px solid #dee2e6 !important}.border-left{border-left:1px solid #dee2e6 !important}.border-0{border:0 !important}.border-top-0{border-top:0 !important}.border-right-0{border-right:0 !important}.border-bottom-0{border-bottom:0 !important}.border-left-0{border-left:0 !important}.border-default{border-color:#dee2e6 !important}.border-primary{border-color:#096B72 !important}.border-secondary{border-color:#6c757d !important}.border-success{border-color:#28a745 !important}.border-info{border-color:#17a2b8 !important}.border-warning{border-color:#ffc107 !important}.border-danger{border-color:#dc3545 !important}.border-light{border-color:#f8f9fa !important}.border-dark{border-color:#343a40 !important}.border-white{border-color:#fff !important}.rounded-sm{border-radius:.2rem !important}.rounded{border-radius:.25rem !important}.rounded-top{border-top-left-radius:.25rem !important;border-top-right-radius:.25rem !important}.rounded-right{border-top-right-radius:.25rem !important;border-bottom-right-radius:.25rem !important}.rounded-bottom{border-bottom-right-radius:.25rem !important;border-bottom-left-radius:.25rem !important}.rounded-left{border-top-left-radius:.25rem !important;border-bottom-left-radius:.25rem !important}.rounded-lg{border-radius:.3rem !important}.rounded-circle{border-radius:50% !important}.rounded-pill{border-radius:50rem !important}.rounded-0{border-radius:0 !important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none !important}.d-inline{display:inline !important}.d-inline-block{display:inline-block !important}.d-block{display:block !important}.d-table{display:table !important}.d-table-row{display:table-row !important}.d-table-cell{display:table-cell !important}.d-flex{display:flex !important}.d-inline-flex{display:inline-flex !important}@media (min-width: 576px){.d-sm-none{display:none !important}.d-sm-inline{display:inline !important}.d-sm-inline-block{display:inline-block !important}.d-sm-block{display:block !important}.d-sm-table{display:table !important}.d-sm-table-row{display:table-row !important}.d-sm-table-cell{display:table-cell !important}.d-sm-flex{display:flex !important}.d-sm-inline-flex{display:inline-flex !important}}@media (min-width: 768px){.d-md-none{display:none !important}.d-md-inline{display:inline !important}.d-md-inline-block{display:inline-block !important}.d-md-block{display:block !important}.d-md-table{display:table !important}.d-md-table-row{display:table-row !important}.d-md-table-cell{display:table-cell !important}.d-md-flex{display:flex !important}.d-md-inline-flex{display:inline-flex !important}}@media (min-width: 992px){.d-lg-none{display:none !important}.d-lg-inline{display:inline !important}.d-lg-inline-block{display:inline-block !important}.d-lg-block{display:block !important}.d-lg-table{display:table !important}.d-lg-table-row{display:table-row !important}.d-lg-table-cell{display:table-cell !important}.d-lg-flex{display:flex !important}.d-lg-inline-flex{display:inline-flex !important}}@media (min-width: 1200px){.d-xl-none{display:none !important}.d-xl-inline{display:inline !important}.d-xl-inline-block{display:inline-block !important}.d-xl-block{display:block !important}.d-xl-table{display:table !important}.d-xl-table-row{display:table-row !important}.d-xl-table-cell{display:table-cell !important}.d-xl-flex{display:flex !important}.d-xl-inline-flex{display:inline-flex !important}}@media print{.d-print-none{display:none !important}.d-print-inline{display:inline !important}.d-print-inline-block{display:inline-block !important}.d-print-block{display:block !important}.d-print-table{display:table !important}.d-print-table-row{display:table-row !important}.d-print-table-cell{display:table-cell !important}.d-print-flex{display:flex !important}.d-print-inline-flex{display:inline-flex !important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.85714%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{flex-direction:row !important}.flex-column{flex-direction:column !important}.flex-row-reverse{flex-direction:row-reverse !important}.flex-column-reverse{flex-direction:column-reverse !important}.flex-wrap{flex-wrap:wrap !important}.flex-nowrap{flex-wrap:nowrap !important}.flex-wrap-reverse{flex-wrap:wrap-reverse !important}.flex-fill{flex:1 1 auto !important}.flex-grow-0{flex-grow:0 !important}.flex-grow-1{flex-grow:1 !important}.flex-shrink-0{flex-shrink:0 !important}.flex-shrink-1{flex-shrink:1 !important}.justify-content-start{justify-content:flex-start !important}.justify-content-end{justify-content:flex-end !important}.justify-content-center{justify-content:center !important}.justify-content-between{justify-content:space-between !important}.justify-content-around{justify-content:space-around !important}.align-items-start{align-items:flex-start !important}.align-items-end{align-items:flex-end !important}.align-items-center{align-items:center !important}.align-items-baseline{align-items:baseline !important}.align-items-stretch{align-items:stretch !important}.align-content-start{align-content:flex-start !important}.align-content-end{align-content:flex-end !important}.align-content-center{align-content:center !important}.align-content-between{align-content:space-between !important}.align-content-around{align-content:space-around !important}.align-content-stretch{align-content:stretch !important}.align-self-auto{align-self:auto !important}.align-self-start{align-self:flex-start !important}.align-self-end{align-self:flex-end !important}.align-self-center{align-self:center !important}.align-self-baseline{align-self:baseline !important}.align-self-stretch{align-self:stretch !important}@media (min-width: 576px){.flex-sm-row{flex-direction:row !important}.flex-sm-column{flex-direction:column !important}.flex-sm-row-reverse{flex-direction:row-reverse !important}.flex-sm-column-reverse{flex-direction:column-reverse !important}.flex-sm-wrap{flex-wrap:wrap !important}.flex-sm-nowrap{flex-wrap:nowrap !important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse !important}.flex-sm-fill{flex:1 1 auto !important}.flex-sm-grow-0{flex-grow:0 !important}.flex-sm-grow-1{flex-grow:1 !important}.flex-sm-shrink-0{flex-shrink:0 !important}.flex-sm-shrink-1{flex-shrink:1 !important}.justify-content-sm-start{justify-content:flex-start !important}.justify-content-sm-end{justify-content:flex-end !important}.justify-content-sm-center{justify-content:center !important}.justify-content-sm-between{justify-content:space-between !important}.justify-content-sm-around{justify-content:space-around !important}.align-items-sm-start{align-items:flex-start !important}.align-items-sm-end{align-items:flex-end !important}.align-items-sm-center{align-items:center !important}.align-items-sm-baseline{align-items:baseline !important}.align-items-sm-stretch{align-items:stretch !important}.align-content-sm-start{align-content:flex-start !important}.align-content-sm-end{align-content:flex-end !important}.align-content-sm-center{align-content:center !important}.align-content-sm-between{align-content:space-between !important}.align-content-sm-around{align-content:space-around !important}.align-content-sm-stretch{align-content:stretch !important}.align-self-sm-auto{align-self:auto !important}.align-self-sm-start{align-self:flex-start !important}.align-self-sm-end{align-self:flex-end !important}.align-self-sm-center{align-self:center !important}.align-self-sm-baseline{align-self:baseline !important}.align-self-sm-stretch{align-self:stretch !important}}@media (min-width: 768px){.flex-md-row{flex-direction:row !important}.flex-md-column{flex-direction:column !important}.flex-md-row-reverse{flex-direction:row-reverse !important}.flex-md-column-reverse{flex-direction:column-reverse !important}.flex-md-wrap{flex-wrap:wrap !important}.flex-md-nowrap{flex-wrap:nowrap !important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse !important}.flex-md-fill{flex:1 1 auto !important}.flex-md-grow-0{flex-grow:0 !important}.flex-md-grow-1{flex-grow:1 !important}.flex-md-shrink-0{flex-shrink:0 !important}.flex-md-shrink-1{flex-shrink:1 !important}.justify-content-md-start{justify-content:flex-start !important}.justify-content-md-end{justify-content:flex-end !important}.justify-content-md-center{justify-content:center !important}.justify-content-md-between{justify-content:space-between !important}.justify-content-md-around{justify-content:space-around !important}.align-items-md-start{align-items:flex-start !important}.align-items-md-end{align-items:flex-end !important}.align-items-md-center{align-items:center !important}.align-items-md-baseline{align-items:baseline !important}.align-items-md-stretch{align-items:stretch !important}.align-content-md-start{align-content:flex-start !important}.align-content-md-end{align-content:flex-end !important}.align-content-md-center{align-content:center !important}.align-content-md-between{align-content:space-between !important}.align-content-md-around{align-content:space-around !important}.align-content-md-stretch{align-content:stretch !important}.align-self-md-auto{align-self:auto !important}.align-self-md-start{align-self:flex-start !important}.align-self-md-end{align-self:flex-end !important}.align-self-md-center{align-self:center !important}.align-self-md-baseline{align-self:baseline !important}.align-self-md-stretch{align-self:stretch !important}}@media (min-width: 992px){.flex-lg-row{flex-direction:row !important}.flex-lg-column{flex-direction:column !important}.flex-lg-row-reverse{flex-direction:row-reverse !important}.flex-lg-column-reverse{flex-direction:column-reverse !important}.flex-lg-wrap{flex-wrap:wrap !important}.flex-lg-nowrap{flex-wrap:nowrap !important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse !important}.flex-lg-fill{flex:1 1 auto !important}.flex-lg-grow-0{flex-grow:0 !important}.flex-lg-grow-1{flex-grow:1 !important}.flex-lg-shrink-0{flex-shrink:0 !important}.flex-lg-shrink-1{flex-shrink:1 !important}.justify-content-lg-start{justify-content:flex-start !important}.justify-content-lg-end{justify-content:flex-end !important}.justify-content-lg-center{justify-content:center !important}.justify-content-lg-between{justify-content:space-between !important}.justify-content-lg-around{justify-content:space-around !important}.align-items-lg-start{align-items:flex-start !important}.align-items-lg-end{align-items:flex-end !important}.align-items-lg-center{align-items:center !important}.align-items-lg-baseline{align-items:baseline !important}.align-items-lg-stretch{align-items:stretch !important}.align-content-lg-start{align-content:flex-start !important}.align-content-lg-end{align-content:flex-end !important}.align-content-lg-center{align-content:center !important}.align-content-lg-between{align-content:space-between !important}.align-content-lg-around{align-content:space-around !important}.align-content-lg-stretch{align-content:stretch !important}.align-self-lg-auto{align-self:auto !important}.align-self-lg-start{align-self:flex-start !important}.align-self-lg-end{align-self:flex-end !important}.align-self-lg-center{align-self:center !important}.align-self-lg-baseline{align-self:baseline !important}.align-self-lg-stretch{align-self:stretch !important}}@media (min-width: 1200px){.flex-xl-row{flex-direction:row !important}.flex-xl-column{flex-direction:column !important}.flex-xl-row-reverse{flex-direction:row-reverse !important}.flex-xl-column-reverse{flex-direction:column-reverse !important}.flex-xl-wrap{flex-wrap:wrap !important}.flex-xl-nowrap{flex-wrap:nowrap !important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse !important}.flex-xl-fill{flex:1 1 auto !important}.flex-xl-grow-0{flex-grow:0 !important}.flex-xl-grow-1{flex-grow:1 !important}.flex-xl-shrink-0{flex-shrink:0 !important}.flex-xl-shrink-1{flex-shrink:1 !important}.justify-content-xl-start{justify-content:flex-start !important}.justify-content-xl-end{justify-content:flex-end !important}.justify-content-xl-center{justify-content:center !important}.justify-content-xl-between{justify-content:space-between !important}.justify-content-xl-around{justify-content:space-around !important}.align-items-xl-start{align-items:flex-start !important}.align-items-xl-end{align-items:flex-end !important}.align-items-xl-center{align-items:center !important}.align-items-xl-baseline{align-items:baseline !important}.align-items-xl-stretch{align-items:stretch !important}.align-content-xl-start{align-content:flex-start !important}.align-content-xl-end{align-content:flex-end !important}.align-content-xl-center{align-content:center !important}.align-content-xl-between{align-content:space-between !important}.align-content-xl-around{align-content:space-around !important}.align-content-xl-stretch{align-content:stretch !important}.align-self-xl-auto{align-self:auto !important}.align-self-xl-start{align-self:flex-start !important}.align-self-xl-end{align-self:flex-end !important}.align-self-xl-center{align-self:center !important}.align-self-xl-baseline{align-self:baseline !important}.align-self-xl-stretch{align-self:stretch !important}}.float-left{float:left !important}.float-right{float:right !important}.float-none{float:none !important}@media (min-width: 576px){.float-sm-left{float:left !important}.float-sm-right{float:right !important}.float-sm-none{float:none !important}}@media (min-width: 768px){.float-md-left{float:left !important}.float-md-right{float:right !important}.float-md-none{float:none !important}}@media (min-width: 992px){.float-lg-left{float:left !important}.float-lg-right{float:right !important}.float-lg-none{float:none !important}}@media (min-width: 1200px){.float-xl-left{float:left !important}.float-xl-right{float:right !important}.float-xl-none{float:none !important}}.user-select-all{user-select:all !important}.user-select-auto{user-select:auto !important}.user-select-none{user-select:none !important}.overflow-auto{overflow:auto !important}.overflow-hidden{overflow:hidden !important}.position-static{position:static !important}.position-relative{position:relative !important}.position-absolute{position:absolute !important}.position-fixed{position:fixed !important}.position-sticky{position:sticky !important}.fixed-top,.navbar-fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom,.navbar-fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports (position: sticky){.sticky-top,.navbar-sticky-top{position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important}.shadow{box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important}.shadow-none{box-shadow:none !important}.w-25{width:25% !important}.w-50{width:50% !important}.w-75{width:75% !important}.w-100{width:100% !important}.w-auto{width:auto !important}.h-25{height:25% !important}.h-50{height:50% !important}.h-75{height:75% !important}.h-100{height:100% !important}.h-auto{height:auto !important}.mw-100{max-width:100% !important}.mh-100{max-height:100% !important}.min-vw-100{min-width:100vw !important}.min-vh-100{min-height:100vh !important}.vw-100{width:100vw !important}.vh-100{height:100vh !important}.m-0{margin:0 !important}.mt-0,.my-0{margin-top:0 !important}.mr-0,.mx-0{margin-right:0 !important}.mb-0,.my-0{margin-bottom:0 !important}.ml-0,.mx-0{margin-left:0 !important}.m-1{margin:.25rem !important}.mt-1,.my-1{margin-top:.25rem !important}.mr-1,.mx-1{margin-right:.25rem !important}.mb-1,.my-1{margin-bottom:.25rem !important}.ml-1,.mx-1{margin-left:.25rem !important}.m-2{margin:.5rem !important}.mt-2,.my-2{margin-top:.5rem !important}.mr-2,.mx-2{margin-right:.5rem !important}.mb-2,.my-2{margin-bottom:.5rem !important}.ml-2,.mx-2{margin-left:.5rem !important}.m-3{margin:1rem !important}.mt-3,.my-3{margin-top:1rem !important}.mr-3,.mx-3{margin-right:1rem !important}.mb-3,.my-3{margin-bottom:1rem !important}.ml-3,.mx-3{margin-left:1rem !important}.m-4{margin:1.5rem !important}.mt-4,.my-4{margin-top:1.5rem !important}.mr-4,.mx-4{margin-right:1.5rem !important}.mb-4,.my-4{margin-bottom:1.5rem !important}.ml-4,.mx-4{margin-left:1.5rem !important}.m-5{margin:3rem !important}.mt-5,.my-5{margin-top:3rem !important}.mr-5,.mx-5{margin-right:3rem !important}.mb-5,.my-5{margin-bottom:3rem !important}.ml-5,.mx-5{margin-left:3rem !important}.p-0{padding:0 !important}.pt-0,.py-0{padding-top:0 !important}.pr-0,.px-0{padding-right:0 !important}.pb-0,.py-0{padding-bottom:0 !important}.pl-0,.px-0{padding-left:0 !important}.p-1{padding:.25rem !important}.pt-1,.py-1{padding-top:.25rem !important}.pr-1,.px-1{padding-right:.25rem !important}.pb-1,.py-1{padding-bottom:.25rem !important}.pl-1,.px-1{padding-left:.25rem !important}.p-2{padding:.5rem !important}.pt-2,.py-2{padding-top:.5rem !important}.pr-2,.px-2{padding-right:.5rem !important}.pb-2,.py-2{padding-bottom:.5rem !important}.pl-2,.px-2{padding-left:.5rem !important}.p-3{padding:1rem !important}.pt-3,.py-3{padding-top:1rem !important}.pr-3,.px-3{padding-right:1rem !important}.pb-3,.py-3{padding-bottom:1rem !important}.pl-3,.px-3{padding-left:1rem !important}.p-4{padding:1.5rem !important}.pt-4,.py-4{padding-top:1.5rem !important}.pr-4,.px-4{padding-right:1.5rem !important}.pb-4,.py-4{padding-bottom:1.5rem !important}.pl-4,.px-4{padding-left:1.5rem !important}.p-5{padding:3rem !important}.pt-5,.py-5{padding-top:3rem !important}.pr-5,.px-5{padding-right:3rem !important}.pb-5,.py-5{padding-bottom:3rem !important}.pl-5,.px-5{padding-left:3rem !important}.m-n1{margin:-.25rem !important}.mt-n1,.my-n1{margin-top:-.25rem !important}.mr-n1,.mx-n1{margin-right:-.25rem !important}.mb-n1,.my-n1{margin-bottom:-.25rem !important}.ml-n1,.mx-n1{margin-left:-.25rem !important}.m-n2{margin:-.5rem !important}.mt-n2,.my-n2{margin-top:-.5rem !important}.mr-n2,.mx-n2{margin-right:-.5rem !important}.mb-n2,.my-n2{margin-bottom:-.5rem !important}.ml-n2,.mx-n2{margin-left:-.5rem !important}.m-n3{margin:-1rem !important}.mt-n3,.my-n3{margin-top:-1rem !important}.mr-n3,.mx-n3{margin-right:-1rem !important}.mb-n3,.my-n3{margin-bottom:-1rem !important}.ml-n3,.mx-n3{margin-left:-1rem !important}.m-n4{margin:-1.5rem !important}.mt-n4,.my-n4{margin-top:-1.5rem !important}.mr-n4,.mx-n4{margin-right:-1.5rem !important}.mb-n4,.my-n4{margin-bottom:-1.5rem !important}.ml-n4,.mx-n4{margin-left:-1.5rem !important}.m-n5{margin:-3rem !important}.mt-n5,.my-n5{margin-top:-3rem !important}.mr-n5,.mx-n5{margin-right:-3rem !important}.mb-n5,.my-n5{margin-bottom:-3rem !important}.ml-n5,.mx-n5{margin-left:-3rem !important}.m-auto{margin:auto !important}.mt-auto,.my-auto{margin-top:auto !important}.mr-auto,.mx-auto{margin-right:auto !important}.mb-auto,.my-auto{margin-bottom:auto !important}.ml-auto,.mx-auto{margin-left:auto !important}@media (min-width: 576px){.m-sm-0{margin:0 !important}.mt-sm-0,.my-sm-0{margin-top:0 !important}.mr-sm-0,.mx-sm-0{margin-right:0 !important}.mb-sm-0,.my-sm-0{margin-bottom:0 !important}.ml-sm-0,.mx-sm-0{margin-left:0 !important}.m-sm-1{margin:.25rem !important}.mt-sm-1,.my-sm-1{margin-top:.25rem !important}.mr-sm-1,.mx-sm-1{margin-right:.25rem !important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem !important}.ml-sm-1,.mx-sm-1{margin-left:.25rem !important}.m-sm-2{margin:.5rem !important}.mt-sm-2,.my-sm-2{margin-top:.5rem !important}.mr-sm-2,.mx-sm-2{margin-right:.5rem !important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem !important}.ml-sm-2,.mx-sm-2{margin-left:.5rem !important}.m-sm-3{margin:1rem !important}.mt-sm-3,.my-sm-3{margin-top:1rem !important}.mr-sm-3,.mx-sm-3{margin-right:1rem !important}.mb-sm-3,.my-sm-3{margin-bottom:1rem !important}.ml-sm-3,.mx-sm-3{margin-left:1rem !important}.m-sm-4{margin:1.5rem !important}.mt-sm-4,.my-sm-4{margin-top:1.5rem !important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem !important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem !important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem !important}.m-sm-5{margin:3rem !important}.mt-sm-5,.my-sm-5{margin-top:3rem !important}.mr-sm-5,.mx-sm-5{margin-right:3rem !important}.mb-sm-5,.my-sm-5{margin-bottom:3rem !important}.ml-sm-5,.mx-sm-5{margin-left:3rem !important}.p-sm-0{padding:0 !important}.pt-sm-0,.py-sm-0{padding-top:0 !important}.pr-sm-0,.px-sm-0{padding-right:0 !important}.pb-sm-0,.py-sm-0{padding-bottom:0 !important}.pl-sm-0,.px-sm-0{padding-left:0 !important}.p-sm-1{padding:.25rem !important}.pt-sm-1,.py-sm-1{padding-top:.25rem !important}.pr-sm-1,.px-sm-1{padding-right:.25rem !important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem !important}.pl-sm-1,.px-sm-1{padding-left:.25rem !important}.p-sm-2{padding:.5rem !important}.pt-sm-2,.py-sm-2{padding-top:.5rem !important}.pr-sm-2,.px-sm-2{padding-right:.5rem !important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem !important}.pl-sm-2,.px-sm-2{padding-left:.5rem !important}.p-sm-3{padding:1rem !important}.pt-sm-3,.py-sm-3{padding-top:1rem !important}.pr-sm-3,.px-sm-3{padding-right:1rem !important}.pb-sm-3,.py-sm-3{padding-bottom:1rem !important}.pl-sm-3,.px-sm-3{padding-left:1rem !important}.p-sm-4{padding:1.5rem !important}.pt-sm-4,.py-sm-4{padding-top:1.5rem !important}.pr-sm-4,.px-sm-4{padding-right:1.5rem !important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem !important}.pl-sm-4,.px-sm-4{padding-left:1.5rem !important}.p-sm-5{padding:3rem !important}.pt-sm-5,.py-sm-5{padding-top:3rem !important}.pr-sm-5,.px-sm-5{padding-right:3rem !important}.pb-sm-5,.py-sm-5{padding-bottom:3rem !important}.pl-sm-5,.px-sm-5{padding-left:3rem !important}.m-sm-n1{margin:-.25rem !important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem !important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem !important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem !important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem !important}.m-sm-n2{margin:-.5rem !important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem !important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem !important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem !important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem !important}.m-sm-n3{margin:-1rem !important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem !important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem !important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem !important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem !important}.m-sm-n4{margin:-1.5rem !important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem !important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem !important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem !important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem !important}.m-sm-n5{margin:-3rem !important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem !important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem !important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem !important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem !important}.m-sm-auto{margin:auto !important}.mt-sm-auto,.my-sm-auto{margin-top:auto !important}.mr-sm-auto,.mx-sm-auto{margin-right:auto !important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto !important}.ml-sm-auto,.mx-sm-auto{margin-left:auto !important}}@media (min-width: 768px){.m-md-0{margin:0 !important}.mt-md-0,.my-md-0{margin-top:0 !important}.mr-md-0,.mx-md-0{margin-right:0 !important}.mb-md-0,.my-md-0{margin-bottom:0 !important}.ml-md-0,.mx-md-0{margin-left:0 !important}.m-md-1{margin:.25rem !important}.mt-md-1,.my-md-1{margin-top:.25rem !important}.mr-md-1,.mx-md-1{margin-right:.25rem !important}.mb-md-1,.my-md-1{margin-bottom:.25rem !important}.ml-md-1,.mx-md-1{margin-left:.25rem !important}.m-md-2{margin:.5rem !important}.mt-md-2,.my-md-2{margin-top:.5rem !important}.mr-md-2,.mx-md-2{margin-right:.5rem !important}.mb-md-2,.my-md-2{margin-bottom:.5rem !important}.ml-md-2,.mx-md-2{margin-left:.5rem !important}.m-md-3{margin:1rem !important}.mt-md-3,.my-md-3{margin-top:1rem !important}.mr-md-3,.mx-md-3{margin-right:1rem !important}.mb-md-3,.my-md-3{margin-bottom:1rem !important}.ml-md-3,.mx-md-3{margin-left:1rem !important}.m-md-4{margin:1.5rem !important}.mt-md-4,.my-md-4{margin-top:1.5rem !important}.mr-md-4,.mx-md-4{margin-right:1.5rem !important}.mb-md-4,.my-md-4{margin-bottom:1.5rem !important}.ml-md-4,.mx-md-4{margin-left:1.5rem !important}.m-md-5{margin:3rem !important}.mt-md-5,.my-md-5{margin-top:3rem !important}.mr-md-5,.mx-md-5{margin-right:3rem !important}.mb-md-5,.my-md-5{margin-bottom:3rem !important}.ml-md-5,.mx-md-5{margin-left:3rem !important}.p-md-0{padding:0 !important}.pt-md-0,.py-md-0{padding-top:0 !important}.pr-md-0,.px-md-0{padding-right:0 !important}.pb-md-0,.py-md-0{padding-bottom:0 !important}.pl-md-0,.px-md-0{padding-left:0 !important}.p-md-1{padding:.25rem !important}.pt-md-1,.py-md-1{padding-top:.25rem !important}.pr-md-1,.px-md-1{padding-right:.25rem !important}.pb-md-1,.py-md-1{padding-bottom:.25rem !important}.pl-md-1,.px-md-1{padding-left:.25rem !important}.p-md-2{padding:.5rem !important}.pt-md-2,.py-md-2{padding-top:.5rem !important}.pr-md-2,.px-md-2{padding-right:.5rem !important}.pb-md-2,.py-md-2{padding-bottom:.5rem !important}.pl-md-2,.px-md-2{padding-left:.5rem !important}.p-md-3{padding:1rem !important}.pt-md-3,.py-md-3{padding-top:1rem !important}.pr-md-3,.px-md-3{padding-right:1rem !important}.pb-md-3,.py-md-3{padding-bottom:1rem !important}.pl-md-3,.px-md-3{padding-left:1rem !important}.p-md-4{padding:1.5rem !important}.pt-md-4,.py-md-4{padding-top:1.5rem !important}.pr-md-4,.px-md-4{padding-right:1.5rem !important}.pb-md-4,.py-md-4{padding-bottom:1.5rem !important}.pl-md-4,.px-md-4{padding-left:1.5rem !important}.p-md-5{padding:3rem !important}.pt-md-5,.py-md-5{padding-top:3rem !important}.pr-md-5,.px-md-5{padding-right:3rem !important}.pb-md-5,.py-md-5{padding-bottom:3rem !important}.pl-md-5,.px-md-5{padding-left:3rem !important}.m-md-n1{margin:-.25rem !important}.mt-md-n1,.my-md-n1{margin-top:-.25rem !important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem !important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem !important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem !important}.m-md-n2{margin:-.5rem !important}.mt-md-n2,.my-md-n2{margin-top:-.5rem !important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem !important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem !important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem !important}.m-md-n3{margin:-1rem !important}.mt-md-n3,.my-md-n3{margin-top:-1rem !important}.mr-md-n3,.mx-md-n3{margin-right:-1rem !important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem !important}.ml-md-n3,.mx-md-n3{margin-left:-1rem !important}.m-md-n4{margin:-1.5rem !important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem !important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem !important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem !important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem !important}.m-md-n5{margin:-3rem !important}.mt-md-n5,.my-md-n5{margin-top:-3rem !important}.mr-md-n5,.mx-md-n5{margin-right:-3rem !important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem !important}.ml-md-n5,.mx-md-n5{margin-left:-3rem !important}.m-md-auto{margin:auto !important}.mt-md-auto,.my-md-auto{margin-top:auto !important}.mr-md-auto,.mx-md-auto{margin-right:auto !important}.mb-md-auto,.my-md-auto{margin-bottom:auto !important}.ml-md-auto,.mx-md-auto{margin-left:auto !important}}@media (min-width: 992px){.m-lg-0{margin:0 !important}.mt-lg-0,.my-lg-0{margin-top:0 !important}.mr-lg-0,.mx-lg-0{margin-right:0 !important}.mb-lg-0,.my-lg-0{margin-bottom:0 !important}.ml-lg-0,.mx-lg-0{margin-left:0 !important}.m-lg-1{margin:.25rem !important}.mt-lg-1,.my-lg-1{margin-top:.25rem !important}.mr-lg-1,.mx-lg-1{margin-right:.25rem !important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem !important}.ml-lg-1,.mx-lg-1{margin-left:.25rem !important}.m-lg-2{margin:.5rem !important}.mt-lg-2,.my-lg-2{margin-top:.5rem !important}.mr-lg-2,.mx-lg-2{margin-right:.5rem !important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem !important}.ml-lg-2,.mx-lg-2{margin-left:.5rem !important}.m-lg-3{margin:1rem !important}.mt-lg-3,.my-lg-3{margin-top:1rem !important}.mr-lg-3,.mx-lg-3{margin-right:1rem !important}.mb-lg-3,.my-lg-3{margin-bottom:1rem !important}.ml-lg-3,.mx-lg-3{margin-left:1rem !important}.m-lg-4{margin:1.5rem !important}.mt-lg-4,.my-lg-4{margin-top:1.5rem !important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem !important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem !important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem !important}.m-lg-5{margin:3rem !important}.mt-lg-5,.my-lg-5{margin-top:3rem !important}.mr-lg-5,.mx-lg-5{margin-right:3rem !important}.mb-lg-5,.my-lg-5{margin-bottom:3rem !important}.ml-lg-5,.mx-lg-5{margin-left:3rem !important}.p-lg-0{padding:0 !important}.pt-lg-0,.py-lg-0{padding-top:0 !important}.pr-lg-0,.px-lg-0{padding-right:0 !important}.pb-lg-0,.py-lg-0{padding-bottom:0 !important}.pl-lg-0,.px-lg-0{padding-left:0 !important}.p-lg-1{padding:.25rem !important}.pt-lg-1,.py-lg-1{padding-top:.25rem !important}.pr-lg-1,.px-lg-1{padding-right:.25rem !important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem !important}.pl-lg-1,.px-lg-1{padding-left:.25rem !important}.p-lg-2{padding:.5rem !important}.pt-lg-2,.py-lg-2{padding-top:.5rem !important}.pr-lg-2,.px-lg-2{padding-right:.5rem !important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem !important}.pl-lg-2,.px-lg-2{padding-left:.5rem !important}.p-lg-3{padding:1rem !important}.pt-lg-3,.py-lg-3{padding-top:1rem !important}.pr-lg-3,.px-lg-3{padding-right:1rem !important}.pb-lg-3,.py-lg-3{padding-bottom:1rem !important}.pl-lg-3,.px-lg-3{padding-left:1rem !important}.p-lg-4{padding:1.5rem !important}.pt-lg-4,.py-lg-4{padding-top:1.5rem !important}.pr-lg-4,.px-lg-4{padding-right:1.5rem !important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem !important}.pl-lg-4,.px-lg-4{padding-left:1.5rem !important}.p-lg-5{padding:3rem !important}.pt-lg-5,.py-lg-5{padding-top:3rem !important}.pr-lg-5,.px-lg-5{padding-right:3rem !important}.pb-lg-5,.py-lg-5{padding-bottom:3rem !important}.pl-lg-5,.px-lg-5{padding-left:3rem !important}.m-lg-n1{margin:-.25rem !important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem !important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem !important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem !important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem !important}.m-lg-n2{margin:-.5rem !important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem !important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem !important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem !important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem !important}.m-lg-n3{margin:-1rem !important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem !important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem !important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem !important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem !important}.m-lg-n4{margin:-1.5rem !important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem !important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem !important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem !important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem !important}.m-lg-n5{margin:-3rem !important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem !important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem !important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem !important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem !important}.m-lg-auto{margin:auto !important}.mt-lg-auto,.my-lg-auto{margin-top:auto !important}.mr-lg-auto,.mx-lg-auto{margin-right:auto !important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto !important}.ml-lg-auto,.mx-lg-auto{margin-left:auto !important}}@media (min-width: 1200px){.m-xl-0{margin:0 !important}.mt-xl-0,.my-xl-0{margin-top:0 !important}.mr-xl-0,.mx-xl-0{margin-right:0 !important}.mb-xl-0,.my-xl-0{margin-bottom:0 !important}.ml-xl-0,.mx-xl-0{margin-left:0 !important}.m-xl-1{margin:.25rem !important}.mt-xl-1,.my-xl-1{margin-top:.25rem !important}.mr-xl-1,.mx-xl-1{margin-right:.25rem !important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem !important}.ml-xl-1,.mx-xl-1{margin-left:.25rem !important}.m-xl-2{margin:.5rem !important}.mt-xl-2,.my-xl-2{margin-top:.5rem !important}.mr-xl-2,.mx-xl-2{margin-right:.5rem !important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem !important}.ml-xl-2,.mx-xl-2{margin-left:.5rem !important}.m-xl-3{margin:1rem !important}.mt-xl-3,.my-xl-3{margin-top:1rem !important}.mr-xl-3,.mx-xl-3{margin-right:1rem !important}.mb-xl-3,.my-xl-3{margin-bottom:1rem !important}.ml-xl-3,.mx-xl-3{margin-left:1rem !important}.m-xl-4{margin:1.5rem !important}.mt-xl-4,.my-xl-4{margin-top:1.5rem !important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem !important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem !important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem !important}.m-xl-5{margin:3rem !important}.mt-xl-5,.my-xl-5{margin-top:3rem !important}.mr-xl-5,.mx-xl-5{margin-right:3rem !important}.mb-xl-5,.my-xl-5{margin-bottom:3rem !important}.ml-xl-5,.mx-xl-5{margin-left:3rem !important}.p-xl-0{padding:0 !important}.pt-xl-0,.py-xl-0{padding-top:0 !important}.pr-xl-0,.px-xl-0{padding-right:0 !important}.pb-xl-0,.py-xl-0{padding-bottom:0 !important}.pl-xl-0,.px-xl-0{padding-left:0 !important}.p-xl-1{padding:.25rem !important}.pt-xl-1,.py-xl-1{padding-top:.25rem !important}.pr-xl-1,.px-xl-1{padding-right:.25rem !important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem !important}.pl-xl-1,.px-xl-1{padding-left:.25rem !important}.p-xl-2{padding:.5rem !important}.pt-xl-2,.py-xl-2{padding-top:.5rem !important}.pr-xl-2,.px-xl-2{padding-right:.5rem !important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem !important}.pl-xl-2,.px-xl-2{padding-left:.5rem !important}.p-xl-3{padding:1rem !important}.pt-xl-3,.py-xl-3{padding-top:1rem !important}.pr-xl-3,.px-xl-3{padding-right:1rem !important}.pb-xl-3,.py-xl-3{padding-bottom:1rem !important}.pl-xl-3,.px-xl-3{padding-left:1rem !important}.p-xl-4{padding:1.5rem !important}.pt-xl-4,.py-xl-4{padding-top:1.5rem !important}.pr-xl-4,.px-xl-4{padding-right:1.5rem !important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem !important}.pl-xl-4,.px-xl-4{padding-left:1.5rem !important}.p-xl-5{padding:3rem !important}.pt-xl-5,.py-xl-5{padding-top:3rem !important}.pr-xl-5,.px-xl-5{padding-right:3rem !important}.pb-xl-5,.py-xl-5{padding-bottom:3rem !important}.pl-xl-5,.px-xl-5{padding-left:3rem !important}.m-xl-n1{margin:-.25rem !important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem !important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem !important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem !important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem !important}.m-xl-n2{margin:-.5rem !important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem !important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem !important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem !important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem !important}.m-xl-n3{margin:-1rem !important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem !important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem !important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem !important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem !important}.m-xl-n4{margin:-1.5rem !important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem !important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem !important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem !important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem !important}.m-xl-n5{margin:-3rem !important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem !important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem !important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem !important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem !important}.m-xl-auto{margin:auto !important}.mt-xl-auto,.my-xl-auto{margin-top:auto !important}.mr-xl-auto,.mx-xl-auto{margin-right:auto !important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto !important}.ml-xl-auto,.mx-xl-auto{margin-left:auto !important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:"JetBrains Mono" !important}.text-justify{text-align:justify !important}.text-wrap{white-space:normal !important}.text-nowrap{white-space:nowrap !important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}@media (min-width: 576px){.text-sm-left{text-align:left !important}.text-sm-right{text-align:right !important}.text-sm-center{text-align:center !important}}@media (min-width: 768px){.text-md-left{text-align:left !important}.text-md-right{text-align:right !important}.text-md-center{text-align:center !important}}@media (min-width: 992px){.text-lg-left{text-align:left !important}.text-lg-right{text-align:right !important}.text-lg-center{text-align:center !important}}@media (min-width: 1200px){.text-xl-left{text-align:left !important}.text-xl-right{text-align:right !important}.text-xl-center{text-align:center !important}}.text-lowercase{text-transform:lowercase !important}.text-uppercase{text-transform:uppercase !important}.text-capitalize{text-transform:capitalize !important}.font-weight-light{font-weight:300 !important}.font-weight-lighter{font-weight:lighter !important}.font-weight-normal{font-weight:400 !important}.font-weight-bold{font-weight:700 !important}.font-weight-bolder{font-weight:bolder !important}.font-italic{font-style:italic !important}.text-white{color:#fff !important}.text-default{color:#dee2e6 !important}a.text-default:hover,a.text-default:focus{color:#b2bcc5 !important}.text-primary{color:#096B72 !important}a.text-primary:hover,a.text-primary:focus{color:#03282b !important}.text-secondary{color:#6c757d !important}a.text-secondary:hover,a.text-secondary:focus{color:#494f54 !important}.text-success{color:#28a745 !important}a.text-success:hover,a.text-success:focus{color:#19692c !important}.text-info{color:#17a2b8 !important}a.text-info:hover,a.text-info:focus{color:#0f6674 !important}.text-warning{color:#ffc107 !important}a.text-warning:hover,a.text-warning:focus{color:#ba8b00 !important}.text-danger{color:#dc3545 !important}a.text-danger:hover,a.text-danger:focus{color:#a71d2a !important}.text-light{color:#f8f9fa !important}a.text-light:hover,a.text-light:focus{color:#cbd3da !important}.text-dark{color:#343a40 !important}a.text-dark:hover,a.text-dark:focus{color:#121416 !important}.text-body{color:#212529 !important}.text-muted,.help-text,.help-block{color:#6c757d !important}.text-black-50{color:rgba(0,0,0,0.5) !important}.text-white-50{color:rgba(255,255,255,0.5) !important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none !important}.text-break{word-break:break-word !important;word-wrap:break-word !important}.text-reset{color:inherit !important}.visible{visibility:visible !important}.invisible{visibility:hidden !important}@media print{*,*::before,*::after{text-shadow:none !important;box-shadow:none !important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap !important}pre,blockquote{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px !important}.container{min-width:992px !important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #dee2e6 !important}.table-dark{color:inherit}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}.table th[align=left]{text-align:left}.table th[align=right]{text-align:right}.table th[align=center]{text-align:center}.well{display:block;background-color:rgba(0,0,0,0.03);color:#212529;padding:1.25rem;border-radius:.25rem}.well-lg{padding:1.5rem;border-radius:.3rem}.well-sm{padding:0.5rem;border-radius:.2rem}.draggable .well{background-color:#f7f7f7}.dropdown-menu>li.active>a{color:#fff;text-decoration:none;background-color:#096B72}.navbar:not(.fixed-bottom):not(.navbar-fixed-bottom):not(.navbar-fixed-bottom){margin-bottom:20px}ul.nav.navbar-nav{flex:1;-webkit-flex:1}ul.nav.navbar-nav.navbar-right{flex:unset;-webkit-flex:unset;display:flex;display:-webkit-flex;justify-content:flex-end;-webkit-justify-content:flex-end}.navbar.navbar-default{background-color:#f8f9fa !important}.navbar.navbar-inverse{background-color:#343a40 !important}.navbar-toggle>.icon-bar{display:none}@media (max-width: 575.98px){.navbar-header{width:100%}.navbar-header .navbar-toggle{float:right}}.nav-tabs>li.active>a{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-pills>li.active>a{color:#fff;background-color:#096B72}.nav-stacked{flex-direction:column;-webkit-flex-direction:column}.progress-bar-default{background-color:#dee2e6;color:#000}.progress-bar-primary{background-color:#096B72;color:#fff}.progress-bar-secondary{background-color:#6c757d;color:#fff}.progress-bar-success{background-color:#28a745;color:#fff}.progress-bar-info{background-color:#17a2b8;color:#fff}.progress-bar-warning{background-color:#ffc107;color:#000}.progress-bar-danger{background-color:#dc3545;color:#fff}.progress-bar-light{background-color:#f8f9fa;color:#000}.progress-bar-dark{background-color:#343a40;color:#fff}@font-face{font-family:'Glyphicons Halflings';src:url("fonts/bootstrap/glyphicons-halflings-regular.eot");src:url("fonts/bootstrap/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("fonts/bootstrap/glyphicons-halflings-regular.woff2") format("woff2"),url("fonts/bootstrap/glyphicons-halflings-regular.woff") format("woff"),url("fonts/bootstrap/glyphicons-halflings-regular.ttf") format("truetype"),url("fonts/bootstrap/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}.form-group{margin-bottom:1rem}.shiny-input-checkboxgroup .checkbox-inline,.shiny-input-checkboxgroup .radio-inline,.shiny-input-radiogroup .checkbox-inline,.shiny-input-radiogroup .radio-inline{padding-left:0;margin-right:.75rem}.shiny-input-checkboxgroup .checkbox-inline label>input,.shiny-input-checkboxgroup .radio-inline label>input,.shiny-input-radiogroup .checkbox-inline label>input,.shiny-input-radiogroup .radio-inline label>input{margin-top:0;margin-right:.3125rem;margin-bottom:0}.input-daterange .input-group-addon.input-group-prepend.input-group-append{padding:inherit;line-height:inherit;text-shadow:inherit;border-width:0}.input-daterange .input-group-addon.input-group-prepend.input-group-append .input-group-text{border-radius:0}pre.shiny-code{padding:0.5rem}h1,h2,h3{margin-top:1.5rem}h4,h5,h6{margin-top:1rem}@media (min-width: 576px){.nav:not(.nav-hidden){display:flex !important;display:-webkit-flex !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column){float:none !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column)>.bslib-nav-spacer{margin-left:auto !important}.nav:not(.nav-hidden):not(.nav-stacked):not(.flex-column)>.form-inline{margin-top:auto;margin-bottom:auto}.nav.nav-stacked:not(.nav-hidden){flex-direction:column;-webkit-flex-direction:column;height:100%}.nav.nav-stacked:not(.nav-hidden)>.bslib-nav-spacer{margin-top:auto !important}} diff --git a/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.eot b/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.eot deleted file mode 100644 index b93a4953..00000000 Binary files a/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.eot and /dev/null differ diff --git a/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.svg b/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.svg deleted file mode 100644 index 94fb5490..00000000 --- a/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.svg +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.ttf b/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.ttf deleted file mode 100644 index 1413fc60..00000000 Binary files a/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.ttf and /dev/null differ diff --git a/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.woff b/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.woff deleted file mode 100644 index 9e612858..00000000 Binary files a/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.woff and /dev/null differ diff --git a/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.woff2 b/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.woff2 deleted file mode 100644 index 64539b54..00000000 Binary files a/_book/libs/bootstrap-4.6.0/fonts/bootstrap/glyphicons-halflings-regular.woff2 and /dev/null differ diff --git a/_book/libs/bs3compat-0.4.0.9000/bs3compat.js b/_book/libs/bs3compat-0.4.0.9000/bs3compat.js deleted file mode 100644 index 4f029dc7..00000000 --- a/_book/libs/bs3compat-0.4.0.9000/bs3compat.js +++ /dev/null @@ -1,48 +0,0 @@ -// Inform the world that we have the ability to use BS3 nav/navbar markup in BS4 -window.BS3_COMPAT = true; - -// This logic needs to execute after both the BS4+ (new) as well as BS3 (legacy) -// jQuery plugins have been registered. For BS5, plugin registration happens -// after DOM content is loaded, which is why we do the same here. -// https://github.com/twbs/bootstrap/blob/08139c22/js/dist/tab.js#L87 -$(function() { - - // The legacy plugin needs to be registered after the new one - if (!$.fn.tab.Constructor.VERSION.match(/^3\./)) { - (console.warn || console.error || console.log)("bs3compat.js couldn't find bs3 tab impl; bs3 tabs will not be properly supported"); - return; - } - var legacyTabPlugin = $.fn.tab.noConflict(); - - if (!$.fn.tab || !$.fn.tab.Constructor || !$.fn.tab.noConflict) { - (console.warn || console.error || console.log)("bs3compat.js couldn't find a jQuery tab impl; bs3 tabs will not be properly supported"); - } - var newTabPlugin = $.fn.tab.noConflict(); - - // Re-define the tab click event - // https://github.com/twbs/bootstrap/blob/08139c2/js/src/tab.js#L33 - var EVENT_KEY = "click.bs.tab.data-api"; - $(document).off(EVENT_KEY); - - var SELECTOR = '[data-toggle="tab"], [data-toggle="pill"], [data-bs-toggle="tab"], [data-bs-toggle="pill"]'; - $(document).on(EVENT_KEY, SELECTOR, function(event) { - event.preventDefault(); - $(this).tab("show"); - }); - - function TabPlugin(config) { - // Legacy (bs3) tabs: li.active > a - // New (bs4+) tabs: li.nav-item > a.active.nav-link - var legacy = $(this).closest(".nav").find("li:not(.dropdown).active > a").length > 0; - var plugin = legacy ? legacyTabPlugin : newTabPlugin; - plugin.call($(this), config); - } - - var noconflict = $.fn.tab; - $.fn.tab = TabPlugin; - $.fn.tab.Constructor = newTabPlugin.Constructor; - $.fn.tab.noConflict = function() { - $.fn.tab = noconflict; - return TabPlugin; - }; -}); diff --git a/_book/libs/bs3compat-0.4.0.9000/tabs.js b/_book/libs/bs3compat-0.4.0.9000/tabs.js deleted file mode 100644 index 79825cce..00000000 --- a/_book/libs/bs3compat-0.4.0.9000/tabs.js +++ /dev/null @@ -1,157 +0,0 @@ -/* ======================================================================== - * Bootstrap: tab.js v3.4.1 - * https://getbootstrap.com/docs/3.4/javascript/#tabs - * ======================================================================== - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - -// Register tab plugin after DOM content loaded in order to -// override BS5's plugin -// https://github.com/twbs/bootstrap/blob/08139c22/js/dist/tab.js#L87 -$(function() { - 'use strict'; - - // TAB CLASS DEFINITION - // ==================== - - var Tab = function (element) { - // jscs:disable requireDollarBeforejQueryAssignment - this.element = $(element) - // jscs:enable requireDollarBeforejQueryAssignment - } - - Tab.VERSION = '3.4.1' - - Tab.TRANSITION_DURATION = 150 - - Tab.prototype.show = function () { - var $this = this.element - var $ul = $this.closest('ul:not(.dropdown-menu)') - var selector = $this.data('target') - - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 - } - - if ($this.parent('li').hasClass('active')) return - - var $previous = $ul.find('.active:last a') - var hideEvent = $.Event('hide.bs.tab', { - relatedTarget: $this[0] - }) - var showEvent = $.Event('show.bs.tab', { - relatedTarget: $previous[0] - }) - - $previous.trigger(hideEvent) - $this.trigger(showEvent) - - if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return - - var $target = $(document).find(selector) - - this.activate($this.closest('li'), $ul) - this.activate($target, $target.parent(), function () { - $previous.trigger({ - type: 'hidden.bs.tab', - relatedTarget: $this[0] - }) - $this.trigger({ - type: 'shown.bs.tab', - relatedTarget: $previous[0] - }) - }) - } - - Tab.prototype.activate = function (element, container, callback) { - var $active = container.find('> .active') - var transition = callback - && $.support.transition - && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) - - function next() { - $active - .removeClass('active') - .find('> .dropdown-menu > .active') - .removeClass('active') - .end() - .find('[data-toggle="tab"]') - .attr('aria-expanded', false) - - element - .addClass('active') - .find('[data-toggle="tab"]') - .attr('aria-expanded', true) - - if (transition) { - element[0].offsetWidth // reflow for transition - element.addClass('in') - } else { - element.removeClass('fade') - } - - if (element.parent('.dropdown-menu').length) { - element - .closest('li.dropdown') - .addClass('active') - .end() - .find('[data-toggle="tab"]') - .attr('aria-expanded', true) - } - - callback && callback() - } - - $active.length && transition ? - $active - .one('bsTransitionEnd', next) - .emulateTransitionEnd(Tab.TRANSITION_DURATION) : - next() - - $active.removeClass('in') - } - - - // TAB PLUGIN DEFINITION - // ===================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.tab') - - if (!data) $this.data('bs.tab', (data = new Tab(this))) - if (typeof option == 'string') data[option]() - }) - } - - var old = $.fn.tab - - $.fn.tab = Plugin - $.fn.tab.Constructor = Tab - - - // TAB NO CONFLICT - // =============== - - $.fn.tab.noConflict = function () { - $.fn.tab = old - return this - } - - - // TAB DATA-API - // ============ - - var clickHandler = function (e) { - e.preventDefault() - Plugin.call($(this), 'show') - } - - $(document) - .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) - .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) - -}); diff --git a/_book/libs/bs3compat-0.4.0.9000/transition.js b/_book/libs/bs3compat-0.4.0.9000/transition.js deleted file mode 100644 index 81e7122c..00000000 --- a/_book/libs/bs3compat-0.4.0.9000/transition.js +++ /dev/null @@ -1,59 +0,0 @@ -/* ======================================================================== - * Bootstrap: transition.js v3.4.1 - * https://getbootstrap.com/docs/3.4/javascript/#transitions - * ======================================================================== - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/v3-dev/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // CSS TRANSITION SUPPORT (Shoutout: https://modernizr.com/) - // ============================================================ - - function transitionEnd() { - var el = document.createElement('bootstrap') - - var transEndEventNames = { - WebkitTransition : 'webkitTransitionEnd', - MozTransition : 'transitionend', - OTransition : 'oTransitionEnd otransitionend', - transition : 'transitionend' - } - - for (var name in transEndEventNames) { - if (el.style[name] !== undefined) { - return { end: transEndEventNames[name] } - } - } - - return false // explicit for ie8 ( ._.) - } - - // https://blog.alexmaccaw.com/css-transitions - $.fn.emulateTransitionEnd = function (duration) { - var called = false - var $el = this - $(this).one('bsTransitionEnd', function () { called = true }) - var callback = function () { if (!called) $($el).trigger($.support.transition.end) } - setTimeout(callback, duration) - return this - } - - $(function () { - $.support.transition = transitionEnd() - - if (!$.support.transition) return - - $.event.special.bsTransitionEnd = { - bindType: $.support.transition.end, - delegateType: $.support.transition.end, - handle: function (e) { - if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) - } - } - }) - -}(jQuery); diff --git a/_book/libs/bs3compat-0.4.1/bs3compat.js b/_book/libs/bs3compat-0.4.1/bs3compat.js deleted file mode 100644 index 4f029dc7..00000000 --- a/_book/libs/bs3compat-0.4.1/bs3compat.js +++ /dev/null @@ -1,48 +0,0 @@ -// Inform the world that we have the ability to use BS3 nav/navbar markup in BS4 -window.BS3_COMPAT = true; - -// This logic needs to execute after both the BS4+ (new) as well as BS3 (legacy) -// jQuery plugins have been registered. For BS5, plugin registration happens -// after DOM content is loaded, which is why we do the same here. -// https://github.com/twbs/bootstrap/blob/08139c22/js/dist/tab.js#L87 -$(function() { - - // The legacy plugin needs to be registered after the new one - if (!$.fn.tab.Constructor.VERSION.match(/^3\./)) { - (console.warn || console.error || console.log)("bs3compat.js couldn't find bs3 tab impl; bs3 tabs will not be properly supported"); - return; - } - var legacyTabPlugin = $.fn.tab.noConflict(); - - if (!$.fn.tab || !$.fn.tab.Constructor || !$.fn.tab.noConflict) { - (console.warn || console.error || console.log)("bs3compat.js couldn't find a jQuery tab impl; bs3 tabs will not be properly supported"); - } - var newTabPlugin = $.fn.tab.noConflict(); - - // Re-define the tab click event - // https://github.com/twbs/bootstrap/blob/08139c2/js/src/tab.js#L33 - var EVENT_KEY = "click.bs.tab.data-api"; - $(document).off(EVENT_KEY); - - var SELECTOR = '[data-toggle="tab"], [data-toggle="pill"], [data-bs-toggle="tab"], [data-bs-toggle="pill"]'; - $(document).on(EVENT_KEY, SELECTOR, function(event) { - event.preventDefault(); - $(this).tab("show"); - }); - - function TabPlugin(config) { - // Legacy (bs3) tabs: li.active > a - // New (bs4+) tabs: li.nav-item > a.active.nav-link - var legacy = $(this).closest(".nav").find("li:not(.dropdown).active > a").length > 0; - var plugin = legacy ? legacyTabPlugin : newTabPlugin; - plugin.call($(this), config); - } - - var noconflict = $.fn.tab; - $.fn.tab = TabPlugin; - $.fn.tab.Constructor = newTabPlugin.Constructor; - $.fn.tab.noConflict = function() { - $.fn.tab = noconflict; - return TabPlugin; - }; -}); diff --git a/_book/libs/bs3compat-0.4.1/tabs.js b/_book/libs/bs3compat-0.4.1/tabs.js deleted file mode 100644 index 79825cce..00000000 --- a/_book/libs/bs3compat-0.4.1/tabs.js +++ /dev/null @@ -1,157 +0,0 @@ -/* ======================================================================== - * Bootstrap: tab.js v3.4.1 - * https://getbootstrap.com/docs/3.4/javascript/#tabs - * ======================================================================== - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - -// Register tab plugin after DOM content loaded in order to -// override BS5's plugin -// https://github.com/twbs/bootstrap/blob/08139c22/js/dist/tab.js#L87 -$(function() { - 'use strict'; - - // TAB CLASS DEFINITION - // ==================== - - var Tab = function (element) { - // jscs:disable requireDollarBeforejQueryAssignment - this.element = $(element) - // jscs:enable requireDollarBeforejQueryAssignment - } - - Tab.VERSION = '3.4.1' - - Tab.TRANSITION_DURATION = 150 - - Tab.prototype.show = function () { - var $this = this.element - var $ul = $this.closest('ul:not(.dropdown-menu)') - var selector = $this.data('target') - - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 - } - - if ($this.parent('li').hasClass('active')) return - - var $previous = $ul.find('.active:last a') - var hideEvent = $.Event('hide.bs.tab', { - relatedTarget: $this[0] - }) - var showEvent = $.Event('show.bs.tab', { - relatedTarget: $previous[0] - }) - - $previous.trigger(hideEvent) - $this.trigger(showEvent) - - if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return - - var $target = $(document).find(selector) - - this.activate($this.closest('li'), $ul) - this.activate($target, $target.parent(), function () { - $previous.trigger({ - type: 'hidden.bs.tab', - relatedTarget: $this[0] - }) - $this.trigger({ - type: 'shown.bs.tab', - relatedTarget: $previous[0] - }) - }) - } - - Tab.prototype.activate = function (element, container, callback) { - var $active = container.find('> .active') - var transition = callback - && $.support.transition - && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) - - function next() { - $active - .removeClass('active') - .find('> .dropdown-menu > .active') - .removeClass('active') - .end() - .find('[data-toggle="tab"]') - .attr('aria-expanded', false) - - element - .addClass('active') - .find('[data-toggle="tab"]') - .attr('aria-expanded', true) - - if (transition) { - element[0].offsetWidth // reflow for transition - element.addClass('in') - } else { - element.removeClass('fade') - } - - if (element.parent('.dropdown-menu').length) { - element - .closest('li.dropdown') - .addClass('active') - .end() - .find('[data-toggle="tab"]') - .attr('aria-expanded', true) - } - - callback && callback() - } - - $active.length && transition ? - $active - .one('bsTransitionEnd', next) - .emulateTransitionEnd(Tab.TRANSITION_DURATION) : - next() - - $active.removeClass('in') - } - - - // TAB PLUGIN DEFINITION - // ===================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.tab') - - if (!data) $this.data('bs.tab', (data = new Tab(this))) - if (typeof option == 'string') data[option]() - }) - } - - var old = $.fn.tab - - $.fn.tab = Plugin - $.fn.tab.Constructor = Tab - - - // TAB NO CONFLICT - // =============== - - $.fn.tab.noConflict = function () { - $.fn.tab = old - return this - } - - - // TAB DATA-API - // ============ - - var clickHandler = function (e) { - e.preventDefault() - Plugin.call($(this), 'show') - } - - $(document) - .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) - .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) - -}); diff --git a/_book/libs/bs3compat-0.4.1/transition.js b/_book/libs/bs3compat-0.4.1/transition.js deleted file mode 100644 index 81e7122c..00000000 --- a/_book/libs/bs3compat-0.4.1/transition.js +++ /dev/null @@ -1,59 +0,0 @@ -/* ======================================================================== - * Bootstrap: transition.js v3.4.1 - * https://getbootstrap.com/docs/3.4/javascript/#transitions - * ======================================================================== - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/v3-dev/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // CSS TRANSITION SUPPORT (Shoutout: https://modernizr.com/) - // ============================================================ - - function transitionEnd() { - var el = document.createElement('bootstrap') - - var transEndEventNames = { - WebkitTransition : 'webkitTransitionEnd', - MozTransition : 'transitionend', - OTransition : 'oTransitionEnd otransitionend', - transition : 'transitionend' - } - - for (var name in transEndEventNames) { - if (el.style[name] !== undefined) { - return { end: transEndEventNames[name] } - } - } - - return false // explicit for ie8 ( ._.) - } - - // https://blog.alexmaccaw.com/css-transitions - $.fn.emulateTransitionEnd = function (duration) { - var called = false - var $el = this - $(this).one('bsTransitionEnd', function () { called = true }) - var callback = function () { if (!called) $($el).trigger($.support.transition.end) } - setTimeout(callback, duration) - return this - } - - $(function () { - $.support.transition = transitionEnd() - - if (!$.support.transition) return - - $.event.special.bsTransitionEnd = { - bindType: $.support.transition.end, - delegateType: $.support.transition.end, - handle: function (e) { - if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) - } - } - }) - -}(jQuery); diff --git a/_book/libs/bs4_book-1.0.0/bs4_book.css b/_book/libs/bs4_book-1.0.0/bs4_book.css deleted file mode 100644 index 0bc45a0b..00000000 --- a/_book/libs/bs4_book-1.0.0/bs4_book.css +++ /dev/null @@ -1,555 +0,0 @@ -/* Page structure ---------------------------------------------------------- - -+-----+-----------------+------+--------------+ -| | sidebar-chapter | main | sidebar-book | -+=====+=================+======+==============+ -| sml | 12 (collapsed) | 12 | - | -| md | 12 (collapsed) | 9 | 3 | -| lg | 3 | 7 | 2 | -+-----+-----------------+------+--------------+ - -Side uses container-fluid so we set up some additional breakpoints, to ensure -that the columns never get too wide, either individually or collectively. - -*/ - - -@media (min-width: 1200px) { - .container-fluid { - max-width: 95rem; - } - .container-fluid .row { - justify-content: space-evenly; - } - .container-fluid main { - max-width: 45rem; - } - .sidebar { - max-width: 15rem; - } - - /* Increase font-size for very wide devices */ - body { - font-size: 18px - } -} - -main {margin-top: 1rem;} - -@media (max-width: 991.98px) { - .sidebar { - max-width: 100%; - } - - .collapse-lg { - display: none; - padding: 1rem; - border-radius: 0.2rem; - background: #fafafa; - margin-top: 0.5rem; - margin-bottom: 1rem; - box-shadow: 5px 5px 10px rgba(0.1, 0.1, 0.1, 0.5); - border: 1px solid var(--primary); - } - .book-toc { - column-count: 2; - } - .sidebar-book, main { - padding-left: 1rem; - } - .sidebar-book { - margin-top: 1rem; - } -} -@media (min-width: 992px) { - .collapse-lg { - display: block !important; - } -} -.collapse-lg.show { - display: block; -} - -@media (min-width: 768px) { - .sidebar-chapter { - position: sticky; - max-height: 100vh; - top: 0; - overflow-y: auto; - } -} - -@media (min-width: 992px) { - .sidebar-book { - position: sticky; - max-height: 100vh; - top: 0; - overflow-y: auto; - } -} - -/* Chapter nav ----------------------------------------- */ - -.chapter-nav { - display: flex; - justify-content: space-between; - margin-top: 2rem; -} -.chapter-nav .prev, .chapter-nav .next { - padding: 0.5rem; - border: 1px solid #eee; - border-radius: 0.2rem; - box-shadow: 0 .5rem 1rem rgba(0,0,0,.15); -} -.chapter-nav .empty { - border: none; -} -.chapter-nav .prev a:before { - content: "Β« "; -} -.chapter-nav .next a:after { - content: " Β»"; -} - -/* Sidebar ------------------------------------------------------ */ - -.sidebar h1, .sidebar h2 { - margin-top: 1.5rem; - margin-bottom: 0.5rem; -} -.sidebar h1 { - font-size: 1.1rem; -} -@media (max-width: 991.98px) { - .sidebar h1 { - font-size: 1.5rem; - margin-top: 0rem; - } -} -.sidebar h2 { - font-size: 0.9rem; -} - -.sidebar hr { - margin: 0 0 0.5rem 0; -} - -.sidebar li { - margin-bottom: 0.5rem; - font-size: 0.9rem; - line-height: 1.5; -} - -.sidebar li.book-part { - margin-top: 1rem; -} - -.book-toc .active { - font-weight: bolder; -} - -.book-extra { - border-top: 1px solid #ccc; - margin-top: 0.5rem; - padding-top: 0.5rem; - font-size: 0.9rem; -} - -.book-extra i { - font-size: 1.2em; -} - -/* Sticky footer ----------------------------------------- */ -html, body {height: 100%} - -body { - display: flex; - flex-direction: column; -} -.container-fluid { - flex: 1 0 auto; -} -footer { - flex-shrink: 0; - font-size: 0.9rem; - -} -footer a { - text-decoration: underline; -} - -/* Scrollspy --------------------------------------------- */ - -nav[data-toggle="toc"] .nav > li { - margin-bottom: calc(0.5rem - 3px); -} - -nav[data-toggle="toc"] .nav > li > a { - padding: 3px; - display: block; -} - -nav[data-toggle="toc"] .nav > li > a:hover { - text-decoration: underline; -} - -nav[data-toggle="toc"] .nav a.nav-link.active, -nav[data-toggle="toc"] .nav .nav-link.active > li > a { - background-color: #eee; -} - -/* Nav: second level (shown on .active) */ -nav[data-toggle="toc"] .nav-link + ul { - display: none; -} -nav[data-toggle="toc"] .nav-link.active + ul { - margin-top: 3px; - display: block; -} - -nav[data-toggle="toc"] .nav .nav > li { - margin-bottom: 0; -} -nav[data-toggle="toc"] .nav .nav > li > a { - margin-left: 10px; -} -/* Figures -------------------------------------------- */ - -.figure, .inline-figure { - width: 100%; - overflow-x: auto; -} - -.inline-figure { - border: solid 2px #f1f1f1; - margin-bottom: 1rem; /* to match

*/ -} - -.figure { - border-top: 2px solid #eee; - border-bottom: 2px solid #eee; - margin: 1.5rem -0.5rem 1rem -0.5rem; - padding: 1.5rem 0 1rem 1rem; -} - -@media (max-width: 767.98px) { - .figure { - margin: 1.5rem -1rem 1.5rem -1rem; - padding: 1.5rem; - width: 100vw; - } -} - -caption, p.caption { - text-align: left; - margin-top: 1rem; - margin-bottom: 0; - font-size: 0.9rem; - color: #777; -} - -/* Headings -------------------------------------------- */ - -h2 { - margin-top: 2rem; - margin-bottom: 1rem; - font-size: 1.5rem; -} -h3 { margin-top: 1.5em; font-size: 1.2rem; } -h4 { margin-top: 1.5em; font-size: 1.1rem; } -h5 { margin-top: 1.5em; font-size: 1rem; } - -h1, h2, h3, h4, h5 { - line-height: 1.3; -} - -.header-section-number { - color: #6C6C6C; - font-weight: normal; -} - -.dropdown-item .header-section-number { - position: absolute; - width: 2rem; - left: -1rem; - display: block; - text-align: right; -} - -.anchor { - font-size: max(0.5em, 1rem); - margin-left: 0.5rem; - display: none; -} -h1:hover .anchor, -h2:hover .anchor, -h3:hover .anchor, -h4:hover .anchor, -h5:hover .anchor, -h6:hover .anchor { - display: inline; -} - -/* Tables ---------------------------------------------- */ - -.inline-table { - overflow-x: auto; -} - -table.kable_wrapper td { - vertical-align: top; -} - - -/* Footnotes --------------------------------------------- */ - -.popover { - max-width: min(100vw, 32rem); - font-size: 0.9rem; - box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.3); -} -.popover-body { - padding: 0.75rem; -} -.popover-body p:last-child { - margin-bottom: 0; -} - -a.footnote-ref { - cursor: pointer; -} - -/* Search ---------------------------------------------- */ - -mark { - background: linear-gradient(-100deg, - hsla(48,92%,75%,.3), - hsla(48,92%,75%,.7) 95%, - hsla(48,92%,75%,.1) - ) -} - -.algolia-autocomplete .aa-hint { - color: #999; -} -.algolia-autocomplete .aa-dropdown-menu { - width: min(100%, 20rem); - background-color: #fff; - border: 1px solid var(--gray); - border-radius: 0.2rem; - margin-top: 2px; - - max-height: 50vh; - overflow-y: auto; -} -.algolia-autocomplete .aa-dropdown-menu .aa-suggestion { - cursor: pointer; - padding: 5px 4px; - border-bottom: 1px #ddd solid; - font-size: 0.9rem; -} -.algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor { - background-color: #B2D7FF; -} - -/* Code ------------------------------------------------ */ - -pre { - position: relative; - overflow: auto; - border: 1px solid #eee; - padding: 0.5rem; - margin: 0 -0.5rem 1rem -0.5rem; - background-image: linear-gradient(160deg,#f8f8f8 0,#f1f1f1 100%); -} - -@media (max-width: 767.98px) { - /* Make background span full width on mobile */ - .section > .sourceCode > pre { - margin: 0 -1rem 1rem -1rem; - padding: 0.5rem 1rem; - width: 100vw; - } -} - -code { - background-color: #f8f8f8; -} - -pre code { - background-color: transparent; - word-break: normal; /* force wide blocks to scroll, not wrap */ - word-wrap: normal; -} - -pre, code { - border-radius: 0.2rem; - color: #212529; /* match text colour */ -} -code a:any-link { - color: inherit; /* use colour from syntax highlighting */ - text-decoration: underline; - text-decoration-color: #ccc; -} - -/* copy button */ - -div.sourceCode { - position: relative; -} - -.btn-copy { - position: absolute; - top: 0rem; - right: -0.5rem; /* coherent with pre margin rule */ -} - -div.sourceCode > button { - filter: opacity(50%); -} - -div.sourceCode > button:hover { - filter: opacity(100%); -} - -div.sourceCode > button > i.bi::before { - display: inline-block; - height: 1rem; - width: 1rem; - content: ""; - vertical-align: -0.125em; - background-image: url('data:image/svg+xml,'); - background-repeat: no-repeat; - background-size: 1rem 1rem; -} - -div.sourceCode > button.btn-copy-checked > .bi::before { - background-image: url('data:image/svg+xml,'); -} - -/* https://github.com/rstudio/distill/blob/master/inst/rmarkdown/templates/distill_article/resources/a11y.theme + https://gist.github.com/hadley/f53b6e92df20994fdabe6562d284728a */ -code span.ot {color:#007faa} -code span.at {color:#7d9029} -code span.ss {color:#bb6688} -code span.an {color:#545454;} -code span.fu {color:#4254A7} -code span.st {color:#008000} -code span.cf {color:#007faa;} -code span.op {color:#696969} -code span.er {color:#ff0000;} -code span.bn {color:#a1024a} -code span.al {color:#ff0000;} -code span.va {color:#19177c} -code span.bu {color: #007faa;} -code span.ex {} -code span.pp {color:#bc7a00} -code span.in {color:#545454;} -code span.vs {color:#008000} -code span.wa {color:#545454; font-style: italic} -code span.do {color:#ba2121; font-style: italic} -code span.im {color:#007faa; font-weight: bold;} -code span.ch {color:#008000} -code span.dt {color:#aa5d00} -code span.fl {color:#a1024a} -code span.co {color:#545454} -code span.cv {color:#545454; font-style: italic} -code span.cn {color:#d91e18} -code span.sc {color:#008000} -code span.dv {color:#a1024a} -code span.kw {color:#007faa} - -/* Misc typography ---------------------------------------------- */ - -a { - overflow-wrap: break-word; - word-wrap: break-word; -} - -blockquote { - border-left: 0.5rem solid #eee; - padding-left: 0.5rem; - margin-left: -0.5rem; -} - -body { - line-height: 1.6; -} - -.smallcaps { - font-variant: small-caps; -} - -/* special callout blocks */ - -.rmdnote, .rmdcaution, .rmdimportant, .rmdtip, .rmdwarning { - margin: 1rem calc(-2px - 0.5em); - padding: 1rem; - border: 2px solid #eee; -} - -.rmdnote > *:last-child, .rmdcaution > *:last-child, .rmdimportant > *:last-child, .rmdtip > *:last-child, .rmdwarning > *:last-child { - margin-bottom: 0; -} - -@media (max-width: 767.98px) { - .rmdnote, .rmdcaution, .rmdimportant, .rmdtip, .rmdwarning { - margin: 1rem -1rem; - border-width: 4px; - } -} - -.rmdnote { - border-color: var(--primary); -} -.rmdimportant { - border-color: var(--success); -} -.rmdcaution { - border-color: var(--danger); -} -.rmdwarning { - border-color: var(--warning); -} -.rmdtip { - border-color: var(--info); -} - -.rmdcaution pre, .rmdimportant pre, .rmdnote pre, .rmdtip pre, .rmdwarning pre { - /* Make code blocks full width in rmdnote */ - margin: 0 -1rem 1rem -1rem; - padding: 1rem; -} - -.rmdcaution .btn-copy, .rmdimportant .btn-copy, .rmdnote .btn-copy, .rmdtip .btn-copy, .rmdwarning .btn-copy { - /* Needs to be set according to margin in callout pre block */ - right: -1rem; -} - -main ul { - list-style-type: square; -} -main ol, main ul { - padding-left: 25px; - margin-bottom: 0; -} -main li { - margin-bottom: 0.5rem; -} -main ol > li:first-child, main ul > li:first-child { - margin-top: 0.5rem; -} - -/* Cover image */ - -img.cover { - float: right; - margin: 0 1rem 0 1rem; - box-shadow: 0 .5rem 1rem rgba(0,0,0,.15); -} -@media (max-width: 767.98px) { - img.cover { - float: none; - display: block; - margin: 0 auto 1rem auto; - } -} diff --git a/_book/libs/bs4_book-1.0.0/bs4_book.js b/_book/libs/bs4_book-1.0.0/bs4_book.js deleted file mode 100644 index 348d1828..00000000 --- a/_book/libs/bs4_book-1.0.0/bs4_book.js +++ /dev/null @@ -1,136 +0,0 @@ -$(function () { - var url = new URL(window.location.href); - var toMark = url.searchParams.get("q"); - var mark = new Mark("main"); - if (toMark) { - mark.mark(toMark, { - accuracy: { - value: "complementary", - limiters: [",", ".", ":", "/"], - } - }); - } - - // Activate popovers - $('[data-toggle="popover"]').popover({ - container: 'body', - html: true, - trigger: 'focus', - placement: "top", - sanitize: false, - }); - $('[data-toggle="tooltip"]').tooltip(); -}) - -// Search ---------------------------------------------------------------------- - -var fuse; - -$(function () { - // Initialise search index on focus - $("#search").focus(async function(e) { - if (fuse) { - return; - } - - $(e.target).addClass("loading"); - - var response = await fetch('search.json'); - var data = await response.json(); - - var options = { - keys: ["heading", "text", "code"], - ignoreLocation: true, - threshold: 0.1, - includeMatches: true, - includeScore: true, - }; - fuse = new Fuse(data, options); - - $(e.target).removeClass("loading"); - }); - - // Use algolia autocomplete - var options = { - autoselect: true, - debug: true, - hint: false, - minLength: 2, - }; - - $("#search").autocomplete(options, [ - { - name: "content", - source: searchFuse, - templates: { - suggestion: (s) => { - if (s.chapter == s.heading) { - return `${s.chapter}`; - } else { - return `${s.chapter} /
${s.heading}`; - } - }, - }, - }, - ]).on('autocomplete:selected', function(event, s) { - window.location.href = s.path + "?q=" + q + "#" + s.id; - }); -}); - -var q; -async function searchFuse(query, callback) { - await fuse; - - var items; - if (!fuse) { - items = []; - } else { - q = query; - var results = fuse.search(query, { limit: 20 }); - items = results - .filter((x) => x.score <= 0.75) - .map((x) => x.item); - } - - callback(items); -} - -// Copy to clipboard ----------------------------------------------------------- - -function changeTooltipMessage(element, msg) { - var tooltipOriginalTitle=element.getAttribute('data-original-title'); - element.setAttribute('data-original-title', msg); - $(element).tooltip('show'); - element.setAttribute('data-original-title', tooltipOriginalTitle); -} - -$(document).ready(function() { - if(ClipboardJS.isSupported()) { - // Insert copy buttons - var copyButton = ""; - $(copyButton).appendTo("div.sourceCode"); - // Initialize tooltips: - $('.btn-copy').tooltip({container: 'body', boundary: 'window'}); - - // Initialize clipboard: - var clipboard = new ClipboardJS('.btn-copy', { - text: function(trigger) { - return trigger.parentNode.textContent; - } - }); - - clipboard.on('success', function(e) { - const btn = e.trigger; - changeTooltipMessage(btn, 'Copied!'); - btn.classList.add('btn-copy-checked'); - setTimeout(function() { - btn.classList.remove('btn-copy-checked'); - }, 2000); - e.clearSelection(); - }); - - clipboard.on('error', function() { - changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); - }); - }; -}); diff --git a/_book/libs/jquery-3.6.0/jquery-3.6.0.min.js b/_book/libs/jquery-3.6.0/jquery-3.6.0.min.js deleted file mode 100644 index c4c6022f..00000000 --- a/_book/libs/jquery-3.6.0/jquery-3.6.0.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="

",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 - - - - - -Chapter 23 Measuring performance | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-23 Measuring performance -

-

Attaching the needed libraries:

-
-library(profvis, warn.conflicts = FALSE)
-library(dplyr, warn.conflicts = FALSE)
-
-

-23.1 Profiling (Exercises 23.2.4) -

-
-

Q1. Profile the following function with torture = TRUE. What is surprising? Read the source code of rm() to figure out what’s going on.

-
-f <- function(n = 1e5) {
-  x <- rep(1, n)
-  rm(x)
-}
-

A1. Let’s source the functions mentioned in exercises.

-
-source("profiling-exercises.R")
-

First, we try without torture = TRUE: it returns no meaningful results.

-
-profvis(f())
-#> Error in parse_rprof(prof_output, expr_source): No parsing data available. Maybe your function was too fast?
-

As mentioned in the docs, setting torture = TRUE

-
-

Triggers garbage collection after every torture memory allocation call.

-
-

This process somehow never seems to finish and crashes the RStudio session when it stops!

-
-profvis(f(), torture = TRUE)
-

The question says that documentation for rm() may provide clues:

-
-rm
-#> function (..., list = character(), pos = -1, envir = as.environment(pos), 
-#>     inherits = FALSE) 
-#> {
-#>     dots <- match.call(expand.dots = FALSE)$...
-#>     if (length(dots) && !all(vapply(dots, function(x) is.symbol(x) || 
-#>         is.character(x), NA, USE.NAMES = FALSE))) 
-#>         stop("... must contain names or character strings")
-#>     names <- vapply(dots, as.character, "")
-#>     if (length(names) == 0L) 
-#>         names <- character()
-#>     list <- .Primitive("c")(list, names)
-#>     .Internal(remove(list, envir, inherits))
-#> }
-#> <bytecode: 0x14ed91b98>
-#> <environment: namespace:base>
-

I still couldn’t figure out why. I would recommend checking out the official answer.

-
-
-
-

-23.2 Microbenchmarking (Exercises 23.3.3) -

-
-

Q1. Instead of using bench::mark(), you could use the built-in function system.time(). But system.time() is much less precise, so you’ll need to repeat each operation many times with a loop, and then divide to find the average time of each operation, as in the code below.

-
-n <- 1e6
-system.time(for (i in 1:n) sqrt(x)) / n
-system.time(for (i in 1:n) x^0.5) / n
-

How do the estimates from system.time() compare to those from bench::mark()? Why are they different?

-

A1. Let’s benchmark first using these two approaches:

-
-n <- 1e6
-x <- runif(100)
-
-# bench -------------------
-
-bench_df <- bench::mark(
-  sqrt(x),
-  x^0.5,
-  iterations = n,
-  time_unit = "us"
-)
-
-t_bench_df <- bench_df %>%
-  select(expression, time) %>%
-  rowwise() %>%
-  mutate(bench_mean = mean(unlist(time))) %>%
-  ungroup() %>%
-  select(-time)
-
-# system.time -------------------
-
-# garbage collection performed immediately before the timing
-t1_systime_gc <- system.time(for (i in 1:n) sqrt(x), gcFirst = TRUE) / n
-t2_systime_gc <- system.time(for (i in 1:n) x^0.5, gcFirst = TRUE) / n
-
-# garbage collection not performed immediately before the timing
-t1_systime_nogc <- system.time(for (i in 1:n) sqrt(x), gcFirst = FALSE) / n
-t2_systime_nogc <- system.time(for (i in 1:n) x^0.5, gcFirst = FALSE) / n
-
-t_systime_df <- tibble(
-  "expression" = bench_df$expression,
-  "systime_with_gc" = c(t1_systime_gc["elapsed"], t2_systime_gc["elapsed"]),
-  "systime_with_nogc" = c(t1_systime_nogc["elapsed"], t2_systime_nogc["elapsed"])
-) %>%
-  mutate(
-    systime_with_gc = systime_with_gc * 1e6, # in microseconds
-    systime_with_nogc = systime_with_nogc * 1e6 # in microseconds
-  )
-

Now we can compare results from these alternatives:

-
-# note that system time columns report time in microseconds
-full_join(t_bench_df, t_systime_df, by = "expression")
-#> # A tibble: 2 Γ— 4
-#>   expression bench_mean systime_with_gc systime_with_nogc
-#>   <bch:expr>   <bch:tm>           <dbl>             <dbl>
-#> 1 sqrt(x)      767.29ns           0.632             0.665
-#> 2 x^0.5          2.45Β΅s           2.27              2.33
-

The comparison reveals that these two approaches yield quite similar results. Slight differences in exact values is possibly due to differences in the precision of timers used internally by these functions.

-
-

Q2. Here are two other ways to compute the square root of a vector. Which do you think will be fastest? Which will be slowest? Use microbenchmarking to test your answers.

-
-x^(1 / 2)
-exp(log(x) / 2)
-
-

A2. Microbenchmarking all ways to compute square root of a vector mentioned in this chapter.

-
-x <- runif(1000)
-
-bench::mark(
-  sqrt(x),
-  x^0.5,
-  x^(1 / 2),
-  exp(log(x) / 2),
-  iterations = 1000
-) %>%
-  select(expression, median) %>%
-  arrange(median)
-#> # A tibble: 4 Γ— 2
-#>   expression      median
-#>   <bch:expr>    <bch:tm>
-#> 1 sqrt(x)         2.79Β΅s
-#> 2 exp(log(x)/2)   9.51Β΅s
-#> 3 x^(1/2)         17.9Β΅s
-#> 4 x^0.5          18.53Β΅s
-

The specialized primitive function sqrt() (written in C) is the fastest way to compute square root.

-
-
-
-

-23.3 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    assertthat    0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bench         1.1.2      2021-11-30 [1] CRAN (R 4.2.0)
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    DBI           1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    dplyr       * 1.0.10     2022-09-01 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    htmlwidgets   1.5.4      2021-09-08 [1] CRAN (R 4.2.0)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    profmem       0.6.0      2020-12-13 [1] CRAN (R 4.2.0)
-#>    profvis     * 0.3.7      2020-11-02 [1] CRAN (R 4.2.0)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang         1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble        3.1.8.9002 2022-10-16 [1] local
-#>    tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/names-and-values.html b/_book/names-and-values.html deleted file mode 100644 index 1645f6ea..00000000 --- a/_book/names-and-values.html +++ /dev/null @@ -1,677 +0,0 @@ - - - - - - -Chapter 2 Names and values | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-2 Names and values -

-

Loading the needed libraries:

- -
-

-2.1 Binding basics (Exercise 2.2.2) -

-
-

Q1. Explain the relationship between a, b, c and d in the following code:

-
-a <- 1:10
-b <- a
-c <- b
-d <- 1:10
-

A1. The names (a, b, and c) have same values and point to the same object in memory, as can be seen by their identical memory addresses:

-
-obj_addrs <- obj_addrs(list(a, b, c))
-unique(obj_addrs)
-#> [1] "0x11a944a10"
-

Except d, which is a different object, even if it has the same value as a, b, and c:

-
-obj_addr(d)
-#> [1] "0x11a748978"
-
-

Q2. The following code accesses the mean function in multiple ways. Do they all point to the same underlying function object? Verify this with lobstr::obj_addr().

-
-mean
-base::mean
-get("mean")
-evalq(mean)
-match.fun("mean")
-

A2. All listed function calls point to the same underlying function object in memory, as shown by this object’s memory address:

-
-obj_addrs <- obj_addrs(list(
-  mean,
-  base::mean,
-  get("mean"),
-  evalq(mean),
-  match.fun("mean")
-))
-
-unique(obj_addrs)
-#> [1] "0x11a25e2d8"
-
-

Q3. By default, base R data import functions, like read.csv(), will automatically convert non-syntactic names to syntactic ones. Why might this be problematic? What option allows you to suppress this behaviour?

-

A3. The conversion of non-syntactic names to syntactic ones can sometimes corrupt the data. Some datasets may require non-syntactic names.

-

To suppress this behavior, one can set check.names = FALSE.

-
-

Q4. What rules does make.names() use to convert non-syntactic names into syntactic ones?

-

A4. make.names() uses following rules to convert non-syntactic names into syntactic ones:

-
    -
  • it prepends non-syntactic names with X -
  • -
  • it converts invalid characters (like @) to . -
  • -
  • it adds a . as a suffix if the name is a reserved keyword -
  • -
-
-make.names(c("123abc", "@me", "_yu", "  gh", "else"))
-#> [1] "X123abc" "X.me"    "X_yu"    "X..gh"   "else."
-
-

Q5. I slightly simplified the rules that govern syntactic names. Why is .123e1 not a syntactic name? Read ?make.names for the full details.

-

A5. .123e1 is not a syntacti name because it is parsed as a number, and not as a string:

-
-typeof(.123e1)
-#> [1] "double"
-

And as the docs mention (emphasis mine):

-
-

A syntactically valid name consists of letters, numbers and the dot or underline characters and starts with a letter or the dot not followed by a number.

-
-
-
-
-

-2.2 Copy-on-modify (Exercise 2.3.6) -

-
-

Q1. Why is tracemem(1:10) not useful?

-

A1. tracemem() traces copying of objects in R. For example:

-
-x <- 1:10
-
-tracemem(x)
-#> [1] "<0x107924860>"
-
-x <- x + 1
-
-untracemem(x)
-

But since the object created in memory by 1:10 is not assigned a name, it can’t be addressed or modified from R, and so there is nothing to trace.

-
-obj_addr(1:10)
-#> [1] "0x1188da070"
-
-tracemem(1:10)
-#> [1] "<0x1189377b0>"
-
-

Q2. Explain why tracemem() shows two copies when you run this code. Hint: carefully look at the difference between this code and the code shown earlier in the section.

-
-x <- c(1L, 2L, 3L)
-tracemem(x)
-
-x[[3]] <- 4
-untracemem(x)
-

A2. This is because the initial atomic vector is of type integer, but 4 (and not 4L) is of type double. This is why a new copy is created.

-
-x <- c(1L, 2L, 3L)
-typeof(x)
-#> [1] "integer"
-tracemem(x)
-#> [1] "<0x106e8d248>"
-
-x[[3]] <- 4
-#> tracemem[0x106e8d248 -> 0x105c18848]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call <Anonymous> evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file <Anonymous> <Anonymous> do.call eval eval eval eval eval.parent local 
-#> tracemem[0x105c18848 -> 0x105c2ca88]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call <Anonymous> evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file <Anonymous> <Anonymous> do.call eval eval eval eval eval.parent local
-untracemem(x)
-
-typeof(x)
-#> [1] "double"
-

Trying with an integer should not create another copy:

-
-x <- c(1L, 2L, 3L)
-typeof(x)
-#> [1] "integer"
-tracemem(x)
-#> [1] "<0x107ac8348>"
-
-x[[3]] <- 4L
-#> tracemem[0x107ac8348 -> 0x118c7a9c8]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call <Anonymous> evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file <Anonymous> <Anonymous> do.call eval eval eval eval eval.parent local
-untracemem(x)
-
-typeof(x)
-#> [1] "integer"
-

To understand why this still produces a copy, here is an explanation from the official solutions manual:

-
-

Please be aware that running this code in RStudio will result in additional copies because of the reference from the environment pane.

-
-
-

Q3. Sketch out the relationship between the following objects:

-
-a <- 1:10
-b <- list(a, a)
-c <- list(b, a, 1:10)
-

A3. We can understand the relationship between these objects by looking at their memory addresses:

-
-a <- 1:10
-b <- list(a, a)
-c <- list(b, a, 1:10)
-
-ref(a)
-#> [1:0x107d1fd98] <int>
-
-ref(b)
-#> β–ˆ [1:0x107d61d88] <list> 
-#> β”œβ”€[2:0x107d1fd98] <int> 
-#> └─[2:0x107d1fd98]
-
-ref(c)
-#> β–ˆ [1:0x107d6aa78] <list> 
-#> β”œβ”€β–ˆ [2:0x107d61d88] <list> 
-#> β”‚ β”œβ”€[3:0x107d1fd98] <int> 
-#> β”‚ └─[3:0x107d1fd98] 
-#> β”œβ”€[3:0x107d1fd98] 
-#> └─[4:0x106f78ca8] <int>
-

Here is what we learn:

-
    -
  • The name a references object 1:10 in the memory.
  • -
  • The name b is bound to a list of two references to the memory address of a.
  • -
  • The name c is also bound to a list of references to a and b, and 1:10 object (not bound to any name).
  • -
-
-

Q4. What happens when you run this code?

-
-x <- list(1:10)
-x[[2]] <- x
-

Draw a picture.

-

A4.

-
-x <- list(1:10)
-x
-#> [[1]]
-#>  [1]  1  2  3  4  5  6  7  8  9 10
-obj_addr(x)
-#> [1] "0x106577798"
-
-x[[2]] <- x
-x
-#> [[1]]
-#>  [1]  1  2  3  4  5  6  7  8  9 10
-#> 
-#> [[2]]
-#> [[2]][[1]]
-#>  [1]  1  2  3  4  5  6  7  8  9 10
-obj_addr(x)
-#> [1] "0x1188dd148"
-
-ref(x)
-#> β–ˆ [1:0x1188dd148] <list> 
-#> β”œβ”€[2:0x10658cc00] <int> 
-#> β””β”€β–ˆ [3:0x106577798] <list> 
-#>   └─[2:0x10658cc00]
-

I don’t have access to OmniGraffle software, so I am including here the figure from the official solution manual:

-
-
-
-
-

-2.3 Object size (Exercise 2.4.1) -

-
-

Q1. In the following example, why are object.size(y) and obj_size(y) so radically different? Consult the documentation of object.size().

-
-y <- rep(list(runif(1e4)), 100)
-
-object.size(y)
-obj_size(y)
-

A1. As mentioned in the docs for object.size():

-
-

This function…does not detect if elements of a list are shared.

-
-

This is why the sizes are so different:

-
-y <- rep(list(runif(1e4)), 100)
-
-object.size(y)
-#> 8005648 bytes
-
-obj_size(y)
-#> 80.90 kB
-
-

Q2. Take the following list. Why is its size somewhat misleading?

-
-funs <- list(mean, sd, var)
-obj_size(funs)
-

A2. These functions are not externally created objects in R, but are always available as part of base packages, so doesn’t make much sense to measure their size because they are never going to be not available.

-
-funs <- list(mean, sd, var)
-obj_size(funs)
-#> 17.55 kB
-
-

Q3. Predict the output of the following code:

-
-a <- runif(1e6)
-obj_size(a)
-
-b <- list(a, a)
-obj_size(b)
-obj_size(a, b)
-
-b[[1]][[1]] <- 10
-obj_size(b)
-obj_size(a, b)
-
-b[[2]][[1]] <- 10
-obj_size(b)
-obj_size(a, b)
-

A3. Correctly predicted πŸ˜‰

-
-a <- runif(1e6)
-obj_size(a)
-#> 8.00 MB
-
-b <- list(a, a)
-obj_size(b)
-#> 8.00 MB
-obj_size(a, b)
-#> 8.00 MB
-
-b[[1]][[1]] <- 10
-obj_size(b)
-#> 16.00 MB
-obj_size(a, b)
-#> 16.00 MB
-
-b[[2]][[1]] <- 10
-obj_size(b)
-#> 16.00 MB
-obj_size(a, b)
-#> 24.00 MB
-

Key pieces of information to keep in mind to make correct predictions:

-
    -
  • Size of empty vector
  • -
-
-obj_size(double())
-#> 48 B
-
    -
  • Size of a single double: 8 bytes
  • -
-
-obj_size(double(1))
-#> 56 B
-
    -
  • Copy-on-modify semantics
  • -
-
-
-
-

-2.4 Modify-in-place (Exercise 2.5.3) -

-
-

Q1. Explain why the following code doesn’t create a circular list.

-
-x <- list()
-x[[1]] <- x
-

A1. Copy-on-modify prevents the creation of a circular list.

-
-x <- list()
-
-obj_addr(x)
-#> [1] "0x106c2ac38"
-
-tracemem(x)
-#> [1] "<0x106c2ac38>"
-
-x[[1]] <- x
-#> tracemem[0x106c2ac38 -> 0x12a7d3a50]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call <Anonymous> evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file <Anonymous> <Anonymous> do.call eval eval eval eval eval.parent local
-
-obj_addr(x[[1]])
-#> [1] "0x106c2ac38"
-
-

Q2. Wrap the two methods for subtracting medians into two functions, then use the β€˜bench’ package to carefully compare their speeds. How does performance change as the number of columns increase?

-

A2. Let’s first microbenchmark functions that do and do not create copies for varying lengths of number of columns.

-
-library(bench)
-library(tidyverse)
-
-generateDataFrame <- function(ncol) {
-  as.data.frame(matrix(runif(100 * ncol), nrow = 100))
-}
-
-withCopy <- function(ncol) {
-  x <- generateDataFrame(ncol)
-  medians <- vapply(x, median, numeric(1))
-
-  for (i in seq_along(medians)) {
-    x[[i]] <- x[[i]] - medians[[i]]
-  }
-
-  return(x)
-}
-
-withoutCopy <- function(ncol) {
-  x <- generateDataFrame(ncol)
-  medians <- vapply(x, median, numeric(1))
-
-  y <- as.list(x)
-
-  for (i in seq_along(medians)) {
-    y[[i]] <- y[[i]] - medians[[i]]
-  }
-
-  return(y)
-}
-
-benchComparison <- function(ncol) {
-  bench::mark(
-    withCopy(ncol),
-    withoutCopy(ncol),
-    iterations = 100,
-    check = FALSE
-  ) %>%
-    dplyr::select(expression:total_time)
-}
-
-nColList <- list(1, 10, 50, 100, 250, 500, 1000)
-
-names(nColList) <- as.character(nColList)
-
-benchDf <- purrr::map_dfr(
-  .x = nColList,
-  .f = benchComparison,
-  .id = "nColumns"
-)
-

Plotting these benchmarks reveals how the performance gets increasingly worse as the number of data frames increases:

-
-ggplot(
-  benchDf,
-  aes(
-    x = as.numeric(nColumns),
-    y = median,
-    group = as.character(expression),
-    color = as.character(expression)
-  )
-) +
-  geom_line() +
-  labs(
-    x = "Number of Columns",
-    y = "Median Execution Time (ms)",
-    colour = "Type of function"
-  )
-
-
-

Q3. What happens if you attempt to use tracemem() on an environment?

-

A3. It doesn’t work and the documentation for tracemem() makes it clear why:

-
-

It is not useful to trace NULL, environments, promises, weak references, or external pointer objects, as these are not duplicated

-
-
-e <- rlang::env(a = 1, b = "3")
-tracemem(e)
-#> Error in tracemem(e): 'tracemem' is not useful for promise and environment objects
-
-
-
-

-2.5 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package       * version    date (UTC) lib source
-#>    assertthat      0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    backports       1.4.1      2021-12-13 [1] CRAN (R 4.2.0)
-#>    base          * 4.2.2      2022-10-31 [?] local
-#>    bench         * 1.1.2      2021-11-30 [1] CRAN (R 4.2.0)
-#>    bookdown        0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    broom           1.0.1      2022-08-29 [1] CRAN (R 4.2.0)
-#>    bslib           0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem          1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cellranger      1.1.0      2016-07-27 [1] CRAN (R 4.2.0)
-#>    cli             3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>    colorspace      2.0-3      2022-02-21 [1] CRAN (R 4.2.0)
-#>  P compiler        4.2.2      2022-10-31 [1] local
-#>    crayon          1.5.2      2022-09-29 [1] CRAN (R 4.2.1)
-#>  P datasets      * 4.2.2      2022-10-31 [1] local
-#>    DBI             1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)
-#>    dbplyr          2.2.1      2022-06-27 [1] CRAN (R 4.2.0)
-#>    digest          0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit         0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    dplyr         * 1.0.10     2022-09-01 [1] CRAN (R 4.2.1)
-#>    ellipsis        0.3.2      2021-04-29 [1] CRAN (R 4.2.0)
-#>    evaluate        0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi           1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    farver          2.1.1      2022-07-06 [1] CRAN (R 4.2.1)
-#>    fastmap         1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    forcats       * 0.5.2      2022-08-19 [1] CRAN (R 4.2.1)
-#>    fs              1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    gargle          1.2.1      2022-09-08 [1] CRAN (R 4.2.1)
-#>    generics        0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    ggplot2       * 3.4.0      2022-11-04 [1] CRAN (R 4.2.2)
-#>    glue            1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>    googledrive     2.0.0      2021-07-08 [1] CRAN (R 4.2.0)
-#>    googlesheets4   1.0.1      2022-08-13 [1] CRAN (R 4.2.0)
-#>  P graphics      * 4.2.2      2022-10-31 [1] local
-#>  P grDevices     * 4.2.2      2022-10-31 [1] local
-#>  P grid            4.2.2      2022-10-31 [1] local
-#>    gtable          0.3.1      2022-09-01 [1] CRAN (R 4.2.1)
-#>    haven           2.5.1      2022-08-22 [1] CRAN (R 4.2.0)
-#>    highr           0.9        2021-04-16 [1] CRAN (R 4.2.0)
-#>    hms             1.1.2      2022-08-19 [1] CRAN (R 4.2.0)
-#>    htmltools       0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    httr            1.4.4      2022-08-17 [1] CRAN (R 4.2.0)
-#>    jquerylib       0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite        1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr           1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    labeling        0.4.2      2020-10-20 [1] CRAN (R 4.2.0)
-#>    lifecycle       1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    lobstr        * 1.1.2      2022-06-22 [1] CRAN (R 4.2.0)
-#>    lubridate       1.9.0      2022-11-06 [1] CRAN (R 4.2.2)
-#>    magrittr      * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise         2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods       * 4.2.2      2022-10-31 [1] local
-#>    modelr          0.1.10     2022-11-11 [1] CRAN (R 4.2.2)
-#>    munsell         0.5.0      2018-06-12 [1] CRAN (R 4.2.0)
-#>    pillar          1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig       2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    prettyunits     1.1.1      2020-01-24 [1] CRAN (R 4.2.0)
-#>    profmem         0.6.0      2020-12-13 [1] CRAN (R 4.2.0)
-#>    purrr         * 0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6              2.5.1.9000 2022-10-27 [1] local
-#>    readr         * 2.1.3      2022-10-01 [1] CRAN (R 4.2.1)
-#>    readxl          1.4.1      2022-08-17 [1] CRAN (R 4.2.0)
-#>    reprex          2.0.2      2022-08-17 [1] CRAN (R 4.2.1)
-#>    rlang           1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown       2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi      0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    rvest           1.0.3      2022-08-19 [1] CRAN (R 4.2.1)
-#>    sass            0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    scales          1.2.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    sessioninfo     1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats         * 4.2.2      2022-10-31 [1] local
-#>    stringi         1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       * 1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble        * 3.1.8.9002 2022-10-16 [1] local
-#>    tidyr         * 1.2.1      2022-09-08 [1] CRAN (R 4.2.1)
-#>    tidyselect      1.2.0      2022-10-10 [1] CRAN (R 4.2.1)
-#>    tidyverse     * 1.3.2      2022-07-18 [1] CRAN (R 4.2.0)
-#>    timechange      0.1.1      2022-11-04 [1] CRAN (R 4.2.2)
-#>  P tools           4.2.2      2022-10-31 [1] local
-#>    tzdb            0.3.0      2022-03-28 [1] CRAN (R 4.2.0)
-#>    utf8            1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils         * 4.2.2      2022-10-31 [1] local
-#>    vctrs           0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr           2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun            0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2            1.3.3.9000 2022-10-10 [1] local
-#>    yaml            2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/quasiquotation.html b/_book/quasiquotation.html deleted file mode 100644 index 719445aa..00000000 --- a/_book/quasiquotation.html +++ /dev/null @@ -1,892 +0,0 @@ - - - - - - -Chapter 19 Quasiquotation | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-19 Quasiquotation -

-

Attaching the needed libraries:

- -
-

-19.1 Motivation (Exercises 19.2.2) -

-
-

Q1. For each function in the following base R code, identify which arguments are quoted and which are evaluated.

-
-library(MASS)
-
-mtcars2 <- subset(mtcars, cyl == 4)
-
-with(mtcars2, sum(vs))
-sum(mtcars2$am)
-
-rm(mtcars2)
-

A1. To identify which arguments are quoted and which are evaluated, we can use the trick mentioned in the book:

-
-

If you’re ever unsure about whether an argument is quoted or evaluated, try executing the code outside of the function. If it doesn’t work or does something different, then that argument is quoted.

-
- -

The package argument in library() is quoted:

-
-library(MASS)
-
-MASS
-#> Error in eval(expr, envir, enclos): object 'MASS' not found
-
    -
  • subset(mtcars, cyl == 4)
  • -
-

The argument x is evaluated, while the argument subset is quoted.

-
-mtcars2 <- subset(mtcars, cyl == 4)
-
-invisible(mtcars)
-
-cyl == 4
-#> Error in eval(expr, envir, enclos): object 'cyl' not found
-
    -
  • with(mtcars2, sum(vs))
  • -
-

The argument data is evaluated, while expr argument is quoted.

-
-with(mtcars2, sum(vs))
-#> [1] 10
-
-invisible(mtcars2)
-
-sum(vs)
-#> Error in eval(expr, envir, enclos): object 'vs' not found
-
    -
  • sum(mtcars2$am)
  • -
-

The argument ... is evaluated.

-
-sum(mtcars2$am)
-#> [1] 8
-
-mtcars2$am
-#>  [1] 1 0 0 1 1 1 0 1 1 1 1
-
    -
  • rm(mtcars2)
  • -
-

The trick we are using so far won’t work here since trying to print mtcars2 will always fail after rm() has made a pass at it.

-
-rm(mtcars2)
-

We can instead look at the docs for ...:

-
-

… the objects to be removed, as names (unquoted) or character strings (quoted).

-
-

Thus, this argument is not evaluated, but rather quoted.

-
-

Q2. For each function in the following tidyverse code, identify which arguments are quoted and which are evaluated.

-
-library(dplyr)
-library(ggplot2)
-
-by_cyl <- mtcars %>%
-  group_by(cyl) %>%
-  summarise(mean = mean(mpg))
-
-ggplot(by_cyl, aes(cyl, mean)) +
-  geom_point()
-

A2. As seen in the answer for Q1., library() quotes its first argument:

- -

In the following code:

- -
-by_cyl <- mtcars %>%
-  group_by(cyl) %>%
-  summarise(mean = mean(mpg))
-

In the following code:

-
    -
  • -ggplot() evaluates the data argument
  • -
  • -aes() quotes its arguments
  • -
-
-ggplot(by_cyl, aes(cyl, mean)) +
-  geom_point()
-
-
-
-
-

-19.2 Quoting (Exercises 19.3.6) -

-
-

Q1. How is expr() implemented? Look at its source code.

-

A1. Looking at the source code, we can see that expr() is a simple wrapper around enexpr(), and captures and returns the user-entered expressions:

-
-rlang::expr
-#> function (expr) 
-#> {
-#>     enexpr(expr)
-#> }
-#> <bytecode: 0x10de65a50>
-#> <environment: namespace:rlang>
-

For example:

-
-x <- expr(x <- 1)
-x
-#> x <- 1
-

In its turn, enexpr() calls native code:

-
-rlang::enexpr
-#> function (arg) 
-#> {
-#>     .Call(ffi_enexpr, substitute(arg), parent.frame())
-#> }
-#> <bytecode: 0x108bbaa68>
-#> <environment: namespace:rlang>
-
-

Q2. Compare and contrast the following two functions. Can you predict the output before running them?

-
-f1 <- function(x, y) {
-  exprs(x = x, y = y)
-}
-f2 <- function(x, y) {
-  enexprs(x = x, y = y)
-}
-f1(a + b, c + d)
-f2(a + b, c + d)
-

A2. The exprs() captures and returns the expressions specified by the developer instead of their values:

-
-f1 <- function(x, y) {
-  exprs(x = x, y = y)
-}
-
-f1(a + b, c + d)
-#> $x
-#> x
-#> 
-#> $y
-#> y
-

On the other hand, enexprs() captures the user-entered expressions and returns their values:

-
-f2 <- function(x, y) {
-  enexprs(x = x, y = y)
-}
-
-f2(a + b, c + d)
-#> $x
-#> a + b
-#> 
-#> $y
-#> c + d
-
-

Q3. What happens if you try to use enexpr() with an expression (i.e.Β enexpr(x + y)? What happens if enexpr() is passed a missing argument?

-

A3. If you try to use enexpr() with an expression, it fails because it works only with symbol.

-
-enexpr(x + y)
-#> Error in `enexpr()`:
-#> ! `arg` must be a symbol
-

If enexpr() is passed a missing argument, it returns a missing argument:

-
-arg <- missing_arg()
-
-enexpr(arg)
-
-is_missing(enexpr(arg))
-#> [1] TRUE
-
-

Q4. How are exprs(a) and exprs(a = ) different? Think about both the input and the output.

-

A4. The key difference between exprs(a) and exprs(a = ) is that the former will return an unnamed list, while the latter will return a named list. This is because the former is interpreted as an unnamed argument, while the latter a named argument.

-
-exprs(a)
-#> [[1]]
-#> a
-
-exprs(a = )
-#> $a
-

In both cases, a is treated as a symbol:

-
-map_lgl(exprs(a), is_symbol)
-#>      
-#> TRUE
-
-map_lgl(exprs(a = ), is_symbol)
-#>    a 
-#> TRUE
-

But, the argument is missing only in the latter case, since only the name but no corresponding value is provided:

-
-map_lgl(exprs(a), is_missing)
-#>       
-#> FALSE
-
-map_lgl(exprs(a = ), is_missing)
-#>    a 
-#> TRUE
-
-

Q5. What are other differences between exprs() and alist()? Read the documentation for the named arguments of exprs() to find out.

-

A5. Here are some additional differences between exprs() and alist().

-
    -
  • Names: If the inputs are not named, exprs() provides a way to name them automatically using .named argument.
  • -
-
-alist("x" = 1, TRUE, "z" = expr(x + y))
-#> $x
-#> [1] 1
-#> 
-#> [[2]]
-#> [1] TRUE
-#> 
-#> $z
-#> expr(x + y)
-
-exprs("x" = 1, TRUE, "z" = expr(x + y), .named = TRUE)
-#> $x
-#> [1] 1
-#> 
-#> $`TRUE`
-#> [1] TRUE
-#> 
-#> $z
-#> expr(x + y)
-
    -
  • Ignoring empty arguments: The .ignore_empty argument in exprs() gives you a much finer control over what to do with the empty arguments, while alist() doesn’t provide a way to ignore such arguments.
  • -
-
-alist("x" = 1, , TRUE, )
-#> $x
-#> [1] 1
-#> 
-#> [[2]]
-#> 
-#> 
-#> [[3]]
-#> [1] TRUE
-#> 
-#> [[4]]
-
-exprs("x" = 1, , TRUE, , .ignore_empty = "trailing")
-#> $x
-#> [1] 1
-#> 
-#> [[2]]
-#> 
-#> 
-#> [[3]]
-#> [1] TRUE
-
-exprs("x" = 1, , TRUE, , .ignore_empty = "none")
-#> $x
-#> [1] 1
-#> 
-#> [[2]]
-#> 
-#> 
-#> [[3]]
-#> [1] TRUE
-#> 
-#> [[4]]
-
-exprs("x" = 1, , TRUE, , .ignore_empty = "all")
-#> $x
-#> [1] 1
-#> 
-#> [[2]]
-#> [1] TRUE
-
    -
  • Names injection: Using .unquote_names argument in exprs(), we can inject a name for the argument.
  • -
-
-alist(foo := bar)
-#> [[1]]
-#> `:=`(foo, bar)
-
-exprs(foo := bar, .unquote_names = FALSE)
-#> [[1]]
-#> `:=`(foo, bar)
-
-exprs(foo := bar, .unquote_names = TRUE)
-#> $foo
-#> bar
-
-

Q6. The documentation for substitute() says:

-
-

Substitution takes place by examining each component of the parse tree -as follows:

-
    -
  • If it is not a bound symbol in env, it is unchanged.
  • -
  • If it is a promise object (i.e., a formal argument to a function) -the expression slot of the promise replaces the symbol.
  • -
  • If it is an ordinary variable, its value is substituted, unless -env is .GlobalEnv in which case the symbol is left unchanged.
  • -
-
-

Create examples that illustrate each of the above cases.

-

A6. See below examples that illustrate each of the above-mentioned cases.

-
-

If it is not a bound symbol in env, it is unchanged.

-
-

Symbol x is not bound in env, so it remains unchanged.

-
-substitute(x + y, env = list(y = 2))
-#> x + 2
-
-

If it is a promise object (i.e., a formal argument to a function) -the expression slot of the promise replaces the symbol.

-
-
-msg <- "old"
-delayedAssign("myVar", msg) # creates a promise
-substitute(myVar)
-#> myVar
-msg <- "new!"
-myVar
-#> [1] "new!"
-
-

If it is an ordinary variable, its value is substituted, unless -env is .GlobalEnv in which case the symbol is left unchanged.

-
-
-substitute(x + y, env = env(x = 2, y = 1))
-#> 2 + 1
-
-x <- 2
-y <- 1
-substitute(x + y, env = .GlobalEnv)
-#> x + y
-
-
-
-

-19.3 Unquoting (Exercises 19.4.8) -

-
-

Q1. Given the following components:

-
-xy <- expr(x + y)
-xz <- expr(x + z)
-yz <- expr(y + z)
-abc <- exprs(a, b, c)
-

Use quasiquotation to construct the following calls:

-
-(x + y) / (y + z)
--(x + z)^(y + z)
-(x + y) + (y + z) - (x + y)
-atan2(x + y, y + z)
-sum(x + y, x + y, y + z)
-sum(a, b, c)
-mean(c(a, b, c), na.rm = TRUE)
-foo(a = x + y, b = y + z)
-

A1. Using quasiquotation to construct the specified calls:

-
-xy <- expr(x + y)
-xz <- expr(x + z)
-yz <- expr(y + z)
-abc <- exprs(a, b, c)
-
-expr((!!xy) / (!!yz))
-#> (x + y)/(y + z)
-
-expr(-(!!xz)^(!!yz))
-#> -(x + z)^(y + z)
-
-expr(((!!xy)) + (!!yz) - (!!xy))
-#> (x + y) + (y + z) - (x + y)
-
-call2("atan2", expr(!!xy), expr(!!yz))
-#> atan2(x + y, y + z)
-
-call2("sum", expr(!!xy), expr(!!xy), expr(!!yz))
-#> sum(x + y, x + y, y + z)
-
-call2("sum", !!!abc)
-#> sum(a, b, c)
-
-expr(mean(c(!!!abc), na.rm = TRUE))
-#> mean(c(a, b, c), na.rm = TRUE)
-
-call2("foo", a = expr(!!xy), b = expr(!!yz))
-#> foo(a = x + y, b = y + z)
-
-

Q2. The following two calls print the same, but are actually different:

-
-(a <- expr(mean(1:10)))
-#> mean(1:10)
-(b <- expr(mean(!!(1:10))))
-#> mean(1:10)
-identical(a, b)
-#> [1] FALSE
-

What’s the difference? Which one is more natural?

-

A2. We can see the difference between these two expression if we convert them to lists:

-
-as.list(expr(mean(1:10)))
-#> [[1]]
-#> mean
-#> 
-#> [[2]]
-#> 1:10
-
-as.list(expr(mean(!!(1:10))))
-#> [[1]]
-#> mean
-#> 
-#> [[2]]
-#>  [1]  1  2  3  4  5  6  7  8  9 10
-

As can be seen, the second element of a is a call object, while that in b is an integer vector:

-
-waldo::compare(a, b)
-#> `old[[2]]` is a call
-#> `new[[2]]` is an integer vector (1, 2, 3, 4, 5, ...)
-

The same can also be noticed in ASTs for these expressions:

-
-ast(expr(mean(1:10)))
-#> β–ˆβ”€expr 
-#> β””β”€β–ˆβ”€mean 
-#>   β””β”€β–ˆβ”€`:` 
-#>     β”œβ”€1 
-#>     └─10
-
-ast(expr(mean(!!(1:10))))
-#> β–ˆβ”€expr 
-#> β””β”€β–ˆβ”€mean 
-#>   └─<inline integer>
-

The first call is more natural, since the second one inlines a vector directly into the call, something that is rarely done.

-
-
-
-

-19.4 ... (dot-dot-dot) (Exercises 19.6.5) -

-
-

Q1. One way to implement exec() is shown below. Describe how it works. What are the key ideas?

-
-exec <- function(f, ..., .env = caller_env()) {
-  args <- list2(...)
-  do.call(f, args, envir = .env)
-}
-

A1. The keys ideas that underlie this implementation of exec() function are the following:

-
    -
  • It constructs a call using function f and its argument ..., and evaluates the call in the environment .env.

  • -
  • It uses dynamic dots via list2(), which means that you can splice arguments using !!!, you can inject names using :=, and trailing commas are not a problem.

  • -
-

Here is an example:

-
-vec <- c(1:5, NA)
-args_list <- list(trim = 0, na.rm = TRUE)
-
-exec(mean, vec, !!!args_list, , .env = caller_env())
-#> [1] 3
-
-rm("exec")
-
-

Q2. Carefully read the source code for interaction(), expand.grid(), and par(). Compare and contrast the techniques they use for switching between dots and list behaviour.

-

A2. Source code reveals the following comparison table:

-
----- - - - - - - - - - - - - - - - - - - - - - - -
FunctionCapture the dotsHandle list input
interaction()args <- list(...)length(args) == 1L && is.list(args[[1L]])
expand.grid()args <- list(...)length(args) == 1L && is.list(args[[1L]])
par()args <- list(...)length(args) == 1L && (is.list(args[[1L]] || is.null(args[[1L]])))
-

All functions capture the dots in a list.

-

Using these dots, the functions check:

-
    -
  • if a list was entered as an argument by checking the number of arguments
  • -
  • if the count is 1, by checking if the argument is a list
  • -
-
-

Q3. Explain the problem with this definition of set_attr()

-
-set_attr <- function(x, ...) {
-  attr <- rlang::list2(...)
-  attributes(x) <- attr
-  x
-}
-set_attr(1:10, x = 10)
-#> Error in attributes(x) <- attr: attributes must be named
-

A3. The set_attr() function signature has a parameter called x, and additionally it uses dynamic dots to pass multiple arguments to specify additional attributes for x.

-

But, as shown in the example, this creates a problem when the attribute is itself named x. Naming the arguments won’t help either:

-
-set_attr <- function(x, ...) {
-  attr <- rlang::list2(...)
-  attributes(x) <- attr
-  x
-}
-set_attr(x = 1:10, x = 10)
-#> Error in set_attr(x = 1:10, x = 10): formal argument "x" matched by multiple actual arguments
-

We can avoid these issues by renaming the parameter:

-
-set_attr <- function(.x, ...) {
-  attr <- rlang::list2(...)
-  attributes(.x) <- attr
-  .x
-}
-
-set_attr(.x = 1:10, x = 10)
-#>  [1]  1  2  3  4  5  6  7  8  9 10
-#> attr(,"x")
-#> [1] 10
-
-
-
-

-19.5 Case studies (Exercises 19.7.5) -

-
-

Q1. In the linear-model example, we could replace the expr() in reduce(summands, ~ expr(!!.x + !!.y)) with call2(): reduce(summands, call2, "+"). Compare and contrast the two approaches. Which do you think is easier to read?

-

A1. We can rewrite the linear() function from this chapter using call2() as follows:

-
-linear <- function(var, val) {
-  var <- ensym(var)
-  coef_name <- map(seq_along(val[-1]), ~ expr((!!var)[[!!.x]]))
-
-  summands <- map2(val[-1], coef_name, ~ expr((!!.x * !!.y)))
-  summands <- c(val[[1]], summands)
-
-  reduce(summands, ~ call2("+", .x, .y))
-}
-
-linear(x, c(10, 5, -4))
-#> 10 + (5 * x[[1L]]) + (-4 * x[[2L]])
-

I personally find the version with call2() to be much more readable since the !! syntax is a bit esoteric.

-
-

Q2. Re-implement the Box-Cox transform defined below using unquoting and new_function():

-
-bc <- function(lambda) {
-  if (lambda == 0) {
-    function(x) log(x)
-  } else {
-    function(x) (x^lambda - 1) / lambda
-  }
-}
-

A2. Re-implementation of the Box-Cox transform using unquoting and new_function():

-
-bc_new <- function(lambda) {
-  lambda <- enexpr(lambda)
-
-  if (!!lambda == 0) {
-    new_function(
-      exprs(x = ),
-      expr(log(x))
-    )
-  } else {
-    new_function(
-      exprs(x = ),
-      expr((x^(!!lambda) - 1) / (!!lambda))
-    )
-  }
-}
-

Let’s try it out to see if it produces the same output as before:

-
-bc(0)(1)
-#> [1] 0
-bc_new(0)(1)
-#> [1] 0
-
-bc(2)(2)
-#> [1] 1.5
-bc_new(2)(2)
-#> [1] 1.5
-
-

Q3. Re-implement the simple compose() defined below using quasiquotation and new_function():

-
-compose <- function(f, g) {
-  function(...) f(g(...))
-}
-

A3. Following is a re-implementation of compose() using quasiquotation and new_function():

-
-compose_new <- function(f, g) {
-  f <- enexpr(f) # or ensym(f)
-  g <- enexpr(g) # or ensym(g)
-
-  new_function(
-    exprs(... = ),
-    expr((!!f)((!!g)(...)))
-  )
-}
-

Checking that the new version behaves the same way as the original version:

-
-not_null <- compose(`!`, is.null)
-not_null(4)
-#> [1] TRUE
-
-not_null2 <- compose_new(`!`, is.null)
-not_null2(4)
-#> [1] TRUE
-
-
-
-

-19.6 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    assertthat    0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>    colorspace    2.0-3      2022-02-21 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>    crayon        1.5.2      2022-09-29 [1] CRAN (R 4.2.1)
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    DBI           1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)
-#>    diffobj       0.3.5      2021-10-05 [1] CRAN (R 4.2.0)
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    dplyr       * 1.0.10     2022-09-01 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    farver        2.1.1      2022-07-06 [1] CRAN (R 4.2.1)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    ggplot2     * 3.4.0      2022-11-04 [1] CRAN (R 4.2.2)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>  P grid          4.2.2      2022-10-31 [1] local
-#>    gtable        0.3.1      2022-09-01 [1] CRAN (R 4.2.1)
-#>    highr         0.9        2021-04-16 [1] CRAN (R 4.2.0)
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    labeling      0.4.2      2020-10-20 [1] CRAN (R 4.2.0)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    lobstr      * 1.1.2      2022-06-22 [1] CRAN (R 4.2.0)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    MASS        * 7.3-58.1   2022-08-03 [1] CRAN (R 4.2.2)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    munsell       0.5.0      2018-06-12 [1] CRAN (R 4.2.0)
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    purrr       * 0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rematch2      2.1.2      2020-05-01 [1] CRAN (R 4.2.0)
-#>    rlang       * 1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    scales        1.2.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble        3.1.8.9002 2022-10-16 [1] local
-#>    tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    waldo         0.4.0      2022-03-16 [1] CRAN (R 4.2.0)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/r6.html b/_book/r6.html deleted file mode 100644 index a582906c..00000000 --- a/_book/r6.html +++ /dev/null @@ -1,676 +0,0 @@ - - - - - - -Chapter 14 R6 | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-14 R6 -

-

Loading the needed libraries:

- -
-

-14.1 Classes and methods (Exercises 14.2.6) -

-

Q1. Create a bank account R6 class that stores a balance and allows you to deposit and withdraw money. Create a subclass that throws an error if you attempt to go into overdraft. Create another subclass that allows you to go into overdraft, but charges you a fee. Create the superclass and make sure it works as expected.

-

A1. First, let’s create a bank account R6 class that stores a balance and allows you to deposit and withdraw money:

-
-library(R6)
-
-bankAccount <- R6::R6Class(
-  "bankAccount",
-  public = list(
-    balance = 0,
-    initialize = function(balance) {
-      self$balance <- balance
-    },
-    deposit = function(amount) {
-      self$balance <- self$balance + amount
-      message(paste0("Current balance is: ", self$balance))
-      invisible(self)
-    },
-    withdraw = function(amount) {
-      self$balance <- self$balance - amount
-      message(paste0("Current balance is: ", self$balance))
-      invisible(self)
-    }
-  )
-)
-

Let’s try it out:

-
-indra <- bankAccount$new(balance = 100)
-
-indra$deposit(20)
-#> Current balance is: 120
-
-indra$withdraw(10)
-#> Current balance is: 110
-

Create a subclass that errors if you attempt to overdraw:

-
-bankAccountStrict <- R6::R6Class(
-  "bankAccountStrict",
-  inherit = bankAccount,
-  public = list(
-    withdraw = function(amount) {
-      if (self$balance - amount < 0) {
-        stop(
-          paste0("Can't withdraw more than your current balance: ", self$balance),
-          call. = FALSE
-        )
-      }
-
-      super$withdraw(amount)
-    }
-  )
-)
-

Let’s try it out:

-
-Pritesh <- bankAccountStrict$new(balance = 100)
-
-Pritesh$deposit(20)
-#> Current balance is: 120
-
-Pritesh$withdraw(150)
-#> Error: Can't withdraw more than your current balance: 120
-

Now let’s create a subclass that charges a fee if account is overdrawn:

-
-bankAccountFee <- R6::R6Class(
-  "bankAccountFee",
-  inherit = bankAccount,
-  public = list(
-    withdraw = function(amount) {
-      super$withdraw(amount)
-
-      if (self$balance) {
-        self$balance <- self$balance - 10
-        message("You're withdrawing more than your current balance. You will be charged a fee of 10 euros.")
-      }
-    }
-  )
-)
-

Let’s try it out:

-
-Mangesh <- bankAccountFee$new(balance = 100)
-
-Mangesh$deposit(20)
-#> Current balance is: 120
-
-Mangesh$withdraw(150)
-#> Current balance is: -30
-#> You're withdrawing more than your current balance. You will be charged a fee of 10 euros.
-

Q2. Create an R6 class that represents a shuffled deck of cards. You should be able to draw cards from the deck with $draw(n), and return all cards to the deck and reshuffle with $reshuffle(). Use the following code to make a vector of cards.

-
-suit <- c("β™ ", "β™₯", "♦", "♣")
-value <- c("A", 2:10, "J", "Q", "K")
-cards <- paste0(rep(value, 4), suit)
-

A2. Let’s create needed class that represents a shuffled deck of cards:

-
-suit <- c("β™ ", "β™₯", "♦", "♣")
-value <- c("A", 2:10, "J", "Q", "K")
-cards <- paste(rep(value, 4), suit)
-
-Deck <- R6::R6Class(
-  "Deck",
-  public = list(
-    initialize = function(deck) {
-      private$cards <- sample(deck)
-    },
-    draw = function(n) {
-      if (n > length(private$cards)) {
-        stop(
-          paste0("Can't draw more than remaining number of cards: ", length(private$cards)),
-          call. = FALSE
-        )
-      }
-
-      drawn_cards <- sample(private$cards, n)
-      private$cards <- private$cards[-which(private$cards %in% drawn_cards)]
-      message(paste0("Remaining number of cards: ", length(private$cards)))
-
-      return(drawn_cards)
-    },
-    reshuffle = function() {
-      private$cards <- sample(private$cards)
-      invisible(self)
-    }
-  ),
-  private = list(
-    cards = NULL
-  )
-)
-

Let’s try it out:

-
-myDeck <- Deck$new(cards)
-
-myDeck$draw(4)
-#> Remaining number of cards: 48
-#> [1] "2 β™ "  "10 ♦" "9 ♦"  "3 ♦"
-
-myDeck$reshuffle()$draw(5)
-#> Remaining number of cards: 43
-#> [1] "6 ♦"  "10 β™₯" "2 β™₯"  "A β™₯"  "8 β™₯"
-
-myDeck$draw(50)
-#> Error: Can't draw more than remaining number of cards: 43
-

Q3. Why can’t you model a bank account or a deck of cards with an S3 class?

-

A3. We can’t model a bank account or a deck of cards with an S3 class because instances of these classes are immutable.

-

On the other hand, R6 classes encapsulate data and represent its state, which can change over the course of object’s lifecycle. In other words, these objects are mutable and well-suited to model a bank account.

-

Q4. Create an R6 class that allows you to get and set the current time zone. You can access the current time zone with Sys.timezone() and set it with Sys.setenv(TZ = "newtimezone"). When setting the time zone, make sure the new time zone is in the list provided by OlsonNames().

-

A4. Here is an R6 class that manages the current time zone:

-
-CurrentTimeZone <- R6::R6Class("CurrentTimeZone",
-  public = list(
-    setTimeZone = function(tz) {
-      stopifnot(tz %in% OlsonNames())
-      Sys.setenv(TZ = tz)
-    },
-    getTimeZone = function() {
-      Sys.timezone()
-    }
-  )
-)
-

Let’s try it out:

-
-myCurrentTimeZone <- CurrentTimeZone$new()
-
-myCurrentTimeZone$getTimeZone()
-#> [1] "Europe/Berlin"
-
-myCurrentTimeZone$setTimeZone("Asia/Kolkata")
-myCurrentTimeZone$getTimeZone()
-#> [1] "Europe/Berlin"
-
-myCurrentTimeZone$setTimeZone("Europe/Berlin")
-

Q5. Create an R6 class that manages the current working directory. It should have $get() and $set() methods.

-

A5. Here is an R6 class that manages the current working directory:

-
-ManageDirectory <- R6::R6Class("ManageDirectory",
-  public = list(
-    setWorkingDirectory = function(dir) {
-      setwd(dir)
-    },
-    getWorkingDirectory = function() {
-      getwd()
-    }
-  )
-)
-

Let’s create an instance of this class and check if the methods work as expected:

-
-myDirManager <- ManageDirectory$new()
-
-# current working directory
-myDirManager$getWorkingDirectory()
-
-# change and check if that worked
-myDirManager$setWorkingDirectory("..")
-myDirManager$getWorkingDirectory()
-
-# revert this change
-myDirManager$setWorkingDirectory("/Advanced-R-exercises")
-

Q6. Why can’t you model the time zone or current working directory with an S3 class?

-

A6. Same as answer to Q3:

-

Objects that represent these real-life entities need to be mutable and S3 class instances are not mutable.

-

Q7. What base type are R6 objects built on top of? What attributes do they have?

-

A7. Let’s create an example class and create instance of that class:

-
-Example <- R6::R6Class("Example")
-myExample <- Example$new()
-

The R6 objects are built on top of environment:

-
-typeof(myExample)
-#> [1] "environment"
-
-rlang::env_print(myExample)
-#> <environment: 0x11a89fe68> [L]
-#> Parent: <environment: empty>
-#> Class: Example, R6
-#> Bindings:
-#> β€’ .__enclos_env__: <env>
-#> β€’ clone: <fn> [L]
-

And it has only class attribute, which is a character vector with the "R6" being the last element and the superclasses being other elements:

-
-attributes(myExample)
-#> $class
-#> [1] "Example" "R6"
-
-
-

-14.2 Controlling access (Exercises 14.3.3) -

-

Q1. Create a bank account class that prevents you from directly setting the account balance, but you can still withdraw from and deposit to. Throw an error if you attempt to go into overdraft.

-

A1. Here is a bank account class that satisfies the specified requirements:

-
-SafeBankAccount <- R6::R6Class(
-  classname = "SafeBankAccount",
-  public = list(
-    deposit = function(deposit_amount) {
-      private$.balance <- private$.balance + deposit_amount
-      print(paste("Current balance:", private$.balance))
-
-      invisible(self)
-    },
-    withdraw = function(withdrawal_amount) {
-      if (withdrawal_amount > private$.balance) {
-        stop("You can't withdraw more than your current balance.", call. = FALSE)
-      }
-
-      private$.balance <- private$.balance - withdrawal_amount
-      print(paste("Current balance:", private$.balance))
-
-      invisible(self)
-    }
-  ),
-  private = list(
-    .balance = 0
-  )
-)
-

Let’s check if it works as expected:

-
-mySafeBankAccount <- SafeBankAccount$new()
-
-mySafeBankAccount$deposit(100)
-#> [1] "Current balance: 100"
-
-mySafeBankAccount$withdraw(50)
-#> [1] "Current balance: 50"
-
-mySafeBankAccount$withdraw(100)
-#> Error: You can't withdraw more than your current balance.
-

Q2. Create a class with a write-only $password field. It should have $check_password(password) method that returns TRUE or FALSE, but there should be no way to view the complete password.

-

A2. Here is an implementation of the class with the needed properties:

-
-library(R6)
-
-checkCredentials <- R6Class(
-  "checkCredentials",
-  public = list(
-    # setter
-    set_password = function(password) {
-      private$.password <- password
-    },
-
-    # checker
-    check_password = function(password) {
-      if (is.null(private$.password)) {
-        stop("No password set to check against.")
-      }
-
-      identical(password, private$.password)
-    },
-
-    # the default print method prints the private fields as well
-    print = function() {
-      cat("Password: XXXX")
-
-      # for method chaining
-      invisible(self)
-    }
-  ),
-  private = list(
-    .password = NULL
-  )
-)
-
-myCheck <- checkCredentials$new()
-
-myCheck$set_password("1234")
-print(myCheck)
-#> Password: XXXX
-
-myCheck$check_password("abcd")
-#> [1] FALSE
-myCheck$check_password("1234")
-#> [1] TRUE
-

But, of course, everything is possible:

-
-myCheck$.__enclos_env__$private$.password
-#> [1] "1234"
-

Q3. Extend the Rando class with another active binding that allows you to access the previous random value. Ensure that active binding is the only way to access the value.

-

A3. Here is a modified version of the Rando class to meet the specified requirements:

-
-Rando <- R6::R6Class("Rando",
-  active = list(
-    random = function(value) {
-      if (missing(value)) {
-        newValue <- runif(1)
-        private$.previousRandom <- private$.currentRandom
-        private$.currentRandom <- newValue
-        return(private$.currentRandom)
-      } else {
-        stop("Can't set `$random`", call. = FALSE)
-      }
-    },
-    previousRandom = function(value) {
-      if (missing(value)) {
-        if (is.null(private$.previousRandom)) {
-          message("No random value has been generated yet.")
-        } else {
-          return(private$.previousRandom)
-        }
-      } else {
-        stop("Can't set `$previousRandom`", call. = FALSE)
-      }
-    }
-  ),
-  private = list(
-    .currentRandom = NULL,
-    .previousRandom = NULL
-  )
-)
-

Let’s try it out:

-
-myRando <- Rando$new()
-
-# first time
-myRando$random
-#> [1] 0.5549124
-myRando$previousRandom
-#> No random value has been generated yet.
-#> NULL
-
-# second time
-myRando$random
-#> [1] 0.3482785
-myRando$previousRandom
-#> [1] 0.5549124
-
-# third time
-myRando$random
-#> [1] 0.2187275
-myRando$previousRandom
-#> [1] 0.3482785
-

Q4. Can subclasses access private fields/methods from their parent? Perform an experiment to find out.

-

A4. Unlike common OOP in other languages (e.g.Β C++), R6 subclasses (or derived classes) also have access to the private methods in superclass (or base class).

-

For instance, in the following example, the Duck class has a private method $quack(), but its subclass Mallard can access it using super$quack().

-
-Duck <- R6Class("Duck",
-  private = list(quack = function() print("Quack Quack"))
-)
-
-Mallard <- R6Class("Mallard",
-  inherit = Duck,
-  public = list(quack = function() super$quack())
-)
-
-myMallard <- Mallard$new()
-myMallard$quack()
-#> [1] "Quack Quack"
-
-
-

-14.3 Reference semantics (Exercises 14.4.4) -

-

Q1. Create a class that allows you to write a line to a specified file. You should open a connection to the file in $initialize(), append a line using cat() in $append_line(), and close the connection in $finalize().

-

A1. Here is a class that allows you to write a line to a specified file:

-
-fileEditor <- R6Class(
-  "fileEditor",
-  public = list(
-    initialize = function(filePath) {
-      private$.connection <- file(filePath, open = "wt")
-    },
-    append_line = function(text) {
-      cat(
-        text,
-        file = private$.connection,
-        sep = "\n",
-        append = TRUE
-      )
-    }
-  ),
-  private = list(
-    .connection = NULL,
-    # according to R6 docs, the destructor method should be private
-    finalize = function() {
-      print("Closing the file connection!")
-      close(private$.connection)
-    }
-  )
-)
-

Let’s check if it works as expected:

-
-greetMom <- function() {
-  f <- tempfile()
-  myfileEditor <- fileEditor$new(f)
-
-  readLines(f)
-
-  myfileEditor$append_line("Hi mom!")
-  myfileEditor$append_line("It's a beautiful day!")
-
-  readLines(f)
-}
-
-greetMom()
-#> [1] "Hi mom!"               "It's a beautiful day!"
-
-# force garbage collection
-gc()
-#> [1] "Closing the file connection!"
-#>           used (Mb) gc trigger (Mb) limit (Mb) max used
-#> Ncells  768546 41.1    1395468 74.6         NA  1395468
-#> Vcells 1407126 10.8    8388608 64.0      16384  2601527
-#>        (Mb)
-#> Ncells 74.6
-#> Vcells 19.9
-
-
-

-14.4 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    R6          * 2.5.1.9000 2022-10-27 [1] local
-#>    rlang         1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/reference-keys.txt b/_book/reference-keys.txt deleted file mode 100644 index 12979b25..00000000 --- a/_book/reference-keys.txt +++ /dev/null @@ -1,120 +0,0 @@ -introduction -names-and-values -binding-basics-exercise-2.2.2 -copy-on-modify-exercise-2.3.6 -object-size-exercise-2.4.1 -modify-in-place-exercise-2.5.3 -session-information -vectors -atomic-vectors-exercises-3.2.5 -attributes-exercises-3.3.4 -s3-atomic-vectors-exercises-3.4.5 -lists-exercises-3.5.4 -data-frames-and-tibbles-exercises-3.6.8 -session-information-1 -subsetting -selecting-multiple-elements-exercises-4.2.6 -selecting-a-single-element-exercises-4.3.5 -applications-exercises-4.5.9 -session-information-2 -control-flow -choices-exercises-5.2.4 -loops-exercises-5.3.3 -session-information-3 -functions -function-fundamentals-exercises-6.2.5 -lexical-scoping-exercises-6.4.5 -lazy-evaluation-exercises-6.5.4 -dot-dot-dot-exercises-6.6.1 -exiting-a-function-exercises-6.7.5 -function-forms-exercises-6.8.6 -session-information-4 -environments -environment-basics-exercises-7.2.7 -recursing-over-environments-exercises-7.3.1 -special-environments-exercises-7.4.5 -call-stacks-exercises-7.5.5 -session-information-5 -conditions -signalling-conditions-exercises-8.2.4 -handling-conditions-exercises-8.4.5 -custom-conditions-exercises-8.5.4 -applications-exercises-8.6.6 -session-information-6 -functionals -my-first-functional-map-exercises-9.2.6 -map-variants-exercises-9.4.6 -predicate-functionals-exercises-9.6.3 -base-functionals-exercises-9.7.3 -session-information-7 -function-factories -factory-fundamentals-exercises-10.2.6 -graphical-factories-exercises-10.3.4 -statistical-factories-exercises-10.4.4 -function-factories-functionals-exercises-10.5.1 -session-information-8 -function-operators -existing-function-operators-exercises-11.2.3 -case-study-creating-your-own-function-operators-exercises-11.3.1 -session-information-9 -base-types -s3 -basics-exercises-13.2.1 -classes-exercises-13.3.4 -generics-and-methods-exercises-13.4.4 -object-styles-exercises-13.5.1 -inheritance-exercises-13.6.3 -dispatch-details-exercises-13.7.5 -session-information-10 -r6 -classes-and-methods-exercises-14.2.6 -controlling-access-exercises-14.3.3 -reference-semantics-exercises-14.4.4 -session-information-11 -s4 -basics-exercises-15.2.1 -classes-exercises-15.3.6 -generics-and-methods-exercises-15.4.5 -method-dispatch-exercises-15.5.5 -s4-and-s3-exercises-15.6.3 -session-information-12 -trade-offs -big-picture -expressions -abstract-syntax-trees-exercises-18.2.4 -expressions-exercises-18.3.5 -parsing-and-grammar-exercises-18.4.4 -walking-ast-with-recursive-functions-exercises-18.5.3 -session-information-13 -quasiquotation -motivation-exercises-19.2.2 -quoting-exercises-19.3.6 -unquoting-exercises-19.4.8 -dot-dot-dot-exercises-19.6.5 -case-studies-exercises-19.7.5 -session-information-14 -evaluation -evaluation-basics-exercises-20.2.4 -quosures-exercises-20.3.6 -data-masks-exercises-20.4.6 -using-tidy-evaluation-exercises-20.5.4 -base-evaluation-exercises-20.6.3 -session-information-15 -translation -html-exercises-21.2.6 -latex-exercises-21.3.8 -session-information-16 -debugging -measuring-performance -profiling-exercises-23.2.4 -microbenchmarking-exercises-23.3.3 -session-information-17 -improving-performance -exercises-24.3.1 -exercises-24.4.3 -exercises-24.5.1 -rewriting-r-code-in-c -getting-started-with-c-exercises-25.2.6 -missing-values-exercises-25.4.5 -standard-template-library-exercises-25.5.7 -session-information-18 diff --git a/_book/rewriting-r-code-in-c.html b/_book/rewriting-r-code-in-c.html deleted file mode 100644 index 44115e1e..00000000 --- a/_book/rewriting-r-code-in-c.html +++ /dev/null @@ -1,1182 +0,0 @@ - - - - - - -Chapter 25 Rewriting R code in C++ | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-25 Rewriting R code in C++ -

-
-library(Rcpp, warn.conflicts = FALSE)
-
-

-25.1 Getting started with C++ (Exercises 25.2.6) -

-

Q1. With the basics of C++ in hand, it’s now a great time to practice by reading and writing some simple C++ functions. For each of the following functions, read the code and figure out what the corresponding base R function is. You might not understand every part of the code yet, but you should be able to figure out the basics of what the function does.

-
#include <Rcpp.h>
-using namespace Rcpp;
-
-// [[Rcpp::export]]
-double f1(NumericVector x) {
-  int n = x.size();
-  double y = 0;
-
-  for(int i = 0; i < n; ++i) {
-    y += x[i] / n;
-  }
-  return y;
-}
-
-// [[Rcpp::export]]
-NumericVector f2(NumericVector x) {
-  int n = x.size();
-  NumericVector out(n);
-
-  out[0] = x[0];
-  for(int i = 1; i < n; ++i) {
-    out[i] = out[i - 1] + x[i];
-  }
-  return out;
-}
-
-// [[Rcpp::export]]
-bool f3(LogicalVector x) {
-  int n = x.size();
-
-  for(int i = 0; i < n; ++i) {
-    if (x[i]) return true;
-  }
-  return false;
-}
-
-// [[Rcpp::export]]
-int f4(Function pred, List x) {
-  int n = x.size();
-
-  for(int i = 0; i < n; ++i) {
-    LogicalVector res = pred(x[i]);
-    if (res[0]) return i + 1;
-  }
-  return 0;
-}
-
-// [[Rcpp::export]]
-NumericVector f5(NumericVector x, NumericVector y) {
-  int n = std::max(x.size(), y.size());
-  NumericVector x1 = rep_len(x, n);
-  NumericVector y1 = rep_len(y, n);
-
-  NumericVector out(n);
-
-  for (int i = 0; i < n; ++i) {
-    out[i] = std::min(x1[i], y1[i]);
-  }
-
-  return out;
-}
-

A1.

-

f1() is the same as mean():

-
-x <- c(1, 2, 3, 4, 5, 6)
-
-f1(x)
-#> [1] 3.5
-mean(x)
-#> [1] 3.5
-

f2() is the same as cumsum():

-
-x <- c(1, 3, 5, 6)
-
-f2(x)
-#> [1]  1  4  9 15
-cumsum(x)
-#> [1]  1  4  9 15
-

f3() is the same as any():

-
-x1 <- c(TRUE, FALSE, FALSE, TRUE)
-x2 <- c(FALSE, FALSE)
-
-f3(x1)
-#> [1] TRUE
-any(x1)
-#> [1] TRUE
-
-f3(x2)
-#> [1] FALSE
-any(x2)
-#> [1] FALSE
-

f4() is the same as Position():

-
-x <- list("a", TRUE, "m", 2)
-
-f4(is.numeric, x)
-#> [1] 4
-Position(is.numeric, x)
-#> [1] 4
-

f5() is the same as pmin():

-
-v1 <- c(1, 3, 4, 5, 6, 7)
-v2 <- c(1, 2, 7, 2, 8, 1)
-
-f5(v1, v2)
-#> [1] 1 2 4 2 6 1
-pmin(v1, v2)
-#> [1] 1 2 4 2 6 1
-

Q2. To practice your function writing skills, convert the following functions into C++. For now, assume the inputs have no missing values.

-
    -
  1. all().

  2. -
  3. cumprod(), cummin(), cummax().

  4. -
  5. diff(). Start by assuming lag 1, and then generalise for lag n.

  6. -
  7. range().

  8. -
  9. var(). Read about the approaches you can take on Wikipedia. Whenever implementing a numerical algorithm, it’s always good to check what is already known about the problem.

  10. -
-

A2. The performance benefits are not going to be observed if the function is primitive since those are already tuned to the max in R for performance. So, expect performance gain only for diff() and var().

-
-is.primitive(all)
-#> [1] TRUE
-is.primitive(cumprod)
-#> [1] TRUE
-is.primitive(diff)
-#> [1] FALSE
-is.primitive(range)
-#> [1] TRUE
-is.primitive(var)
-#> [1] FALSE
- -
#include <vector>
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-bool allC(std::vector<bool> x)
-{
-    for (const auto& xElement : x)
-    {
-        if (!xElement) return false;
-    }
-
-    return true;
-}
-
-v1 <- rep(TRUE, 10)
-v2 <- c(rep(TRUE, 5), rep(FALSE, 5))
-
-all(v1)
-#> [1] TRUE
-allC(v1)
-#> [1] TRUE
-
-all(v2)
-#> [1] FALSE
-allC(v2)
-#> [1] FALSE
-
-# performance benefits?
-bench::mark(
-  all(c(rep(TRUE, 1000), rep(FALSE, 1000))),
-  allC(c(rep(TRUE, 1000), rep(FALSE, 1000))),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression                                      min
-#>   <bch:expr>                                 <bch:tm>
-#> 1 all(c(rep(TRUE, 1000), rep(FALSE, 1000)))    14.2Β΅s
-#> 2 allC(c(rep(TRUE, 1000), rep(FALSE, 1000)))   12.6Β΅s
-#>     median `itr/sec` mem_alloc `gc/sec`
-#>   <bch:tm>     <dbl> <bch:byt>    <dbl>
-#> 1   16.5Β΅s    60321.    15.8KB        0
-#> 2   15.1Β΅s    64552.    18.3KB        0
- -
#include <vector>
-
-// [[Rcpp::export]]
-std::vector<double> cumprodC(const std::vector<double> &x)
-{
-    std::vector<double> out{x};
-
-    for (size_t i = 1; i < x.size(); i++)
-    {
-        out[i] = out[i - 1] * x[i];
-    }
-
-    return out;
-}
-
-v1 <- c(10, 4, 6, 8)
-
-cumprod(v1)
-#> [1]   10   40  240 1920
-cumprodC(v1)
-#> [1]   10   40  240 1920
-
-# performance benefits?
-bench::mark(
-  cumprod(v1),
-  cumprodC(v1),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression        min   median `itr/sec` mem_alloc
-#>   <bch:expr>   <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 cumprod(v1)         0     41ns 13697871.        0B
-#> 2 cumprodC(v1)    779ns    943ns   636481.    6.62KB
-#>   `gc/sec`
-#>      <dbl>
-#> 1        0
-#> 2        0
-
    -
  • cumminC()
  • -
-
#include <vector>
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-std::vector<double> cumminC(const std::vector<double> &x)
-{
-    std::vector<double> out{x};
-
-    for (size_t i = 1; i < x.size(); i++)
-    {
-        out[i] = (out[i] < out[i - 1]) ? out[i] : out[i - 1];
-    }
-
-    return out;
-}
-
-v1 <- c(3:1, 2:0, 4:2)
-
-cummin(v1)
-#> [1] 3 2 1 1 1 0 0 0 0
-cumminC(v1)
-#> [1] 3 2 1 1 1 0 0 0 0
-
-# performance benefits?
-bench::mark(
-  cummin(v1),
-  cumminC(v1),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression       min   median `itr/sec` mem_alloc `gc/sec`
-#>   <bch:expr>  <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
-#> 1 cummin(v1)      41ns  82.07ns  6699581.        0B        0
-#> 2 cumminC(v1)    984ns   1.15Β΅s   694673.    6.62KB        0
-
    -
  • cummaxC()
  • -
-
#include <vector>
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-std::vector<double> cummaxC(const std::vector<double> &x)
-{
-    std::vector<double> out{x};
-
-    for (size_t i = 1; i < x.size(); i++)
-    {
-        out[i] = (out[i] > out[i - 1]) ? out[i] : out[i - 1];
-    }
-    
-    return out;
-}
-
-v1 <- c(3:1, 2:0, 4:2)
-
-cummax(v1)
-#> [1] 3 3 3 3 3 3 4 4 4
-cummaxC(v1)
-#> [1] 3 3 3 3 3 3 4 4 4
-
-# performance benefits?
-bench::mark(
-  cummax(v1),
-  cummaxC(v1),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression       min   median `itr/sec` mem_alloc `gc/sec`
-#>   <bch:expr>  <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
-#> 1 cummax(v1)   40.98ns   41.1ns  8647005.        0B        0
-#> 2 cummaxC(v1)   2.71Β΅s    3.5Β΅s   255715.    6.62KB        0
- -
#include <vector>
-#include <functional>
-#include <algorithm>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-std::vector<double> diffC(const std::vector<double> &x, int lag)
-{
-    std::vector<double> vec_start;
-    std::vector<double> vec_lagged;
-    std::vector<double> vec_diff;
-
-    for (size_t i = lag; i < x.size(); i++)
-    {
-        vec_lagged.push_back(x[i]);
-    }
-
-    for (size_t i = 0; i < (x.size() - lag); i++)
-    {
-        vec_start.push_back(x[i]);
-    }
-
-    std::transform(
-        vec_lagged.begin(), vec_lagged.end(),
-        vec_start.begin(), std::back_inserter(vec_diff),
-        std::minus<double>());
-
-    return vec_diff;
-}
-
-v1 <- c(1, 2, 4, 8, 13)
-v2 <- c(1, 2, NA, 8, 13)
-
-diff(v1, 2)
-#> [1] 3 6 9
-diffC(v1, 2)
-#> [1] 3 6 9
-
-diff(v2, 2)
-#> [1] NA  6 NA
-diffC(v2, 2)
-#> [1] NA  6 NA
-
-# performance benefits?
-bench::mark(
-  diff(v1, 2),
-  diffC(v1, 2),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression        min   median `itr/sec` mem_alloc
-#>   <bch:expr>   <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 diff(v1, 2)    2.42Β΅s   3.36Β΅s   166452.        0B
-#> 2 diffC(v1, 2)   4.02Β΅s    4.3Β΅s   210097.    2.49KB
-#>   `gc/sec`
-#>      <dbl>
-#> 1        0
-#> 2        0
- -
#include <iostream>
-#include <vector>
-#include <algorithm>
-using namespace std;
-
-// [[Rcpp::export]]
-std::vector<double> rangeC(std::vector<double> x)
-{
-    std::vector<double> rangeVec{0.0, 0.0};
-
-    rangeVec.at(0) = *std::min_element(x.begin(), x.end());
-    rangeVec.at(1) = *std::max_element(x.begin(), x.end());
-
-    return rangeVec;
-}
-
-v1 <- c(10, 4, 6, 8)
-
-range(v1)
-#> [1]  4 10
-rangeC(v1)
-#> [1]  4 10
-
-# performance benefits?
-bench::mark(
-  range(v1),
-  rangeC(v1),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
-#> 1 range(v1)     3.4Β΅s   3.65Β΅s   250437.        0B        0
-#> 2 rangeC(v1)   2.91Β΅s   3.16Β΅s   253825.    6.62KB        0
- -
#include <vector>
-#include <cmath>
-#include <numeric>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-double variance(std::vector<double> x)
-{
-    double sumSquared{0};
-
-    double mean = std::accumulate(x.begin(), x.end(), 0.0) / x.size();
-
-    for (const auto& xElement : x)
-    {
-        sumSquared += pow(xElement - mean, 2.0);
-    }
-
-    return sumSquared / (x.size() - 1);
-}
-
-v1 <- c(1, 4, 7, 8)
-
-var(v1)
-#> [1] 10
-variance(v1)
-#> [1] 10
-
-# performance benefits?
-bench::mark(
-  var(v1),
-  variance(v1),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression        min   median `itr/sec` mem_alloc
-#>   <bch:expr>   <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 var(v1)        4.14Β΅s   5.08Β΅s   139996.        0B
-#> 2 variance(v1) 943.08ns   2.91Β΅s   365503.    6.62KB
-#>   `gc/sec`
-#>      <dbl>
-#> 1        0
-#> 2        0
-
-
-

-25.2 Missing values (Exercises 25.4.5) -

-

Q1. Rewrite any of the functions from Exercise 25.2.6 to deal with missing values. If na.rm is true, ignore the missing values. If na.rm is false, return a missing value if the input contains any missing values. Some good functions to practice with are min(), max(), range(), mean(), and var().

-

A1. We will only create a version of range() that deals with missing values. The same principle applies to others:

-
#include <iostream>
-#include <vector>
-#include <algorithm>
-#include <math.h>
-#include <Rcpp.h>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-std::vector<double> rangeC_NA(std::vector<double> x, bool removeNA = true)
-{
-    std::vector<double> rangeVec{0.0, 0.0};
-
-    bool naPresent = std::any_of(
-        x.begin(),
-        x.end(),
-        [](double d)
-        { return isnan(d); });
-
-    if (naPresent)
-    {
-        if (removeNA)
-        {
-            std::remove(x.begin(), x.end(), NAN);
-        }
-        else
-        {
-            rangeVec.at(0) = NA_REAL; // NAN;
-            rangeVec.at(1) = NA_REAL; // NAN;
-
-            return rangeVec;
-        }
-    }
-
-    rangeVec.at(0) = *std::min_element(x.begin(), x.end());
-    rangeVec.at(1) = *std::max_element(x.begin(), x.end());
-
-    return rangeVec;
-}
-
-v1 <- c(10, 4, NA, 6, 8)
-
-range(v1, na.rm = FALSE)
-#> [1] NA NA
-rangeC_NA(v1, FALSE)
-#> [1] NA NA
-
-range(v1, na.rm = TRUE)
-#> [1]  4 10
-rangeC_NA(v1, TRUE)
-#> [1]  4 10
-

Q2. Rewrite cumsum() and diff() so they can handle missing values. Note that these functions have slightly more complicated behaviour.

-

A2. The cumsum() docs say:

-
-

An NA value in x causes the corresponding and following elements of the return value to be NA, as does integer overflow in cumsum (with a warning).

-
-

Similarly, diff() docs say:

-
-

NA’s propagate.

-
-

Therefore, both of these functions don’t allow removing missing values and the NAs propagate.

-

As seen from the examples above, diffC() already behaves this way.

-

Similarly, cumsumC() propagates NAs as well.

-
#include <Rcpp.h>
-using namespace Rcpp;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-NumericVector cumsumC(NumericVector x) {
-  int n = x.size();
-  NumericVector out(n);
-  
-  out[0] = x[0];
-  for(int i = 1; i < n; ++i) {
-    out[i] = out[i - 1] + x[i];
-  }
-  
-  return out;
-}
-
-v1 <- c(1, 2, 3, 4)
-v2 <- c(1, 2, NA, 4)
-
-cumsum(v1)
-#> [1]  1  3  6 10
-cumsumC(v1)
-#> [1]  1  3  6 10
-
-cumsum(v2)
-#> [1]  1  3 NA NA
-cumsumC(v2)
-#> [1]  1  3 NA NA
-
-
-

-25.3 Standard Template Library (Exercises 25.5.7) -

-

Q1. To practice using the STL algorithms and data structures, implement the following using R functions in C++, using the hints provided:

-

A1.

-
    -
  1. -median.default() using partial_sort.
  2. -
-
#include <iostream>
-#include <vector>
-#include <algorithm>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-double medianC(std::vector<double> &x)
-{
-    int middleIndex = static_cast<int>(x.size() / 2);
-
-    std::partial_sort(x.begin(), x.begin() + middleIndex, x.end());
-
-    // for even number of observations
-    if (x.size() % 2 == 0)
-    {
-        return (x[middleIndex - 1] + x[middleIndex]) / 2;
-    }
-
-    return x[middleIndex];
-}
-
-v1 <- c(1, 3, 3, 6, 7, 8, 9)
-v2 <- c(1, 2, 3, 4, 5, 6, 8, 9)
-
-median.default(v1)
-#> [1] 6
-medianC(v1)
-#> [1] 6
-
-median.default(v2)
-#> [1] 4.5
-medianC(v2)
-#> [1] 4.5
-
-# performance benefits?
-bench::mark(
-  median.default(v2),
-  medianC(v2),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression              min   median `itr/sec` mem_alloc
-#>   <bch:expr>         <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 median.default(v2)   13.1Β΅s  13.88Β΅s    66718.        0B
-#> 2 medianC(v2)           943ns   1.15Β΅s   616685.    2.49KB
-#>   `gc/sec`
-#>      <dbl>
-#> 1        0
-#> 2        0
-
    -
  1. -%in% using unordered_set and the find() or count() methods.
  2. -
-
#include <vector>
-#include <unordered_set>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-std::vector<bool> matchC(const std::vector<double> &x, const std::vector<double> &table)
-{
-    std::unordered_set<double> tableUnique(table.begin(), table.end());
-    std::vector<bool> out;
-
-    for (const auto &xElem : x)
-    {
-        out.push_back(tableUnique.find(xElem) != tableUnique.end() ? true : false);
-    }
-
-    return out;
-}
-
-x1 <- c(3, 4, 8)
-x2 <- c(1, 2, 3, 3, 4, 4, 5, 6)
-
-x1 %in% x2
-#> [1]  TRUE  TRUE FALSE
-matchC(x1, x2)
-#> [1]  TRUE  TRUE FALSE
-
-# performance benefits?
-bench::mark(
-  x1 %in% x2,
-  matchC(x1, x2),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression          min   median `itr/sec` mem_alloc
-#>   <bch:expr>     <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 x1 %in% x2     532.95ns 696.98ns   571727.        0B
-#> 2 matchC(x1, x2)   4.26Β΅s   4.57Β΅s   173399.    6.62KB
-#>   `gc/sec`
-#>      <dbl>
-#> 1        0
-#> 2        0
-
    -
  1. -unique() using an unordered_set (challenge: do it in one line!).
  2. -
-
#include <unordered_set>
-#include <vector>
-#include <iostream>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-std::unordered_set<double> uniqueC(const std::vector<double> &x)
-{
-    std::unordered_set<double> xSet(x.begin(), x.end());
-
-    return xSet;
-}
-

Note that these functions are not comparable. As far as I can see, there is no way to get the same output as the R version of the function using the unordered_set data structure.

-
-v1 <- c(1, 3, 3, 6, 7, 8, 9)
-
-unique(v1)
-#> [1] 1 3 6 7 8 9
-uniqueC(v1)
-#> [1] 9 8 1 7 3 6
-

We can make comparable version using set data structure:

-
#include <set>
-#include <vector>
-#include <iostream>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-std::set<double> uniqueC2(const std::vector<double> &x)
-{
-    std::set<double> xSet(x.begin(), x.end());
-
-    return xSet;
-}
-
-v1 <- c(1, 3, 3, 6, 7, 8, 9)
-
-unique(v1)
-#> [1] 1 3 6 7 8 9
-uniqueC2(v1)
-#> [1] 1 3 6 7 8 9
-
-# performance benefits?
-bench::mark(
-  unique(v1),
-  uniqueC2(v1),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression        min   median `itr/sec` mem_alloc
-#>   <bch:expr>   <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 unique(v1)     1.31Β΅s    1.5Β΅s   452423.        0B
-#> 2 uniqueC2(v1)   1.02Β΅s   1.21Β΅s   462107.    6.62KB
-#>   `gc/sec`
-#>      <dbl>
-#> 1        0
-#> 2        0
-
    -
  1. -min() using std::min(), or max() using std::max().
  2. -
-
#include <iostream>
-#include <vector>
-#include <algorithm>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-const double minC(const std::vector<double> &x)
-{
-     return *std::min_element(x.begin(), x.end());
-}
-
-// [[Rcpp::export]]
-const double maxC(std::vector<double> x)
-{
-     return *std::max_element(x.begin(), x.end());
-}
-
-v1 <- c(3, 3, 6, 1, 9, 7, 8)
-
-min(v1)
-#> [1] 1
-minC(v1)
-#> [1] 1
-
-# performance benefits?
-bench::mark(
-  min(v1),
-  minC(v1),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
-#> 1 min(v1)    122.94ns 615.02ns  1122395.        0B        0
-#> 2 minC(v1)     2.99Β΅s   3.16Β΅s   249057.    6.62KB        0
-
-max(v1)
-#> [1] 9
-maxC(v1)
-#> [1] 9
-
-# performance benefits?
-bench::mark(
-  max(v1),
-  maxC(v1),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
-#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
-#> 1 max(v1)       123ns 164.03ns  5517545.        0B        0
-#> 2 maxC(v1)      943ns   2.91Β΅s   359208.    6.62KB        0
-
    -
  1. -which.min() using min_element, or which.max() using max_element.
  2. -
-
#include <vector>
-#include <algorithm>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-int which_maxC(std::vector<double> &x)
-{
-    int maxIndex = std::distance(x.begin(), std::max_element(x.begin(), x.end()));
-  
-    // R is 1-index based, while C++ is 0-index based
-    return maxIndex + 1;
-}
-
-// [[Rcpp::export]]
-int which_minC(std::vector<double> &x)
-{
-    int minIndex = std::distance(x.begin(), std::min_element(x.begin(), x.end()));
-  
-    // R is 1-index based, while C++ is 0-index based
-    return minIndex + 1;
-}
-
-v1 <- c(3, 3, 6, 1, 9, 7, 8)
-
-which.min(v1)
-#> [1] 4
-which_minC(v1)
-#> [1] 4
-
-# performance benefits?
-bench::mark(
-  which.min(v1),
-  which_minC(v1),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression          min   median `itr/sec` mem_alloc
-#>   <bch:expr>     <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 which.min(v1)     287ns 943.08ns   692700.        0B
-#> 2 which_minC(v1)    984ns   3.26Β΅s    40664.    6.62KB
-#>   `gc/sec`
-#>      <dbl>
-#> 1        0
-#> 2        0
-
-which.max(v1)
-#> [1] 5
-which_maxC(v1)
-#> [1] 5
-
-# performance benefits?
-bench::mark(
-  which.max(v1),
-  which_maxC(v1),
-  iterations = 100
-)
-#> # A tibble: 2 Γ— 6
-#>   expression          min   median `itr/sec` mem_alloc
-#>   <bch:expr>     <bch:tm> <bch:tm>     <dbl> <bch:byt>
-#> 1 which.max(v1)     205ns 942.96ns   191837.        0B
-#> 2 which_maxC(v1)    902ns   1.07Β΅s   513690.    6.62KB
-#>   `gc/sec`
-#>      <dbl>
-#> 1        0
-#> 2        0
-
    -
  1. -setdiff(), union(), and intersect() for integers using sorted ranges and set_union, set_intersection and set_difference.
  2. -
-

Note that the following C++ implementations of given functions are not strictly equivalent to their R versions. As far as I can see, there is no way for them to be identical while satisfying the specifications mentioned in the question.

- -
#include <algorithm>
-#include <iostream>
-#include <vector>
-#include <set>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-std::set<int> unionC(std::vector<int> &v1, std::vector<int> &v2)
-{
-    std::sort(v1.begin(), v1.end());
-    std::sort(v2.begin(), v2.end());
-
-    std::vector<int> union_vec(v1.size() + v2.size());
-    auto it = std::set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), union_vec.begin());
-
-    union_vec.resize(it - union_vec.begin());
-    std::set<int> union_set(union_vec.begin(), union_vec.end());
-
-    return union_set;
-}
-
-v1 <- c(1, 4, 5, 5, 5, 6, 2)
-v2 <- c(4, 1, 6, 8)
-
-union(v1, v2)
-#> [1] 1 4 5 6 2 8
-unionC(v1, v2)
-#> [1] 1 2 4 5 6 8
- -
#include <algorithm>
-#include <iostream>
-#include <vector>
-#include <set>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-std::set<int> intersectC(std::vector<int> &v1, std::vector<int> &v2)
-{
-    std::sort(v1.begin(), v1.end());
-    std::sort(v2.begin(), v2.end());
-
-    std::vector<int> union_vec(v1.size() + v2.size());
-    auto it = std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), union_vec.begin());
-
-    union_vec.resize(it - union_vec.begin());
-    std::set<int> union_set(union_vec.begin(), union_vec.end());
-
-    return union_set;
-}
-
-v1 <- c(1, 4, 5, 5, 5, 6, 2)
-v2 <- c(4, 1, 6, 8)
-
-intersect(v1, v2)
-#> [1] 1 4 6
-intersectC(v1, v2)
-#> [1] 1 4 6
- -
#include <algorithm>
-#include <iostream>
-#include <vector>
-#include <set>
-using namespace std;
-// [[Rcpp::plugins(cpp11)]]
-
-// [[Rcpp::export]]
-std::set<int> setdiffC(std::vector<int> &v1, std::vector<int> &v2)
-{
-    std::sort(v1.begin(), v1.end());
-    std::sort(v2.begin(), v2.end());
-
-    std::vector<int> union_vec(v1.size() + v2.size());
-    auto it = std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), union_vec.begin());
-
-    union_vec.resize(it - union_vec.begin());
-    std::set<int> union_set(union_vec.begin(), union_vec.end());
-
-    return union_set;
-}
-
-v1 <- c(1, 4, 5, 5, 5, 6, 2)
-v2 <- c(4, 1, 6, 8)
-
-setdiff(v1, v2)
-#> [1] 5 2
-setdiffC(v1, v2)
-#> [1] 2 5
-
-
-

-25.4 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bench         1.1.2      2021-11-30 [1] CRAN (R 4.2.0)
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    profmem       0.6.0      2020-12-13 [1] CRAN (R 4.2.0)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    Rcpp        * 1.0.9      2022-07-08 [1] CRAN (R 4.2.1)
-#>    rlang         1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble        3.1.8.9002 2022-10-16 [1] local
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
- - - - - - - - - - - - - -
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/s3.html b/_book/s3.html deleted file mode 100644 index cea5311f..00000000 --- a/_book/s3.html +++ /dev/null @@ -1,1304 +0,0 @@ - - - - - - -Chapter 13 S3 | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-13 S3 -

-

Attaching the needed libraries:

-
-library(sloop, warn.conflicts = FALSE)
-library(dplyr, warn.conflicts = FALSE)
-library(purrr, warn.conflicts = FALSE)
-
-

-13.1 Basics (Exercises 13.2.1) -

-
-

Q1. Describe the difference between t.test() and t.data.frame(). When is each function called?

-

A1. The difference between t.test() and t.data.frame() is the following:

-
    -
  • t.test() is a generic function to perform a t-test.

  • -
  • t.data.frame() is a method for generic t() (a matrix transform function) and will be dispatched for data.frame objects.

  • -
-

We can also confirm these function types using ftype():

-
-ftype(t.test)
-#> [1] "S3"      "generic"
-ftype(t.data.frame)
-#> [1] "S3"     "method"
-
-

Q2. Make a list of commonly used base R functions that contain . in their name but are not S3 methods.

-

A2. Here are a few common R functions with . but that are not S3 methods:

- -

For example,

-
-ftype(as.data.frame)
-#> [1] "S3"      "generic"
-ftype(on.exit)
-#> [1] "primitive"
-
-

Q3. What does the as.data.frame.data.frame() method do? Why is it confusing? How could you avoid this confusion in your own code?

-

A3. It’s an S3 method for generic as.data.frame().

-
-ftype(as.data.frame.data.frame)
-#> [1] "S3"     "method"
-

It can be seen in all methods supported by this generic:

-
-s3_methods_generic("as.data.frame") %>%
-  dplyr::filter(class == "data.frame")
-#> # A tibble: 1 Γ— 4
-#>   generic       class      visible source
-#>   <chr>         <chr>      <lgl>   <chr> 
-#> 1 as.data.frame data.frame TRUE    base
-

Given the number of .s in this name, it is quite confusing to figure out what is the name of the generic and the name of the class.

-
-

Q4. Describe the difference in behaviour in these two calls.

-
-set.seed(1014)
-some_days <- as.Date("2017-01-31") + sample(10, 5)
-mean(some_days)
-#> [1] "2017-02-06"
-mean(unclass(some_days))
-#> [1] 17203.4
-

A4. The difference in behaviour in the specified calls.

-
    -
  • Before unclassing, the mean generic dispatches .Date method:
  • -
-
-some_days <- as.Date("2017-01-31") + sample(10, 5)
-
-some_days
-#> [1] "2017-02-06" "2017-02-09" "2017-02-05" "2017-02-08"
-#> [5] "2017-02-07"
-
-s3_dispatch(mean(some_days))
-#> => mean.Date
-#>  * mean.default
-
-mean(some_days)
-#> [1] "2017-02-07"
-
    -
  • After unclassing, the mean generic dispatches .numeric method:
  • -
-
-unclass(some_days)
-#> [1] 17203 17206 17202 17205 17204
-
-mean(unclass(some_days))
-#> [1] 17204
-
-s3_dispatch(mean(unclass(some_days)))
-#>    mean.double
-#>    mean.numeric
-#> => mean.default
-
-

Q5. What class of object does the following code return? What base type is it built on? What attributes does it use?

-
-x <- ecdf(rpois(100, 10))
-x
-

A5. The object is based on base type closure6, which is a type of function.

-
-x <- ecdf(rpois(100, 10))
-x
-#> Empirical CDF 
-#> Call: ecdf(rpois(100, 10))
-#>  x[1:18] =      2,      3,      4,  ...,     18,     19
-
-otype(x)
-#> [1] "S3"
-typeof(x)
-#> [1] "closure"
-

Its class is ecdf, which has other superclasses.

-
-s3_class(x)
-#> [1] "ecdf"     "stepfun"  "function"
-

Apart from class, it has the following attributes:

-
-attributes(x)
-#> $class
-#> [1] "ecdf"     "stepfun"  "function"
-#> 
-#> $call
-#> ecdf(rpois(100, 10))
-
-

Q6. What class of object does the following code return? What base type is it built on? What attributes does it use?

-
-x <- table(rpois(100, 5))
-x
-

A6. The object is based on base type integer.

-
-x <- table(rpois(100, 5))
-x
-#> 
-#>  1  2  3  4  5  6  7  8  9 10 
-#>  7  7 18 13 14 14 16  4  4  3
-
-otype(x)
-#> [1] "S3"
-typeof(x)
-#> [1] "integer"
-

Its class is table.

-
-s3_class(x)
-#> [1] "table"
-

Apart from class, it has the following attributes:

-
-attributes(x)
-#> $dim
-#> [1] 10
-#> 
-#> $dimnames
-#> $dimnames[[1]]
-#>  [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10"
-#> 
-#> 
-#> $class
-#> [1] "table"
-
-
-
-

-13.2 Classes (Exercises 13.3.4) -

-
-

Q1. Write a constructor for data.frame objects. What base type is a data frame built on? What attributes does it use? What are the restrictions placed on the individual elements? What about the names?

-

A1. A data frame is built on top of a named list of atomic vectors and has attributes for row names:

-
-unclass(data.frame())
-#> named list()
-#> attr(,"row.names")
-#> integer(0)
-

The restriction imposed on individual elements is that they need to have the same length. Additionally, the names need to be syntactically valid and unique.

-
-new_data_frame <- function(x = list(), row.names = character()) {
-  # row names should be character
-  if (!all(is.character(row.names))) {
-    stop("Row name should be of `chracter` type.", call. = FALSE)
-  }
-
-  # all elements should have the same length
-  unique_element_lengths <- unique(purrr::map_int(x, length))
-  if (length(unique_element_lengths) > 1L) {
-    stop("All list elements in `x` should have same length.", call. = FALSE)
-  }
-
-  # if not provided, generate row names
-  # this is necessary if there is at least one element in the list
-  if (length(x) > 0L && length(row.names) == 0L) {
-    row.names <- .set_row_names(unique_element_lengths)
-  }
-
-  structure(x, class = "data.frame", row.names = row.names)
-}
-

Let’s try it out:

-
-new_data_frame(list("x" = 1, "y" = c(2, 3)))
-#> Error: All list elements in `x` should have same length.
-
-new_data_frame(list("x" = 1, "y" = c(2)), row.names = 1L)
-#> Error: Row name should be of `chracter` type.
-
-new_data_frame(list())
-#> data frame with 0 columns and 0 rows
-
-new_data_frame(list("x" = 1, "y" = 2))
-#>   x y
-#> 1 1 2
-
-new_data_frame(list("x" = 1, "y" = 2), row.names = "row-1")
-#>       x y
-#> row-1 1 2
-
-

Q2. Enhance my factor() helper to have better behaviour when one or more values is not found in levels. What does base::factor() do in this situation?

-

A2. When one or more values is not found in levels, those values are converted to NA in base::factor():

-
-base::factor(c("a", "b", "c"), levels = c("a", "c"))
-#> [1] a    <NA> c   
-#> Levels: a c
-

In the new constructor, we can throw an error to inform the user:

-
-new_factor <- function(x = integer(), levels = character()) {
-  stopifnot(is.integer(x))
-  stopifnot(is.character(levels))
-
-  structure(
-    x,
-    levels = levels,
-    class = "factor"
-  )
-}
-
-validate_factor <- function(x) {
-  values <- unclass(x)
-  levels <- attr(x, "levels")
-
-  if (!all(!is.na(values) & values > 0)) {
-    stop(
-      "All `x` values must be non-missing and greater than zero",
-      call. = FALSE
-    )
-  }
-
-  if (length(levels) < max(values)) {
-    stop(
-      "There must be at least as many `levels` as possible values in `x`",
-      call. = FALSE
-    )
-  }
-
-  x
-}
-
-create_factor <- function(x = character(), levels = unique(x)) {
-  ind <- match(x, levels)
-
-  if (any(is.na(ind))) {
-    missing_values <- x[which(is.na(match(x, levels)))]
-
-    stop(
-      paste0(
-        "Following values from `x` are not present in `levels`:\n",
-        paste0(missing_values, collapse = "\n")
-      ),
-      call. = FALSE
-    )
-  }
-
-  validate_factor(new_factor(ind, levels))
-}
-

Let’s try it out:

-
-create_factor(c("a", "b", "c"), levels = c("a", "c"))
-#> Error: Following values from `x` are not present in `levels`:
-#> b
-
-create_factor(c("a", "b", "c"), levels = c("a", "b", "c"))
-#> [1] a b c
-#> Levels: a b c
-
-

Q3. Carefully read the source code of factor(). What does it do that my constructor does not?

-

A3. The source code for factor() can be read here.

-

There are a number ways in which the base version is more flexible.

-
    -
  • It allows labeling the values:
  • -
-
-x <- c("a", "b", "b")
-levels <- c("a", "b", "c")
-labels <- c("one", "two", "three")
-
-factor(x, levels = levels, labels = labels)
-#> [1] one two two
-#> Levels: one two three
-
    -
  • It checks that the levels are not duplicated.
  • -
-
-x <- c("a", "b", "b")
-levels <- c("a", "b", "b")
-
-factor(x, levels = levels)
-#> Error in `levels<-`(`*tmp*`, value = as.character(levels)): factor level [3] is duplicated
-
-create_factor(x, levels = levels)
-#> [1] a b b
-#> Levels: a b b
-#> Warning in print.factor(x): duplicated level [3] in factor
-
    -
  • The levels argument can be NULL.
  • -
-
-x <- c("a", "b", "b")
-
-factor(x, levels = NULL)
-#> [1] <NA> <NA> <NA>
-#> Levels:
-
-create_factor(x, levels = NULL)
-#> Error: Following values from `x` are not present in `levels`:
-#> a
-#> b
-#> b
-

Q4. Factors have an optional β€œcontrasts” attribute. Read the help for C(), and briefly describe the purpose of the attribute. What type should it have? Rewrite the new_factor() constructor to include this attribute.

-

A4. Categorical variables are typically encoded as dummy variables in regression models and by default each level is compared with the first factor level. Contrats provide a flexible way for such comparisons.

-

You can set the "contrasts" attribute for a factor using stats::C().

-

Alternatively, you can set the "contrasts" attribute using matrix (?contrasts):

-
-

[Contrasts] can be a matrix with one row for each level of the factor or a suitable function like contr.poly or a character string giving the name of the function

-
-

The constructor provided in the book:

-
-new_factor <- function(x = integer(), levels = character()) {
-  stopifnot(is.integer(x))
-  stopifnot(is.character(levels))
-
-  structure(
-    x,
-    levels = levels,
-    class = "factor"
-  )
-}
-

Here is how it can be updated to also support contrasts:

-
-new_factor <- function(x = integer(),
-                       levels = character(),
-                       contrasts = NULL) {
-  stopifnot(is.integer(x))
-  stopifnot(is.character(levels))
-
-  if (!is.null(contrasts)) {
-    stopifnot(is.matrix(contrasts) && is.numeric(contrasts))
-  }
-
-  structure(
-    x,
-    levels = levels,
-    class = "factor",
-    contrasts = contrasts
-  )
-}
-

Q5. Read the documentation for utils::as.roman(). How would you write a constructor for this class? Does it need a validator? What might a helper do?

-

A5. utils::as.roman() converts Indo-Arabic numerals to Roman numerals. Removing its class also reveals that it is implemented using the base type integer:

-
-as.roman(1)
-#> [1] I
-
-typeof(unclass(as.roman(1)))
-#> [1] "integer"
-

Therefore, we can create a simple constructor to create a new instance of this class:

-
-new_roman <- function(x = integer()) {
-  stopifnot(is.integer(x))
-
-  structure(x, class = "roman")
-}
-

The docs mention the following:

-
-

Only numbers between 1 and 3899 have a unique representation as roman numbers, and hence others result in as.roman(NA).

-
-
-as.roman(10000)
-#> [1] <NA>
-

Therefore, we can warn the user and then return NA in a validator function:

-
-validate_new_roman <- function(x) {
-  int_values <- unclass(x)
-
-  if (any(int_values < 1L | int_values > 3899L)) {
-    warning(
-      "Integer should be between 1 and 3899. Returning `NA` otherwise.",
-      call. = FALSE
-    )
-  }
-
-  x
-}
-

The helper function can coerce the entered input to integer type for convenience:

-
-roman <- function(x = integer()) {
-  x <- as.integer(x)
-
-  validate_new_roman(new_roman(x))
-}
-

Let’s try it out:

-
-roman(1)
-#> [1] I
-
-roman(c(5, 20, 100, 150, 100000))
-#> Warning: Integer should be between 1 and 3899. Returning
-#> `NA` otherwise.
-#> [1] V    XX   C    CL   <NA>
-
-
-

-13.3 Generics and methods (Exercises 13.4.4) -

-

Q1. Read the source code for t() and t.test() and confirm that t.test() is an S3 generic and not an S3 method. What happens if you create an object with class test and call t() with it? Why?

-
-x <- structure(1:10, class = "test")
-t(x)
-

A1. Looking at source code of these functions, we can see that both of these are generic, and we can confirm the same using sloop:

-
-t
-#> function (x) 
-#> UseMethod("t")
-#> <bytecode: 0x105af69f0>
-#> <environment: namespace:base>
-sloop::is_s3_generic("t")
-#> [1] TRUE
-
-t.test
-#> function (x, ...) 
-#> UseMethod("t.test")
-#> <bytecode: 0x103f412d0>
-#> <environment: namespace:stats>
-sloop::is_s3_generic("t.test")
-#> [1] TRUE
-

Looking at the S3 dispatch, we can see that since R can’t find S3 method for test class for generic function t(), it dispatches the default method, which converts the structure to a matrix:

-
-x <- structure(1:10, class = "test")
-t(x)
-#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
-#> [1,]    1    2    3    4    5    6    7    8    9    10
-#> attr(,"class")
-#> [1] "test"
-s3_dispatch(t(x))
-#> => t.test
-#>  * t.default
-

The same behaviour can be observed with a vector:

-
-t(1:10)
-#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
-#> [1,]    1    2    3    4    5    6    7    8    9    10
-

Q2. What generics does the table class have methods for?

-

A2. The table class have methods for the following generics:

-
-s3_methods_class("table")
-#> # A tibble: 11 Γ— 4
-#>    generic       class visible source             
-#>    <chr>         <chr> <lgl>   <chr>              
-#>  1 [             table TRUE    base               
-#>  2 aperm         table TRUE    base               
-#>  3 as_tibble     table FALSE   registered S3method
-#>  4 as.data.frame table TRUE    base               
-#>  5 Axis          table FALSE   registered S3method
-#>  6 lines         table FALSE   registered S3method
-#>  7 plot          table FALSE   registered S3method
-#>  8 points        table FALSE   registered S3method
-#>  9 print         table TRUE    base               
-#> 10 summary       table TRUE    base               
-#> 11 tail          table FALSE   registered S3method
-

Q3. What generics does the ecdf class have methods for?

-

A3. The ecdf class have methods for the following generics:

-
-s3_methods_class("ecdf")
-#> # A tibble: 4 Γ— 4
-#>   generic  class visible source             
-#>   <chr>    <chr> <lgl>   <chr>              
-#> 1 plot     ecdf  TRUE    stats              
-#> 2 print    ecdf  FALSE   registered S3method
-#> 3 quantile ecdf  FALSE   registered S3method
-#> 4 summary  ecdf  FALSE   registered S3method
-

Q4. Which base generic has the greatest number of defined methods?

-

A4. To answer this question, first, let’s list all functions base has and only retain the generics.

-
-# getting all functions names
-objs <- mget(ls("package:base", all = TRUE), inherits = TRUE)
-funs <- Filter(is.function, objs)
-
-# extracting only generics
-genFuns <- names(funs) %>%
-  purrr::keep(~ sloop::is_s3_generic(.x))
-

Now it’s a simple matter of counting number of methods per generic and ordering the data frame in descending order of this count:

-
-purrr::map_dfr(
-  genFuns,
-  ~ s3_methods_generic(.)
-) %>%
-  dplyr::group_by(generic) %>%
-  dplyr::tally() %>%
-  dplyr::arrange(desc(n))
-#> # A tibble: 120 Γ— 2
-#>    generic           n
-#>    <chr>         <int>
-#>  1 print           286
-#>  2 format          132
-#>  3 [                53
-#>  4 summary          39
-#>  5 as.character     37
-#>  6 as.data.frame    31
-#>  7 plot             30
-#>  8 [[               27
-#>  9 [<-              17
-#> 10 $                15
-#> # … with 110 more rows
-

This reveals that the base generic function with most methods is print().

-

Q5. Carefully read the documentation for UseMethod() and explain why the following code returns the results that it does. What two usual rules of function evaluation does UseMethod() violate?

-
-g <- function(x) {
-  x <- 10
-  y <- 10
-  UseMethod("g")
-}
-g.default <- function(x) c(x = x, y = y)
-x <- 1
-y <- 1
-g(x)
-#>  x  y 
-#>  1 10
-

A5. If called directly, g.default() method takes x value from argument and y from the global environment:

-
-g.default(x)
-#> x y 
-#> 1 1
-

But, if g() function is called, it takes the x from argument, but comes from function environment:

-
-g(x)
-#>  x  y 
-#>  1 10
-

The docs for ?UseMethod() clarify why this is the case:

-
-

Any local variables defined before the call to UseMethod are retained

-
-

That is, when UseMethod() calls g.default(), variables defined inside the generic are also available to g.default() method. The arguments supplied to the function are passed on as is, however, and cannot be affected by code inside the generic.

-

Two rules of function evaluation violated by UseMethod():

-
    -
  • Name masking
  • -
  • A fresh start
  • -
-

Q6. What are the arguments to [? Why is this a hard question to answer?

-

A6. It is difficult to say how many formal arguments the subsetting [ operator has because it is a generic function with methods for vectors, matrices, arrays, lists, etc., and these different methods have different number of arguments:

-
-s3_methods_generic("[") %>%
-  dplyr::filter(source == "base")
-#> # A tibble: 17 Γ— 4
-#>    generic class           visible source
-#>    <chr>   <chr>           <lgl>   <chr> 
-#>  1 [       AsIs            TRUE    base  
-#>  2 [       data.frame      TRUE    base  
-#>  3 [       Date            TRUE    base  
-#>  4 [       difftime        TRUE    base  
-#>  5 [       Dlist           TRUE    base  
-#>  6 [       DLLInfoList     TRUE    base  
-#>  7 [       factor          TRUE    base  
-#>  8 [       hexmode         TRUE    base  
-#>  9 [       listof          TRUE    base  
-#> 10 [       noquote         TRUE    base  
-#> 11 [       numeric_version TRUE    base  
-#> 12 [       octmode         TRUE    base  
-#> 13 [       POSIXct         TRUE    base  
-#> 14 [       POSIXlt         TRUE    base  
-#> 15 [       simple.list     TRUE    base  
-#> 16 [       table           TRUE    base  
-#> 17 [       warnings        TRUE    base
-

We can sample a few of them to see the wide variation in the number of formal arguments:

-
-# table
-names(formals(`[.table`))
-#> [1] "x"    "i"    "j"    "..."  "drop"
-
-# Date
-names(formals(`[.Date`))
-#> [1] "x"    "..."  "drop"
-
-# data frame
-names(formals(`[.data.frame`))
-#> [1] "x"    "i"    "j"    "drop"
-
-# etc.
-
-
-

-13.4 Object styles (Exercises 13.5.1) -

-

Q1. Categorise the objects returned by lm(), factor(), table(), as.Date(), as.POSIXct() ecdf(), ordered(), I() into the styles described above.

-

A1. Objects returned by these functions can be categorized as follows:

-
    -
  • Vector style objects (length represents no. of observations)
  • -
-

factor()

-
-factor_obj <- factor(c("a", "b"))
-length(factor_obj)
-#> [1] 2
-length(unclass(factor_obj))
-#> [1] 2
-

table()

-
-tab_object <- table(mtcars$am)
-length(tab_object)
-#> [1] 2
-length(unlist(tab_object))
-#> [1] 2
-

as.Date()

-
-date_obj <- as.Date("02/27/92", "%m/%d/%y")
-length(date_obj)
-#> [1] 1
-length(unclass(date_obj))
-#> [1] 1
-

as.POSIXct()

-
-posix_obj <- as.POSIXct(1472562988, origin = "1960-01-01")
-length(posix_obj)
-#> [1] 1
-length(unclass(posix_obj))
-#> [1] 1
-

ordered()

-
-ordered_obj <- ordered(factor(c("a", "b")))
-length(ordered_obj)
-#> [1] 2
-length(unclass(ordered_obj))
-#> [1] 2
-
    -
  • Record style objects (equi-length vectors to represent object components)
  • -
-

None.

-
    -
  • Dataframe style objects (Record style but two-dimensions)
  • -
-

None.

-
    -
  • Scalar objects (a list to represent a single thing)
  • -
-

lm() (represent one regression model)

-
-lm_obj <- lm(wt ~ mpg, mtcars)
-length(lm_obj)
-#> [1] 12
-length(unclass(lm_obj))
-#> [1] 12
-

ecdf() (represents one distribution)

-
-ecdf_obj <- ecdf(rnorm(12))
-length(ecdf_obj)
-#> [1] 1
-length(unclass(ecdf_obj))
-#> [1] 1
-

I() is special: -It just adds a new class to the object to indicate that it should be treated as is.

-
-x <- ecdf(rnorm(12))
-class(x)
-#> [1] "ecdf"     "stepfun"  "function"
-class(I(x))
-#> [1] "AsIs"     "ecdf"     "stepfun"  "function"
-

Therefore, the object style would be the same as the superclass’ object style.

-

Q2. What would a constructor function for lm objects, new_lm(), look like? Use ?lm and experimentation to figure out the required fields and their types.

-

A2. The lm object is a scalar object, i.e.Β this object contains a named list of atomic vectors of varying lengths and types to represent a single thing (a regression model).

-
-mod <- lm(wt ~ mpg, mtcars)
-
-typeof(mod)
-#> [1] "list"
-
-attributes(mod)
-#> $names
-#>  [1] "coefficients"  "residuals"     "effects"      
-#>  [4] "rank"          "fitted.values" "assign"       
-#>  [7] "qr"            "df.residual"   "xlevels"      
-#> [10] "call"          "terms"         "model"        
-#> 
-#> $class
-#> [1] "lm"
-
-purrr::map_chr(unclass(mod), typeof)
-#>  coefficients     residuals       effects          rank 
-#>      "double"      "double"      "double"     "integer" 
-#> fitted.values        assign            qr   df.residual 
-#>      "double"     "integer"        "list"     "integer" 
-#>       xlevels          call         terms         model 
-#>        "list"    "language"    "language"        "list"
-
-purrr::map_int(unclass(mod), length)
-#>  coefficients     residuals       effects          rank 
-#>             2            32            32             1 
-#> fitted.values        assign            qr   df.residual 
-#>            32             2             5             1 
-#>       xlevels          call         terms         model 
-#>             0             3             3             2
-

Based on this information, we can write a new constructor for this object:

-
-new_lm <- function(coefficients,
-                   residuals,
-                   effects,
-                   rank,
-                   fitted.values,
-                   assign,
-                   qr,
-                   df.residual,
-                   xlevels,
-                   call,
-                   terms,
-                   model) {
-  stopifnot(
-    is.double(coefficients),
-    is.double(residuals),
-    is.double(effects),
-    is.integer(rank),
-    is.double(fitted.values),
-    is.integer(assign),
-    is.list(qr),
-    is.integer(df.residual),
-    is.list(xlevels),
-    is.language(call),
-    is.language(terms),
-    is.list(model)
-  )
-
-  structure(
-    list(
-      coefficients  = coefficients,
-      residuals     = residuals,
-      effects       = effects,
-      rank          = rank,
-      fitted.values = fitted.values,
-      assign        = assign,
-      qr            = qr,
-      df.residual   = df.residual,
-      xlevels       = xlevels,
-      call          = call,
-      terms         = terms,
-      model         = model
-    ),
-    class = "lm"
-  )
-}
-
-
-

-13.5 Inheritance (Exercises 13.6.3) -

-

Q1. How does [.Date support subclasses? How does it fail to support subclasses?

-

A1. The [.Date method is defined as follows:

-
-sloop::s3_get_method("[.Date")
-#> function (x, ..., drop = TRUE) 
-#> {
-#>     .Date(NextMethod("["), oldClass(x))
-#> }
-#> <bytecode: 0x103bd4370>
-#> <environment: namespace:base>
-

The .Date function looks like this:

-
-.Date
-#> function (xx, cl = "Date") 
-#> `class<-`(xx, cl)
-#> <bytecode: 0x105d0ff88>
-#> <environment: namespace:base>
-

Here, oldClass is the same as class().

-

Therefore, by reading this code, we can surmise that:

-
    -
  • -[.Date supports subclasses by preserving the class of the input.
  • -
  • -[.Date fails to support subclasses by not preserving the attributes of the input.
  • -
-

For example,

-
-x <- structure(Sys.Date(), name = "myName", class = c("subDate", "Date"))
-
-# `$name` is gone
-attributes(x[1])
-#> $class
-#> [1] "subDate" "Date"
-
-x[1]
-#> [1] "2022-11-12"
-

Q2. R has two classes for representing date time data, POSIXct and POSIXlt, which both inherit from POSIXt. Which generics have different behaviours for the two classes? Which generics share the same behaviour?

-

A2. First, let’s demonstrate that POSIXct and POSIXlt are indeed subclasses and POSIXt is the superclass.

-
-dt_lt <- as.POSIXlt(Sys.time(), "GMT")
-class(dt_lt)
-#> [1] "POSIXlt" "POSIXt"
-
-dt_ct <- as.POSIXct(Sys.time(), "GMT")
-class(dt_ct)
-#> [1] "POSIXct" "POSIXt"
-
-dt_t <- structure(dt_ct, class = "POSIXt")
-class(dt_t)
-#> [1] "POSIXt"
-

Remember that the way S3 method dispatch works, if a generic has a method for superclass, then that method is also inherited by the subclass.

-

We can extract a vector of all generics supported by both sub- and super-classes:

-
-(t_generics <- s3_methods_class("POSIXt")$generic)
-#>  [1] "-"            "+"            "all.equal"   
-#>  [4] "as.character" "Axis"         "cut"         
-#>  [7] "diff"         "hist"         "is.numeric"  
-#> [10] "julian"       "Math"         "months"      
-#> [13] "Ops"          "pretty"       "quantile"    
-#> [16] "quarters"     "round"        "seq"         
-#> [19] "str"          "trunc"        "weekdays"
-
-(lt_generics <- s3_methods_class("POSIXlt")$generic)
-#>  [1] "["             "[["            "[[<-"         
-#>  [4] "[<-"           "anyNA"         "as.data.frame"
-#>  [7] "as.Date"       "as.double"     "as.list"      
-#> [10] "as.matrix"     "as.POSIXct"    "as.vector"    
-#> [13] "c"             "duplicated"    "format"       
-#> [16] "is.na"         "length"        "length<-"     
-#> [19] "mean"          "names"         "names<-"      
-#> [22] "print"         "rep"           "sort"         
-#> [25] "summary"       "Summary"       "unique"       
-#> [28] "weighted.mean" "xtfrm"
-
-(ct_generics <- s3_methods_class("POSIXct")$generic)
-#>  [1] "["             "[["            "[<-"          
-#>  [4] "as.data.frame" "as.Date"       "as.list"      
-#>  [7] "as.POSIXlt"    "c"             "format"       
-#> [10] "length<-"      "mean"          "print"        
-#> [13] "rep"           "split"         "summary"      
-#> [16] "Summary"       "weighted.mean" "xtfrm"
-

Methods which are specific to the subclasses:

-
-union(lt_generics, ct_generics)
-#>  [1] "["             "[["            "[[<-"         
-#>  [4] "[<-"           "anyNA"         "as.data.frame"
-#>  [7] "as.Date"       "as.double"     "as.list"      
-#> [10] "as.matrix"     "as.POSIXct"    "as.vector"    
-#> [13] "c"             "duplicated"    "format"       
-#> [16] "is.na"         "length"        "length<-"     
-#> [19] "mean"          "names"         "names<-"      
-#> [22] "print"         "rep"           "sort"         
-#> [25] "summary"       "Summary"       "unique"       
-#> [28] "weighted.mean" "xtfrm"         "as.POSIXlt"   
-#> [31] "split"
-

Let’s see an example:

-
-s3_dispatch(is.na(dt_lt))
-#> => is.na.POSIXlt
-#>    is.na.POSIXt
-#>    is.na.default
-#>  * is.na (internal)
-
-s3_dispatch(is.na(dt_ct))
-#>    is.na.POSIXct
-#>    is.na.POSIXt
-#>    is.na.default
-#> => is.na (internal)
-
-s3_dispatch(is.na(dt_t))
-#>    is.na.POSIXt
-#>    is.na.default
-#> => is.na (internal)
-

Methods which are inherited by subclasses from superclass:

-
-setdiff(t_generics, union(lt_generics, ct_generics))
-#>  [1] "-"            "+"            "all.equal"   
-#>  [4] "as.character" "Axis"         "cut"         
-#>  [7] "diff"         "hist"         "is.numeric"  
-#> [10] "julian"       "Math"         "months"      
-#> [13] "Ops"          "pretty"       "quantile"    
-#> [16] "quarters"     "round"        "seq"         
-#> [19] "str"          "trunc"        "weekdays"
-

Let’s see one example generic:

-
-s3_dispatch(is.numeric(dt_lt))
-#>    is.numeric.POSIXlt
-#> => is.numeric.POSIXt
-#>    is.numeric.default
-#>  * is.numeric (internal)
-
-s3_dispatch(is.numeric(dt_ct))
-#>    is.numeric.POSIXct
-#> => is.numeric.POSIXt
-#>    is.numeric.default
-#>  * is.numeric (internal)
-
-s3_dispatch(is.numeric(dt_t))
-#> => is.numeric.POSIXt
-#>    is.numeric.default
-#>  * is.numeric (internal)
-

Q3. What do you expect this code to return? What does it actually return? Why?

-
-generic2 <- function(x) UseMethod("generic2")
-generic2.a1 <- function(x) "a1"
-generic2.a2 <- function(x) "a2"
-generic2.b <- function(x) {
-  class(x) <- "a1"
-  NextMethod()
-}
-
-generic2(structure(list(), class = c("b", "a2")))
-

A3. Naively, we would expect for this code to return "a1", but it actually returns "a2":

-
-generic2 <- function(x) UseMethod("generic2")
-generic2.a1 <- function(x) "a1"
-generic2.a2 <- function(x) "a2"
-generic2.b <- function(x) {
-  class(x) <- "a1"
-  NextMethod()
-}
-
-generic2(structure(list(), class = c("b", "a2")))
-#> [1] "a2"
-

S3 dispatch explains why:

-
-sloop::s3_dispatch(generic2(structure(list(), class = c("b", "a2"))))
-#> => generic2.b
-#> -> generic2.a2
-#>    generic2.default
-

As mentioned in the book, the UseMethod() function

-
-

tracks the list of potential next methods with a special variable, which means that modifying the object that’s being dispatched upon will have no impact on which method gets called next.

-
-

This special variable is .Class:

-
-

.Class is a character vector of classes used to find the next method. NextMethod adds an attribute β€œprevious” to .Class giving the .Class last used for dispatch, and shifts .Class along to that used for dispatch.

-
-

So, we can print .Class to confirm that adding a new class to x indeed doesn’t change .Class, and therefore dispatch occurs on "a2" class:

-
-generic2.b <- function(x) {
-  message(paste0("before: ", paste0(.Class, collapse = ", ")))
-  class(x) <- "a1"
-  message(paste0("after: ", paste0(.Class, collapse = ", ")))
-
-  NextMethod()
-}
-
-invisible(generic2(structure(list(), class = c("b", "a2"))))
-#> before: b, a2
-#> after: b, a2
-
-
-

-13.6 Dispatch details (Exercises 13.7.5) -

-

Q1. Explain the differences in dispatch below:

-
-length.integer <- function(x) 10
-
-x1 <- 1:5
-class(x1)
-#> [1] "integer"
-s3_dispatch(length(x1))
-#>  * length.integer
-#>    length.numeric
-#>    length.default
-#> => length (internal)
-
-x2 <- structure(x1, class = "integer")
-class(x2)
-#> [1] "integer"
-s3_dispatch(length(x2))
-#> => length.integer
-#>    length.default
-#>  * length (internal)
-

A1. The differences in the dispatch are due to classes of arguments:

-
-s3_class(x1)
-#> [1] "integer" "numeric"
-
-s3_class(x2)
-#> [1] "integer"
-

x1 has an implicit class integer but it inherits from numeric, while x2 is explicitly assigned the class integer.

-

Q2. What classes have a method for the Math group generic in base R? Read the source code. How do the methods work?

-

A2. The following classes have a method for the Math group generic in base R:

-
-s3_methods_generic("Math") %>%
-  dplyr::filter(source == "base")
-#> # A tibble: 5 Γ— 4
-#>   generic class      visible source
-#>   <chr>   <chr>      <lgl>   <chr> 
-#> 1 Math    data.frame TRUE    base  
-#> 2 Math    Date       TRUE    base  
-#> 3 Math    difftime   TRUE    base  
-#> 4 Math    factor     TRUE    base  
-#> 5 Math    POSIXt     TRUE    base
-

Reading source code for a few of the methods:

-

Math.factor() and Math.Date() provide only error message:

-
-Math.factor <- function(x, ...) {
-  stop(gettextf("%s not meaningful for factors", sQuote(.Generic)))
-}
-
-Math.Date <- function(x, ...) {
-  stop(gettextf("%s not defined for \"Date\" objects", .Generic),
-    domain = NA
-  )
-}
-

Math.data.frame() is defined as follows (except the first line of code, which I have deliberately added):

-
-Math.data.frame <- function(x, ...) {
-  message(paste0("Environment variable `.Generic` set to: ", .Generic))
-
-  mode.ok <- vapply(x, function(x) {
-    is.numeric(x) || is.logical(x) || is.complex(x)
-  }, NA)
-
-  if (all(mode.ok)) {
-    x[] <- lapply(X = x, FUN = .Generic, ...)
-    return(x)
-  } else {
-    vnames <- names(x)
-    if (is.null(vnames)) vnames <- seq_along(x)
-    stop(
-      "non-numeric-alike variable(s) in data frame: ",
-      paste(vnames[!mode.ok], collapse = ", ")
-    )
-  }
-}
-

As can be surmised from the code: the method checks that all elements are of the same and expected type.

-

If so, it applies the generic (tracked via the environment variable .Generic) to each element of the list of atomic vectors that makes up a data frame:

-
-df1 <- data.frame(x = 1:2, y = 3:4)
-sqrt(df1)
-#> Environment variable `.Generic` set to: sqrt
-#>          x        y
-#> 1 1.000000 1.732051
-#> 2 1.414214 2.000000
-

If not, it produces an error:

-
-df2 <- data.frame(x = c(TRUE, FALSE), y = c("a", "b"))
-abs(df2)
-#> Environment variable `.Generic` set to: abs
-#> Error in Math.data.frame(df2): non-numeric-alike variable(s) in data frame: y
-

Q3. Math.difftime() is more complicated than I described. Why?

-

A3. Math.difftime() source code looks like the following:

-
-Math.difftime <- function(x, ...) {
-  switch(.Generic,
-    "abs" = ,
-    "sign" = ,
-    "floor" = ,
-    "ceiling" = ,
-    "trunc" = ,
-    "round" = ,
-    "signif" = {
-      units <- attr(x, "units")
-      .difftime(NextMethod(), units)
-    },
-    ### otherwise :
-    stop(gettextf("'%s' not defined for \"difftime\" objects", .Generic),
-      domain = NA
-    )
-  )
-}
-

This group generic is a bit more complicated because it produces an error for some generics, while it works for others.

-
-
-

-13.7 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    assertthat    0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>    codetools     0.2-18     2020-11-04 [1] CRAN (R 4.2.2)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>    crayon        1.5.2      2022-09-29 [1] CRAN (R 4.2.1)
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    DBI           1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    dplyr       * 1.0.10     2022-09-01 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    purrr       * 0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang         1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>    sloop       * 1.0.1      2019-02-17 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble        3.1.8.9002 2022-10-16 [1] local
-#>    tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
- -
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/s4.html b/_book/s4.html deleted file mode 100644 index 4d51206b..00000000 --- a/_book/s4.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - -Chapter 15 S4 | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-15 S4 -

-
-

-15.1 Basics (Exercises 15.2.1) -

-
-

Q1. lubridate::period() returns an S4 class. What slots does it have? What class is each slot? What accessors does it provide?

-

A1. Let’s first create an instance of Period class:

-
-library(lubridate)
-x <- lubridate::period(c(2, 43, 6), c("hour", "second", "minute"))
-x
-#> [1] "2H 6M 43S"
-

It has the following slots:

-
-slotNames(x)
-#> [1] ".Data"  "year"   "month"  "day"    "hour"   "minute"
-

Additionally, the base type of each slot (numeric) can be seen in str() output:

-
-str(x)
-#> Formal class 'Period' [package "lubridate"] with 6 slots
-#>   ..@ .Data : num 43
-#>   ..@ year  : num 0
-#>   ..@ month : num 0
-#>   ..@ day   : num 0
-#>   ..@ hour  : num 2
-#>   ..@ minute: num 6
-

The lubridate package provides accessors for all slots:

-
-year(x)
-#> [1] 0
-month(x)
-#> [1] 0
-day(x)
-#> [1] 0
-hour(x)
-#> [1] 2
-minute(x)
-#> [1] 6
-second(x)
-#> [1] 43
-
-

Q2. What other ways can you find help for a method? Read ?"?" and summarise the details.

-

A2. The "?" operator allows access to documentation in three ways. To demonstrate different ways to access documentation, let’s define a new S4 class.

-
-pow <- function(x, exp) c(x, exp)
-setGeneric("pow")
-#> [1] "pow"
-setMethod("pow", c("numeric", "numeric"), function(x, exp) x^exp)
-

Ways to access documentation:

-
    -
  • The general documentation for a generic can be found with ?topic:
  • -
-
-?pow
-
    -
  • The expression type?topic will look for the overall documentation methods for the function f.
  • -
-
-?pow # produces the function documentation
-
-methods?pow # looks for the overall methods documentation
-
-
-
-

-15.2 Classes (Exercises 15.3.6) -

-
-

Q1. Extend the Person class with fields to match utils::person(). Think about what slots you will need, what class each slot should have, and what you’ll need to check in your validity method.

-

A1. The code below extends the Person class described in the book to match more closely with utils::person().

-
-setClass("Person",
-  slots = c(
-    age     = "numeric",
-    given   = "character",
-    family  = "character",
-    middle  = "character",
-    email   = "character",
-    role    = "character",
-    comment = "character"
-  ),
-  prototype = list(
-    age     = NA_real_,
-    given   = NA_character_,
-    family  = NA_character_,
-    middle  = NA_character_,
-    email   = NA_character_,
-    role    = NA_character_,
-    comment = NA_character_
-  )
-)
-
-# Helper function to create an instance of the `Person` class
-Person <- function(given,
-                   family,
-                   middle = NA_character_,
-                   age = NA_real_,
-                   email = NA_character_,
-                   role = NA_character_,
-                   comment = NA_character_) {
-  age <- as.double(age)
-
-  new("Person",
-    age     = age,
-    given   = given,
-    family  = family,
-    middle  = middle,
-    email   = email,
-    role    = role,
-    comment = comment
-  )
-}
-
-# Validator to ensure that each slot is of length one and that the specified
-# role is one of the possible roles
-setValidity("Person", function(object) {
-  invalid_length <- NULL
-  slot_lengths <- c(
-    length(object@age),
-    length(object@given),
-    length(object@middle),
-    length(object@family),
-    length(object@email),
-    length(object@comment)
-  )
-
-  if (any(slot_lengths > 1L)) {
-    invalid_length <- "\nFollowing slots must be of length 1:\n @age, @given, @family, @middle, @email, @comment"
-  }
-
-  possible_roles <- c(
-    NA_character_, "aut", "com", "cph", "cre", "ctb", "ctr", "dtc", "fnd", "rev", "ths", "trl"
-  )
-
-  if (any(!object@role %in% possible_roles)) {
-    invalid_length <- paste(
-      invalid_length,
-      "\nSlot @role(s) must be one of the following:\n",
-      paste(possible_roles, collapse = ", ")
-    )
-  }
-
-  if (!is.null(invalid_length)) {
-    return(invalid_length)
-  } else {
-    return(TRUE)
-  }
-})
-#> Class "Person" [in ".GlobalEnv"]
-#> 
-#> Slots:
-#>                                                         
-#> Name:        age     given    family    middle     email
-#> Class:   numeric character character character character
-#>                           
-#> Name:       role   comment
-#> Class: character character
-

Let’s make sure that validation works as expected:

-
-# length of first argument not 1
-Person(c("Indrajeet", "Surendra"), "Patil")
-#> Error in validObject(.Object): invalid class "Person" object: 
-#> Following slots must be of length 1:
-#>  @age, @given, @family, @middle, @email, @comment
-
-# role not recognized
-Person("Indrajeet", "Patil", role = "xyz")
-#> Error in validObject(.Object): invalid class "Person" object:  
-#> Slot @role(s) must be one of the following:
-#>  NA, aut, com, cph, cre, ctb, ctr, dtc, fnd, rev, ths, trl
-
-# all okay
-Person("Indrajeet", "Patil", role = c("aut", "cph"))
-#> An object of class "Person"
-#> Slot "age":
-#> [1] NA
-#> 
-#> Slot "given":
-#> [1] "Indrajeet"
-#> 
-#> Slot "family":
-#> [1] "Patil"
-#> 
-#> Slot "middle":
-#> [1] NA
-#> 
-#> Slot "email":
-#> [1] NA
-#> 
-#> Slot "role":
-#> [1] "aut" "cph"
-#> 
-#> Slot "comment":
-#> [1] NA
-
-

Q2. What happens if you define a new S4 class that doesn’t have any slots? (Hint: read about virtual classes in ?setClass.)

-

A2. If you define a new S4 class that doesn’t have any slots, it will create virtual classes:

-
-setClass("Empty")
-
-isVirtualClass("Empty")
-#> [1] TRUE
-

You can’t create an instance of this class:

-
-new("Empty")
-#> Error in new("Empty"): trying to generate an object from a virtual class ("Empty")
-

So how is this useful? As mentioned in ?setClass docs:

-
-

Classes exist for which no actual objects can be created, the virtual classes.

-

The most common and useful form of virtual class is the class union, a virtual class that is defined in a call to setClassUnion() rather than a call to setClass().

-
-

So virtual classes can still be inherited:

-
-setClass("Nothing", contains = "Empty")
-

In addition to not specifying any slots, here is another way to create virtual classes:

-
-

Calls to setClass() will also create a virtual class, either when only the Class argument is supplied (no slots or superclasses) or when the contains= argument includes the special class name "VIRTUAL".

-
-
-

Q3. Imagine you were going to reimplement factors, dates, and data frames in S4. Sketch out the setClass() calls that you would use to define the classes. Think about appropriate slots and prototype.

-

A3. The reimplementation of following classes in S4 might have definitions like the following.

-
    -
  • factor
  • -
-

For simplicity, we won’t provide all options that factor() provides. Note that x has pseudo-class ANY to accept objects of any type.

-
-setClass("Factor",
-  slots = c(
-    x       = "ANY",
-    levels  = "character",
-    ordered = "logical"
-  ),
-  prototype = list(
-    x       = character(),
-    levels  = character(),
-    ordered = FALSE
-  )
-)
-
-new("Factor", x = letters[1:3], levels = LETTERS[1:3])
-#> An object of class "Factor"
-#> Slot "x":
-#> [1] "a" "b" "c"
-#> 
-#> Slot "levels":
-#> [1] "A" "B" "C"
-#> 
-#> Slot "ordered":
-#> [1] FALSE
-
-new("Factor", x = 1:3, levels = letters[1:3])
-#> An object of class "Factor"
-#> Slot "x":
-#> [1] 1 2 3
-#> 
-#> Slot "levels":
-#> [1] "a" "b" "c"
-#> 
-#> Slot "ordered":
-#> [1] FALSE
-
-new("Factor", x = c(TRUE, FALSE, TRUE), levels = c("x", "y", "x"))
-#> An object of class "Factor"
-#> Slot "x":
-#> [1]  TRUE FALSE  TRUE
-#> 
-#> Slot "levels":
-#> [1] "x" "y" "x"
-#> 
-#> Slot "ordered":
-#> [1] FALSE
-
    -
  • Date
  • -
-

Just like the base-R version, this will have only integer values.

-
-setClass("Date2",
-  slots = list(
-    data = "integer"
-  ),
-  prototype = list(
-    data = integer()
-  )
-)
-
-new("Date2", data = 1342L)
-#> An object of class "Date2"
-#> Slot "data":
-#> [1] 1342
-
    -
  • data.frame
  • -
-

The tricky part is supporting the ... argument of data.frame(). For this, we can let the users pass a (named) list.

-
-setClass("DataFrame",
-  slots = c(
-    data      = "list",
-    row.names = "character"
-  ),
-  prototype = list(
-    data      = list(),
-    row.names = character(0L)
-  )
-)
-
-new("DataFrame", data = list(x = c("a", "b"), y = c(1L, 2L)))
-#> An object of class "DataFrame"
-#> Slot "data":
-#> $x
-#> [1] "a" "b"
-#> 
-#> $y
-#> [1] 1 2
-#> 
-#> 
-#> Slot "row.names":
-#> character(0)
-
-
-
-

-15.3 Generics and methods (Exercises 15.4.5) -

-
-

Q1. Add age() accessors for the Person class.

-

A1. We first should define a generic and then a method for our class:

-
-Indra <- Person("Indrajeet", "Patil", role = c("aut", "cph"), age = 34)
-
-setGeneric("age", function(x) standardGeneric("age"))
-#> [1] "age"
-setMethod("age", "Person", function(x) x@age)
-
-age(Indra)
-#> [1] 34
-
-

Q2. In the definition of the generic, why is it necessary to repeat the name of the generic twice?

-

A2. Let’s look at the generic we just defined; the generic name "age" is repeated twice.

-
-setGeneric(name = "age", def = function(x) standardGeneric("age"))
-

This is because:

-
    -
  • the "age" passed to argument name provides the name for the generic
  • -
  • the "age" passed to argument def supplies the method dispatch
  • -
-

This is reminiscent of how we defined S3 generic, where we also had to repeat the name twice:

-
-age <- function(x) {
-  UseMethod("age")
-}
-
-

Q3. Why does the show() method defined in Section Show method use is(object)[[1]]? (Hint: try printing the employee subclass.)

-

A3. Because we wish to define show() method for a specific class, we need to disregard the other super-/sub-classes.

-

Always using the first element ensures that the method will be defined for the class in question:

-
-Alice <- new("Employee")
-
-is(Alice)
-#> [1] "Employee" "Person"
-
-is(Alice)[[1]]
-#> [1] "Employee"
-
-

Q4. What happens if you define a method with different argument names to the generic?

-

A4. Let’s experiment with the method we defined in Q1. to study this behavior.

-

The original method that worked as expected since the argument name between generic and method matched:

-
-setMethod("age", "Person", function(x) x@age)
-

If this is not the case, we either get a warning or get an error depending on which and how many arguments have been specified:

-
-setMethod("age", "Person", function(object) object@age)
-#> Warning: For function 'age', signature 'Person': argument in
-#> method definition changed from (object) to (x)
-
-setMethod("age", "Person", function(object, x) object@age)
-#> Error in rematchDefinition(definition, fdef, mnames, fnames, signature): methods can add arguments to the generic 'age' only if '...' is an argument to the generic
-
-setMethod("age", "Person", function(...) ...elt(1)@age)
-#> Warning: For function 'age', signature 'Person': argument in
-#> method definition changed from (...) to (x)
-
-setMethod("age", "Person", function(x, ...) x@age)
-#> Error in rematchDefinition(definition, fdef, mnames, fnames, signature): methods can add arguments to the generic 'age' only if '...' is an argument to the generic
-
-
-
-

-15.4 Method dispatch (Exercises 15.5.5) -

-
-

Q1. Draw the method graph for f(πŸ˜…, 😽).

-

A1. I don’t how to prepare the visual illustrations used in the book, so I am linking to the illustration in the official solution manual:

-
-
-

Q2. Draw the method graph for f(πŸ˜ƒ, πŸ˜‰, πŸ˜™).

-

A2. I don’t how to prepare the visual illustrations used in the book, so I am linking to the illustration in the official solution manual:

-
-
-

Q3. Take the last example which shows multiple dispatch over two classes that use multiple inheritance. What happens if you define a method for all terminal classes? Why does method dispatch not save us much work here?

-

A3. Because one class has distance of 2 to all terminal nodes and the other four have distance of 1 to two terminal nodes each, this will introduce ambiguity.

-

Method dispatch not save us much work here because to resolve this ambiguity we have to define five more methods (one per class combination).

-
-
-
-

-15.5 S4 and S3 (Exercises 15.6.3) -

-
-

Q1. What would a full setOldClass() definition look like for an ordered factor (i.e.Β add slots and prototype the definition above)?

-

A1. We can register the old-style/S3 ordered class to a formally defined class using setOldClass().

-
-setClass("factor",
-  contains = "integer",
-  slots = c(
-    levels = "character"
-  ),
-  prototype = structure(
-    integer(),
-    levels = character()
-  )
-)
-setOldClass("factor", S4Class = "factor")
-#> Warning in rm(list = what, pos = classWhere): object
-#> '.__C__factor' not found
-
-setClass("Ordered",
-  contains = "factor",
-  slots = c(
-    levels  = "character",
-    ordered = "logical"
-  ),
-  prototype = structure(
-    integer(),
-    levels  = character(),
-    ordered = logical()
-  )
-)
-
-setOldClass("ordered", S4Class = "Ordered")
-

Let’s use it to see if it works as expected.

-
-x <- new("Ordered", 1L:4L, levels = letters[1:4], ordered = TRUE)
-
-x
-#> Object of class "Ordered"
-#> [1] a b c d
-#> Levels: a b c d
-#> Slot "ordered":
-#> [1] TRUE
-
-str(x)
-#> Formal class 'Ordered' [package ".GlobalEnv"] with 4 slots
-#>   ..@ .Data   : int [1:4] 1 2 3 4
-#>   ..@ levels  : chr [1:4] "a" "b" "c" "d"
-#>   ..@ ordered : logi TRUE
-#>   ..@ .S3Class: chr "factor"
-
-class(x)
-#> [1] "Ordered"
-#> attr(,"package")
-#> [1] ".GlobalEnv"
-
-

Q2. Define a length method for the Person class.

-

A2. Because our Person class can be used to create objects that represent multiple people, let’s say the length() method returns how many persons are in the object.

-
-Friends <- new("Person", name = c("Vishu", "Aditi"))
-

We can define an S3 method for this class:

-
-length.Person <- function(x) length(x@name)
-
-length(Friends)
-#> [1] 2
-

Alternatively, we can also write S4 method:

-
-setMethod("length", "Person", function(x) length(x@name))
-
-length(Friends)
-#> [1] 2
-
-
-
-

-15.6 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    assertthat    0.2.1      2019-03-21 [1] CRAN (R 4.2.0)
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>    crayon        1.5.2      2022-09-29 [1] CRAN (R 4.2.1)
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    emo           0.0.0.9000 2022-05-17 [1] Github (hadley/emo@3f03b11)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.1)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    highr         0.9        2021-04-16 [1] CRAN (R 4.2.0)
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lubridate   * 1.9.0      2022-11-06 [1] CRAN (R 4.2.2)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    purrr         0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang         1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    timechange  * 0.1.1      2022-11-04 [1] CRAN (R 4.2.2)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/search.json b/_book/search.json deleted file mode 100644 index 8fceaddf..00000000 --- a/_book/search.json +++ /dev/null @@ -1 +0,0 @@ -[{"path":"index.html","id":"about","chapter":"About","heading":"About","text":"book provides solutions exercises Hadley Wickham’s Advanced R (2nd edition) book.started working book part process learn solving book’s exercises. comparing solutions official solutions manual, realized solutions took different approaches least explained differently. ’m sharing solutions case others might find another perspective explanation official solution manual helpful building understanding.Although tried make sure solutions correct, blame inaccuracies lies solely . ’d much appreciate suggestions corrections.","code":""},{"path":"introduction.html","id":"introduction","chapter":"1 Introduction","heading":"1 Introduction","text":"exercises.","code":""},{"path":"names-and-values.html","id":"names-and-values","chapter":"2 Names and values","heading":"2 Names and values","text":"Loading needed libraries:","code":"\nlibrary(lobstr)"},{"path":"names-and-values.html","id":"binding-basics-exercise-2.2.2","chapter":"2 Names and values","heading":"2.1 Binding basics (Exercise 2.2.2)","text":"Q1. Explain relationship , b, c d following code:A1. names (, b, c) values point object memory, can seen identical memory addresses:Except d, different object, even value , b, c:Q2. following code accesses mean function multiple ways. point underlying function object? Verify lobstr::obj_addr().A2. listed function calls point underlying function object memory, shown object’s memory address:Q3. default, base R data import functions, like read.csv(), automatically convert non-syntactic names syntactic ones. might problematic? option allows suppress behaviour?A3. conversion non-syntactic names syntactic ones can sometimes corrupt data. datasets may require non-syntactic names.suppress behavior, one can set check.names = FALSE.Q4. rules make.names() use convert non-syntactic names syntactic ones?A4. make.names() uses following rules convert non-syntactic names syntactic ones:prepends non-syntactic names Xit converts invalid characters (like @) .adds . suffix name reserved keywordQ5. slightly simplified rules govern syntactic names. .123e1 syntactic name? Read ?make.names full details.A5. .123e1 syntacti name parsed number, string:docs mention (emphasis mine):syntactically valid name consists letters, numbers dot underline characters starts letter dot followed number.","code":"\na <- 1:10\nb <- a\nc <- b\nd <- 1:10\nobj_addrs <- obj_addrs(list(a, b, c))\nunique(obj_addrs)\n#> [1] \"0x11a944a10\"\nobj_addr(d)\n#> [1] \"0x11a748978\"\nmean\nbase::mean\nget(\"mean\")\nevalq(mean)\nmatch.fun(\"mean\")\nobj_addrs <- obj_addrs(list(\n mean,\n base::mean,\n get(\"mean\"),\n evalq(mean),\n match.fun(\"mean\")\n))\n\nunique(obj_addrs)\n#> [1] \"0x11a25e2d8\"\nmake.names(c(\"123abc\", \"@me\", \"_yu\", \" gh\", \"else\"))\n#> [1] \"X123abc\" \"X.me\" \"X_yu\" \"X..gh\" \"else.\"\ntypeof(.123e1)\n#> [1] \"double\""},{"path":"names-and-values.html","id":"copy-on-modify-exercise-2.3.6","chapter":"2 Names and values","heading":"2.2 Copy-on-modify (Exercise 2.3.6)","text":"Q1. tracemem(1:10) useful?A1. tracemem() traces copying objects R. example:since object created memory 1:10 assigned name, can’t addressed modified R, nothing trace.Q2. Explain tracemem() shows two copies run code. Hint: carefully look difference code code shown earlier section.A2. initial atomic vector type integer, 4 (4L) type double. new copy created.Trying integer create another copy:understand still produces copy, explanation official solutions manual:Please aware running code RStudio result additional copies reference environment pane.Q3. Sketch relationship following objects:A3. can understand relationship objects looking memory addresses:learn:name references object 1:10 memory.name b bound list two references memory address .name c also bound list references b, 1:10 object (bound name).Q4. happens run code?Draw picture.A4.don’t access OmniGraffle software, including figure official solution manual:","code":"\nx <- 1:10\n\ntracemem(x)\n#> [1] \"<0x107924860>\"\n\nx <- x + 1\n\nuntracemem(x)\nobj_addr(1:10)\n#> [1] \"0x1188da070\"\n\ntracemem(1:10)\n#> [1] \"<0x1189377b0>\"\nx <- c(1L, 2L, 3L)\ntracemem(x)\n\nx[[3]] <- 4\nuntracemem(x)\nx <- c(1L, 2L, 3L)\ntypeof(x)\n#> [1] \"integer\"\ntracemem(x)\n#> [1] \"<0x106e8d248>\"\n\nx[[3]] <- 4\n#> tracemem[0x106e8d248 -> 0x105c18848]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file do.call eval eval eval eval eval.parent local \n#> tracemem[0x105c18848 -> 0x105c2ca88]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file do.call eval eval eval eval eval.parent local\nuntracemem(x)\n\ntypeof(x)\n#> [1] \"double\"\nx <- c(1L, 2L, 3L)\ntypeof(x)\n#> [1] \"integer\"\ntracemem(x)\n#> [1] \"<0x107ac8348>\"\n\nx[[3]] <- 4L\n#> tracemem[0x107ac8348 -> 0x118c7a9c8]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file do.call eval eval eval eval eval.parent local\nuntracemem(x)\n\ntypeof(x)\n#> [1] \"integer\"\na <- 1:10\nb <- list(a, a)\nc <- list(b, a, 1:10)\na <- 1:10\nb <- list(a, a)\nc <- list(b, a, 1:10)\n\nref(a)\n#> [1:0x107d1fd98] \n\nref(b)\n#> β–ˆ [1:0x107d61d88] \n#> β”œβ”€[2:0x107d1fd98] \n#> └─[2:0x107d1fd98]\n\nref(c)\n#> β–ˆ [1:0x107d6aa78] \n#> β”œβ”€β–ˆ [2:0x107d61d88] \n#> β”‚ β”œβ”€[3:0x107d1fd98] \n#> β”‚ └─[3:0x107d1fd98] \n#> β”œβ”€[3:0x107d1fd98] \n#> └─[4:0x106f78ca8] \nx <- list(1:10)\nx[[2]] <- x\nx <- list(1:10)\nx\n#> [[1]]\n#> [1] 1 2 3 4 5 6 7 8 9 10\nobj_addr(x)\n#> [1] \"0x106577798\"\n\nx[[2]] <- x\nx\n#> [[1]]\n#> [1] 1 2 3 4 5 6 7 8 9 10\n#> \n#> [[2]]\n#> [[2]][[1]]\n#> [1] 1 2 3 4 5 6 7 8 9 10\nobj_addr(x)\n#> [1] \"0x1188dd148\"\n\nref(x)\n#> β–ˆ [1:0x1188dd148] \n#> β”œβ”€[2:0x10658cc00] \n#> β””β”€β–ˆ [3:0x106577798] \n#> └─[2:0x10658cc00]"},{"path":"names-and-values.html","id":"object-size-exercise-2.4.1","chapter":"2 Names and values","heading":"2.3 Object size (Exercise 2.4.1)","text":"Q1. following example, object.size(y) obj_size(y) radically different? Consult documentation object.size().A1. mentioned docs object.size():function…detect elements list shared.sizes different:Q2. Take following list. size somewhat misleading?A2. functions externally created objects R, always available part base packages, doesn’t make much sense measure size never going available.Q3. Predict output following code:A3. Correctly predicted πŸ˜‰Key pieces information keep mind make correct predictions:Size empty vectorSize single double: 8 bytesCopy--modify semantics","code":"\ny <- rep(list(runif(1e4)), 100)\n\nobject.size(y)\nobj_size(y)\ny <- rep(list(runif(1e4)), 100)\n\nobject.size(y)\n#> 8005648 bytes\n\nobj_size(y)\n#> 80.90 kB\nfuns <- list(mean, sd, var)\nobj_size(funs)\nfuns <- list(mean, sd, var)\nobj_size(funs)\n#> 17.55 kB\na <- runif(1e6)\nobj_size(a)\n\nb <- list(a, a)\nobj_size(b)\nobj_size(a, b)\n\nb[[1]][[1]] <- 10\nobj_size(b)\nobj_size(a, b)\n\nb[[2]][[1]] <- 10\nobj_size(b)\nobj_size(a, b)\na <- runif(1e6)\nobj_size(a)\n#> 8.00 MB\n\nb <- list(a, a)\nobj_size(b)\n#> 8.00 MB\nobj_size(a, b)\n#> 8.00 MB\n\nb[[1]][[1]] <- 10\nobj_size(b)\n#> 16.00 MB\nobj_size(a, b)\n#> 16.00 MB\n\nb[[2]][[1]] <- 10\nobj_size(b)\n#> 16.00 MB\nobj_size(a, b)\n#> 24.00 MB\nobj_size(double())\n#> 48 B\nobj_size(double(1))\n#> 56 B"},{"path":"names-and-values.html","id":"modify-in-place-exercise-2.5.3","chapter":"2 Names and values","heading":"2.4 Modify-in-place (Exercise 2.5.3)","text":"Q1. Explain following code doesn’t create circular list.A1. Copy--modify prevents creation circular list.Q2. Wrap two methods subtracting medians two functions, use β€˜bench’ package carefully compare speeds. performance change number columns increase?A2. Let’s first microbenchmark functions create copies varying lengths number columns.Plotting benchmarks reveals performance gets increasingly worse number data frames increases:Q3. happens attempt use tracemem() environment?A3. doesn’t work documentation tracemem() makes clear :useful trace NULL, environments, promises, weak references, external pointer objects, duplicated","code":"\nx <- list()\nx[[1]] <- x\nx <- list()\n\nobj_addr(x)\n#> [1] \"0x106c2ac38\"\n\ntracemem(x)\n#> [1] \"<0x106c2ac38>\"\n\nx[[1]] <- x\n#> tracemem[0x106c2ac38 -> 0x12a7d3a50]: eval eval eval_with_user_handlers withVisible withCallingHandlers handle timing_fn evaluate_call evaluate in_dir in_input_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file do.call eval eval eval eval eval.parent local\n\nobj_addr(x[[1]])\n#> [1] \"0x106c2ac38\"\nlibrary(bench)\nlibrary(tidyverse)\n\ngenerateDataFrame <- function(ncol) {\n as.data.frame(matrix(runif(100 * ncol), nrow = 100))\n}\n\nwithCopy <- function(ncol) {\n x <- generateDataFrame(ncol)\n medians <- vapply(x, median, numeric(1))\n\n for (i in seq_along(medians)) {\n x[[i]] <- x[[i]] - medians[[i]]\n }\n\n return(x)\n}\n\nwithoutCopy <- function(ncol) {\n x <- generateDataFrame(ncol)\n medians <- vapply(x, median, numeric(1))\n\n y <- as.list(x)\n\n for (i in seq_along(medians)) {\n y[[i]] <- y[[i]] - medians[[i]]\n }\n\n return(y)\n}\n\nbenchComparison <- function(ncol) {\n bench::mark(\n withCopy(ncol),\n withoutCopy(ncol),\n iterations = 100,\n check = FALSE\n ) %>%\n dplyr::select(expression:total_time)\n}\n\nnColList <- list(1, 10, 50, 100, 250, 500, 1000)\n\nnames(nColList) <- as.character(nColList)\n\nbenchDf <- purrr::map_dfr(\n .x = nColList,\n .f = benchComparison,\n .id = \"nColumns\"\n)\nggplot(\n benchDf,\n aes(\n x = as.numeric(nColumns),\n y = median,\n group = as.character(expression),\n color = as.character(expression)\n )\n) +\n geom_line() +\n labs(\n x = \"Number of Columns\",\n y = \"Median Execution Time (ms)\",\n colour = \"Type of function\"\n )\ne <- rlang::env(a = 1, b = \"3\")\ntracemem(e)\n#> Error in tracemem(e): 'tracemem' is not useful for promise and environment objects"},{"path":"names-and-values.html","id":"session-information","chapter":"2 Names and values","heading":"2.5 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)\n#> backports 1.4.1 2021-12-13 [1] CRAN (R 4.2.0)\n#> base * 4.2.2 2022-10-31 [?] local\n#> bench * 1.1.2 2021-11-30 [1] CRAN (R 4.2.0)\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> broom 1.0.1 2022-08-29 [1] CRAN (R 4.2.0)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cellranger 1.1.0 2016-07-27 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1)\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)\n#> dbplyr 2.2.1 2022-06-27 [1] CRAN (R 4.2.0)\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.1)\n#> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.2.0)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> farver 2.1.1 2022-07-06 [1] CRAN (R 4.2.1)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> forcats * 0.5.2 2022-08-19 [1] CRAN (R 4.2.1)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> gargle 1.2.1 2022-09-08 [1] CRAN (R 4.2.1)\n#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1)\n#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.2)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> googledrive 2.0.0 2021-07-08 [1] CRAN (R 4.2.0)\n#> googlesheets4 1.0.1 2022-08-13 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> P grid 4.2.2 2022-10-31 [1] local\n#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.1)\n#> haven 2.5.1 2022-08-22 [1] CRAN (R 4.2.0)\n#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0)\n#> hms 1.1.2 2022-08-19 [1] CRAN (R 4.2.0)\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> httr 1.4.4 2022-08-17 [1] CRAN (R 4.2.0)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> labeling 0.4.2 2020-10-20 [1] CRAN (R 4.2.0)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> lobstr * 1.1.2 2022-06-22 [1] CRAN (R 4.2.0)\n#> lubridate 1.9.0 2022-11-06 [1] CRAN (R 4.2.2)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> modelr 0.1.10 2022-11-11 [1] CRAN (R 4.2.2)\n#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0)\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)\n#> prettyunits 1.1.1 2020-01-24 [1] CRAN (R 4.2.0)\n#> profmem 0.6.0 2020-12-13 [1] CRAN (R 4.2.0)\n#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> readr * 2.1.3 2022-10-01 [1] CRAN (R 4.2.1)\n#> readxl 1.4.1 2022-08-17 [1] CRAN (R 4.2.0)\n#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.1)\n#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> rvest 1.0.3 2022-08-19 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr * 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> tibble * 3.1.8.9002 2022-10-16 [1] local\n#> tidyr * 1.2.1 2022-09-08 [1] CRAN (R 4.2.1)\n#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1)\n#> tidyverse * 1.3.2 2022-07-18 [1] CRAN (R 4.2.0)\n#> timechange 0.1.1 2022-11-04 [1] CRAN (R 4.2.2)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> tzdb 0.3.0 2022-03-28 [1] CRAN (R 4.2.0)\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"vectors.html","id":"vectors","chapter":"3 Vectors","heading":"3 Vectors","text":"","code":""},{"path":"vectors.html","id":"atomic-vectors-exercises-3.2.5","chapter":"3 Vectors","heading":"3.1 Atomic vectors (Exercises 3.2.5)","text":"Q1. create raw complex scalars? (See ?raw ?complex.)A1. R, scalars nothing vectors length 1, can created using constructor.Raw vectorsThe raw type holds raw bytes, can created using charToRaw(). example,alternative use .raw():Complex vectorsComplex vectors used represent (surprise!) complex numbers.Example complex scalar:Q2. Test knowledge vector coercion rules predicting output following uses c():A2. vector coercion rules dictate data type smaller size converted data type bigger size.Q3. 1 == \"1\" true? -1 < FALSE true? \"one\" < 2 false?A3. coercion rules vectors reveal comparisons return results .Q4. default missing value, NA, logical vector? ’s special logical vectors? (Hint: think c(FALSE, NA_character_).)A4. \"logical\" type lowest coercion hierarchy.NA defaulting type (e.g.Β \"numeric\") mean time missing element vector, rest elements converted type higher hierarchy, problematic types lower hierarchy.Q5. Precisely .atomic(), .numeric(), .vector() test ?A5. Let’s discuss one--one..atomic()function checks object vector atomic type (NULL).Quoting docs:.atomic true atomic types (β€œlogical”, β€œinteger”, β€œnumeric”, β€œcomplex”, β€œcharacter” β€œraw”) NULL..numeric()documentation says:.numeric return true base type class double integer values can reasonably regarded numericTherefore, function checks double integer base types types based top types (factor, Date, POSIXt, difftime)..vector()per documentation:.vector returns TRUE x vector specified mode attributes names. returns FALSE otherwise.Thus, function can incorrectif object attributes names.better way check vector:","code":"\nx <- \"A string\"\n\n(y <- charToRaw(x))\n#> [1] 41 20 73 74 72 69 6e 67\n\ntypeof(y)\n#> [1] \"raw\"\nas.raw(\"–\") # en-dash\n#> Warning: NAs introduced by coercion\n#> Warning: out-of-range values treated as 0 in coercion to raw\n#> [1] 00\nas.raw(\"β€”\") # em-dash\n#> Warning: NAs introduced by coercion\n\n#> Warning: out-of-range values treated as 0 in coercion to raw\n#> [1] 00\n(x <- complex(length.out = 1, real = 1, imaginary = 8))\n#> [1] 1+8i\n\ntypeof(x)\n#> [1] \"complex\"\nc(1, FALSE)\nc(\"a\", 1)\nc(TRUE, 1L)\nc(1, FALSE)\n#> [1] 1 0\n\nc(\"a\", 1)\n#> [1] \"a\" \"1\"\n\nc(TRUE, 1L)\n#> [1] 1 1\n1 == \"1\"\n#> [1] TRUE\n\nc(1, \"1\")\n#> [1] \"1\" \"1\"\n-1 < FALSE\n#> [1] TRUE\n\nc(-1, FALSE)\n#> [1] -1 0\n\"one\" < 2\n#> [1] FALSE\n\nc(\"one\", 2)\n#> [1] \"one\" \"2\"\n\nsort(c(\"one\", 2))\n#> [1] \"2\" \"one\"\ntypeof(NA)\n#> [1] \"logical\"\n\nc(FALSE, NA_character_)\n#> [1] \"FALSE\" NA\nis.atomic(NULL)\n#> [1] TRUE\n\nis.atomic(list(NULL))\n#> [1] FALSE\nis.numeric(1L)\n#> [1] TRUE\n\nis.numeric(factor(1L))\n#> [1] FALSE\nx <- c(\"x\" = 1, \"y\" = 2)\n\nis.vector(x)\n#> [1] TRUE\n\nattr(x, \"m\") <- \"abcdef\"\n\nis.vector(x)\n#> [1] FALSE\nis.null(dim(x))\n#> [1] TRUE"},{"path":"vectors.html","id":"attributes-exercises-3.3.4","chapter":"3 Vectors","heading":"3.2 Attributes (Exercises 3.3.4)","text":"Q1. setNames() implemented? unname() implemented? Read source code.A1. Let’s look implementations functions.setNames()Given function signature, can see , first argument given, result still named vector.unname()unname() removes existing names (dimnames) setting NULL.Q2. dim() return applied 1-dimensional vector? might use NROW() NCOL()?A2. Dimensions 1-dimensional vector NULL. example,NROW() NCOL() helpful getting dimensions 1D vectors treating matrices dataframes.Q3. describe following three objects? makes different 1:5?A3. x1, x2, x3 one-dimensional arrays, different β€œorientations”, mentally visualize .x1 5 entries third dimension, x2 second dimension, x1 first dimension.Q4. early draft used code illustrate structure():print object don’t see comment attribute. ? attribute missing, something else special ? (Hint: try using help.)A4. ?attributes (emphasis mine):Note attributes (namely class, comment, dim, dimnames, names, row.names tsp) treated specially restrictions values can set.","code":"\nsetNames\n#> function (object = nm, nm) \n#> {\n#> names(object) <- nm\n#> object\n#> }\n#> \n#> \nsetNames(, c(\"a\", \"b\"))\n#> a b \n#> \"a\" \"b\"\n\nsetNames(c(1, 2), c(\"a\", \"b\"))\n#> a b \n#> 1 2\nunname\n#> function (obj, force = FALSE) \n#> {\n#> if (!is.null(names(obj))) \n#> names(obj) <- NULL\n#> if (!is.null(dimnames(obj)) && (force || !is.data.frame(obj))) \n#> dimnames(obj) <- NULL\n#> obj\n#> }\n#> \n#> \nunname(setNames(, c(\"a\", \"b\")))\n#> [1] \"a\" \"b\"\ndim(c(1, 2))\n#> NULL\n# example-1\nx <- character(0)\n\ndim(x)\n#> NULL\n\nnrow(x)\n#> NULL\nNROW(x)\n#> [1] 0\n\nncol(x)\n#> NULL\nNCOL(x)\n#> [1] 1\n\n# example-2\ny <- 1:4\n\ndim(y)\n#> NULL\n\nnrow(y)\n#> NULL\nNROW(y)\n#> [1] 4\n\nncol(y)\n#> NULL\nNCOL(y)\n#> [1] 1\nx1 <- array(1:5, c(1, 1, 5))\nx2 <- array(1:5, c(1, 5, 1))\nx3 <- array(1:5, c(5, 1, 1))\nstructure(1:5, comment = \"my attribute\")\n#> [1] 1 2 3 4 5\nstructure(1:5, x = \"my attribute\")\n#> [1] 1 2 3 4 5\n#> attr(,\"x\")\n#> [1] \"my attribute\"\n\nstructure(1:5, comment = \"my attribute\")\n#> [1] 1 2 3 4 5"},{"path":"vectors.html","id":"s3-atomic-vectors-exercises-3.4.5","chapter":"3 Vectors","heading":"3.3 S3 atomic vectors (Exercises 3.4.5)","text":"Q1. sort object table() return? type? attributes ? dimensionality change tabulate variables?A1. table() returns array integer type dimensions scale number variables present.Q2. happens factor modify levels?A2. levels change underlying integer values remain .Q3. code ? f2 f3 differ f1?A3. code:f2: underlying integers reversed, levels remain unchanged.f3: levels underlying integers reversed.","code":"\n(x <- table(mtcars$am))\n#> \n#> 0 1 \n#> 19 13\n(y <- table(mtcars$am, mtcars$cyl))\n#> \n#> 4 6 8\n#> 0 3 4 12\n#> 1 8 3 2\n(z <- table(mtcars$am, mtcars$cyl, mtcars$vs))\n#> , , = 0\n#> \n#> \n#> 4 6 8\n#> 0 0 0 12\n#> 1 1 3 2\n#> \n#> , , = 1\n#> \n#> \n#> 4 6 8\n#> 0 3 4 0\n#> 1 7 0 0\n\n# type\npurrr::map(list(x, y, z), typeof)\n#> [[1]]\n#> [1] \"integer\"\n#> \n#> [[2]]\n#> [1] \"integer\"\n#> \n#> [[3]]\n#> [1] \"integer\"\n\n# attributes\npurrr::map(list(x, y, z), attributes)\n#> [[1]]\n#> [[1]]$dim\n#> [1] 2\n#> \n#> [[1]]$dimnames\n#> [[1]]$dimnames[[1]]\n#> [1] \"0\" \"1\"\n#> \n#> \n#> [[1]]$class\n#> [1] \"table\"\n#> \n#> \n#> [[2]]\n#> [[2]]$dim\n#> [1] 2 3\n#> \n#> [[2]]$dimnames\n#> [[2]]$dimnames[[1]]\n#> [1] \"0\" \"1\"\n#> \n#> [[2]]$dimnames[[2]]\n#> [1] \"4\" \"6\" \"8\"\n#> \n#> \n#> [[2]]$class\n#> [1] \"table\"\n#> \n#> \n#> [[3]]\n#> [[3]]$dim\n#> [1] 2 3 2\n#> \n#> [[3]]$dimnames\n#> [[3]]$dimnames[[1]]\n#> [1] \"0\" \"1\"\n#> \n#> [[3]]$dimnames[[2]]\n#> [1] \"4\" \"6\" \"8\"\n#> \n#> [[3]]$dimnames[[3]]\n#> [1] \"0\" \"1\"\n#> \n#> \n#> [[3]]$class\n#> [1] \"table\"\nf1 <- factor(letters)\nlevels(f1) <- rev(levels(f1))\nf1 <- factor(letters)\nf1\n#> [1] a b c d e f g h i j k l m n o p q r s t u v w x y z\n#> 26 Levels: a b c d e f g h i j k l m n o p q r s t u ... z\nas.integer(f1)\n#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18\n#> [19] 19 20 21 22 23 24 25 26\n\nlevels(f1) <- rev(levels(f1))\nf1\n#> [1] z y x w v u t s r q p o n m l k j i h g f e d c b a\n#> 26 Levels: z y x w v u t s r q p o n m l k j i h g f ... a\nas.integer(f1)\n#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18\n#> [19] 19 20 21 22 23 24 25 26\nf2 <- rev(factor(letters))\nf3 <- factor(letters, levels = rev(letters))\nf2 <- rev(factor(letters))\nf2\n#> [1] z y x w v u t s r q p o n m l k j i h g f e d c b a\n#> 26 Levels: a b c d e f g h i j k l m n o p q r s t u ... z\nas.integer(f2)\n#> [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9\n#> [19] 8 7 6 5 4 3 2 1\nf3 <- factor(letters, levels = rev(letters))\nf3\n#> [1] a b c d e f g h i j k l m n o p q r s t u v w x y z\n#> 26 Levels: z y x w v u t s r q p o n m l k j i h g f ... a\nas.integer(f3)\n#> [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9\n#> [19] 8 7 6 5 4 3 2 1"},{"path":"vectors.html","id":"lists-exercises-3.5.4","chapter":"3 Vectors","heading":"3.4 Lists (Exercises 3.5.4)","text":"Q1. List ways list differs atomic vector.A1. table comparison:Q2. need use unlist() convert list atomic vector? doesn’t .vector() work?A2. list already (generic) vector, .vector() going change anything, .atomic.vector. Thus, need use unlist().Q3. Compare contrast c() unlist() combining date date-time single vector.A3. Let’s first create date datetime objectAnd check attributes underlying double representation:Behavior c()Since S3 method c() dispatches first argument, resulting class vector going first argument. , attributes lost.Behavior unlist()removes attributes left underlying double representations objects.","code":"\nx <- list(a = 1, b = 2)\n\nis.vector(x)\n#> [1] TRUE\nis.atomic(x)\n#> [1] FALSE\n\n# still a list\nas.vector(x)\n#> $a\n#> [1] 1\n#> \n#> $b\n#> [1] 2\n\n# now a vector\nunlist(x)\n#> a b \n#> 1 2\ndate <- as.Date(\"1947-08-15\")\ndatetime <- as.POSIXct(\"1950-01-26 00:01\", tz = \"UTC\")\nattributes(date)\n#> $class\n#> [1] \"Date\"\nattributes(datetime)\n#> $class\n#> [1] \"POSIXct\" \"POSIXt\" \n#> \n#> $tzone\n#> [1] \"UTC\"\n\nas.double(date) # number of days since the Unix epoch 1970-01-01\n#> [1] -8175\nas.double(datetime) # number of seconds since then\n#> [1] -628991940\nc(date, datetime)\n#> [1] \"1947-08-15\" \"1950-01-26\"\n\nattributes(c(date, datetime))\n#> $class\n#> [1] \"Date\"\n\nc(datetime, date)\n#> [1] \"1950-01-26 01:01:00 CET\" \"1947-08-15 02:00:00 CEST\"\n\nattributes(c(datetime, date))\n#> $class\n#> [1] \"POSIXct\" \"POSIXt\"\nunlist(list(date, datetime))\n#> [1] -8175 -628991940\n\nunlist(list(datetime, date))\n#> [1] -628991940 -8175"},{"path":"vectors.html","id":"data-frames-and-tibbles-exercises-3.6.8","chapter":"3 Vectors","heading":"3.5 Data frames and tibbles (Exercises 3.6.8)","text":"Q1. Can data frame zero rows? zero columns?A1. Data frame 0 rows possible. basically list vector length 0.Data frame 0 columns also possible. empty list., finally, data frame 0 rows columns also possible:Although, might common create data frames, can results subsetting. example,Q2. happens attempt set rownames unique?A2. attempt set data frame rownames unique, work.Q3. df data frame, can say t(df), t(t(df))? Perform experiments, making sure try different column types.A3. Transposing data frame:transforms matrixcoerces elements typeQ4. .matrix() applied data frame columns different types? differ data.matrix()?A4. return type .matrix() depends data frame column types.docs .matrix() mention:method data frames return character matrix atomic columns non-(numeric/logical/complex) column, applying .vector factors format non-character columns. Otherwise usual coercion hierarchy (logical < integer < double < complex) used, e.g.Β -logical data frames coerced logical matrix, mixed logical-integer give integer matrix, etc.Let’s experiment:hand, data.matrix() always returns numeric matrix.documentation data.matrix():Return matrix obtained converting variables data frame numeric mode binding together columns matrix. Factors ordered factors replaced internal codes. […] Character columns first converted factors integers.Let’s experiment:","code":"\ndata.frame(x = numeric(0))\n#> [1] x\n#> <0 rows> (or 0-length row.names)\ndata.frame(row.names = 1)\n#> data frame with 0 columns and 1 row\ndata.frame()\n#> data frame with 0 columns and 0 rows\n\ndim(data.frame())\n#> [1] 0 0\nBOD[0, ]\n#> [1] Time demand\n#> <0 rows> (or 0-length row.names)\n\nBOD[, 0]\n#> data frame with 0 columns and 6 rows\n\nBOD[0, 0]\n#> data frame with 0 columns and 0 rows\ndata.frame(row.names = c(1, 1))\n#> Error in data.frame(row.names = c(1, 1)): duplicate row.names: 1\n# original\n(df <- head(iris))\n#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species\n#> 1 5.1 3.5 1.4 0.2 setosa\n#> 2 4.9 3.0 1.4 0.2 setosa\n#> 3 4.7 3.2 1.3 0.2 setosa\n#> 4 4.6 3.1 1.5 0.2 setosa\n#> 5 5.0 3.6 1.4 0.2 setosa\n#> 6 5.4 3.9 1.7 0.4 setosa\n\n# transpose\nt(df)\n#> 1 2 3 4 5 \n#> Sepal.Length \"5.1\" \"4.9\" \"4.7\" \"4.6\" \"5.0\" \n#> Sepal.Width \"3.5\" \"3.0\" \"3.2\" \"3.1\" \"3.6\" \n#> Petal.Length \"1.4\" \"1.4\" \"1.3\" \"1.5\" \"1.4\" \n#> Petal.Width \"0.2\" \"0.2\" \"0.2\" \"0.2\" \"0.2\" \n#> Species \"setosa\" \"setosa\" \"setosa\" \"setosa\" \"setosa\"\n#> 6 \n#> Sepal.Length \"5.4\" \n#> Sepal.Width \"3.9\" \n#> Petal.Length \"1.7\" \n#> Petal.Width \"0.4\" \n#> Species \"setosa\"\n\n# transpose of a transpose\nt(t(df))\n#> Sepal.Length Sepal.Width Petal.Length Petal.Width\n#> 1 \"5.1\" \"3.5\" \"1.4\" \"0.2\" \n#> 2 \"4.9\" \"3.0\" \"1.4\" \"0.2\" \n#> 3 \"4.7\" \"3.2\" \"1.3\" \"0.2\" \n#> 4 \"4.6\" \"3.1\" \"1.5\" \"0.2\" \n#> 5 \"5.0\" \"3.6\" \"1.4\" \"0.2\" \n#> 6 \"5.4\" \"3.9\" \"1.7\" \"0.4\" \n#> Species \n#> 1 \"setosa\"\n#> 2 \"setosa\"\n#> 3 \"setosa\"\n#> 4 \"setosa\"\n#> 5 \"setosa\"\n#> 6 \"setosa\"\n\n# is it a dataframe?\nis.data.frame(df)\n#> [1] TRUE\nis.data.frame(t(df))\n#> [1] FALSE\nis.data.frame(t(t(df)))\n#> [1] FALSE\n\n# check type\ntypeof(df)\n#> [1] \"list\"\ntypeof(t(df))\n#> [1] \"character\"\ntypeof(t(t(df)))\n#> [1] \"character\"\n\n# check dimensions\ndim(df)\n#> [1] 6 5\ndim(t(df))\n#> [1] 5 6\ndim(t(t(df)))\n#> [1] 6 5\n# example with mixed types (coerced to character)\n(df <- head(iris))\n#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species\n#> 1 5.1 3.5 1.4 0.2 setosa\n#> 2 4.9 3.0 1.4 0.2 setosa\n#> 3 4.7 3.2 1.3 0.2 setosa\n#> 4 4.6 3.1 1.5 0.2 setosa\n#> 5 5.0 3.6 1.4 0.2 setosa\n#> 6 5.4 3.9 1.7 0.4 setosa\n\nas.matrix(df)\n#> Sepal.Length Sepal.Width Petal.Length Petal.Width\n#> 1 \"5.1\" \"3.5\" \"1.4\" \"0.2\" \n#> 2 \"4.9\" \"3.0\" \"1.4\" \"0.2\" \n#> 3 \"4.7\" \"3.2\" \"1.3\" \"0.2\" \n#> 4 \"4.6\" \"3.1\" \"1.5\" \"0.2\" \n#> 5 \"5.0\" \"3.6\" \"1.4\" \"0.2\" \n#> 6 \"5.4\" \"3.9\" \"1.7\" \"0.4\" \n#> Species \n#> 1 \"setosa\"\n#> 2 \"setosa\"\n#> 3 \"setosa\"\n#> 4 \"setosa\"\n#> 5 \"setosa\"\n#> 6 \"setosa\"\n\nstr(as.matrix(df))\n#> chr [1:6, 1:5] \"5.1\" \"4.9\" \"4.7\" \"4.6\" \"5.0\" \"5.4\" ...\n#> - attr(*, \"dimnames\")=List of 2\n#> ..$ : chr [1:6] \"1\" \"2\" \"3\" \"4\" ...\n#> ..$ : chr [1:5] \"Sepal.Length\" \"Sepal.Width\" \"Petal.Length\" \"Petal.Width\" ...\n\n# another example (no such coercion)\nBOD\n#> Time demand\n#> 1 1 8.3\n#> 2 2 10.3\n#> 3 3 19.0\n#> 4 4 16.0\n#> 5 5 15.6\n#> 6 7 19.8\n\nas.matrix(BOD)\n#> Time demand\n#> [1,] 1 8.3\n#> [2,] 2 10.3\n#> [3,] 3 19.0\n#> [4,] 4 16.0\n#> [5,] 5 15.6\n#> [6,] 7 19.8\ndata.matrix(df)\n#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species\n#> 1 5.1 3.5 1.4 0.2 1\n#> 2 4.9 3.0 1.4 0.2 1\n#> 3 4.7 3.2 1.3 0.2 1\n#> 4 4.6 3.1 1.5 0.2 1\n#> 5 5.0 3.6 1.4 0.2 1\n#> 6 5.4 3.9 1.7 0.4 1\n\nstr(data.matrix(df))\n#> num [1:6, 1:5] 5.1 4.9 4.7 4.6 5 5.4 3.5 3 3.2 3.1 ...\n#> - attr(*, \"dimnames\")=List of 2\n#> ..$ : chr [1:6] \"1\" \"2\" \"3\" \"4\" ...\n#> ..$ : chr [1:5] \"Sepal.Length\" \"Sepal.Width\" \"Petal.Length\" \"Petal.Width\" ..."},{"path":"vectors.html","id":"session-information-1","chapter":"3 Vectors","heading":"3.6 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"subsetting.html","id":"subsetting","chapter":"4 Subsetting","heading":"4 Subsetting","text":"Attaching needed libraries:","code":"\nlibrary(tibble)"},{"path":"subsetting.html","id":"selecting-multiple-elements-exercises-4.2.6","chapter":"4 Subsetting","heading":"4.1 Selecting multiple elements (Exercises 4.2.6)","text":"Q1. Fix following common data frame subsetting errors:A1. Fixed versions commands:Q2. following code yield five missing values?A2. two reasons:default type NA R logical type.R recycles indexes match length vector.Q3. upper.tri() return? subsetting matrix work? need additional subsetting rules describe behaviour?A3. documentation upper.tri() states-Returns matrix logicals size given matrix entries TRUE upper triangleWhen used matrix subsetting, elements corresponding TRUE subsetting matrix selected. , instead matrix, returns vector:Q4. mtcars[1:20] return error? differ similar mtcars[1:20, ]?A4. indexed like list, data frame columns given indices selected.mtcars[1:20] doesn’t work 11 columns mtcars dataset.hand, mtcars[1:20, ] indexes dataframe like matrix, indeed 20 rows mtcars, columns rows selected.Q5. Implement function extracts diagonal entries matrix (behave like diag(x) x matrix).A5. can combine existing functions advantage:Q6. df[.na(df)] <- 0 ? work?A6. expression replaces every instance NA df 0..na(df) produces matrix logical values, provides way subsetting.","code":"mtcars[mtcars$cyl = 4, ]\nmtcars[-1:4, ]\nmtcars[mtcars$cyl <= 5]\nmtcars[mtcars$cyl == 4 | 6, ]\n# `==` instead of `=`\nmtcars[mtcars$cyl == 4, ]\n\n# `-(1:4)` instead of `-1:4`\nmtcars[-(1:4), ]\n\n# `,` was missing\nmtcars[mtcars$cyl <= 5, ]\n\n# correct subsetting syntax\nmtcars[mtcars$cyl == 4 | mtcars$cyl == 6, ]\nmtcars[mtcars$cyl %in% c(4, 6), ]\nx <- 1:5\nx[NA]\n#> [1] NA NA NA NA NA\ntypeof(NA)\n#> [1] \"logical\"\nx <- 1:5\nx[c(TRUE, FALSE)] # recycled to c(TRUE, FALSE, TRUE, FALSE, TRUE)\n#> [1] 1 3 5\nx <- outer(1:5, 1:5, FUN = \"*\")\nx[upper.tri(x)]\n(x <- outer(1:5, 1:5, FUN = \"*\"))\n#> [,1] [,2] [,3] [,4] [,5]\n#> [1,] 1 2 3 4 5\n#> [2,] 2 4 6 8 10\n#> [3,] 3 6 9 12 15\n#> [4,] 4 8 12 16 20\n#> [5,] 5 10 15 20 25\n\nupper.tri(x)\n#> [,1] [,2] [,3] [,4] [,5]\n#> [1,] FALSE TRUE TRUE TRUE TRUE\n#> [2,] FALSE FALSE TRUE TRUE TRUE\n#> [3,] FALSE FALSE FALSE TRUE TRUE\n#> [4,] FALSE FALSE FALSE FALSE TRUE\n#> [5,] FALSE FALSE FALSE FALSE FALSE\nx[upper.tri(x)]\n#> [1] 2 3 6 4 8 12 5 10 15 20\nhead(mtcars[1:2])\n#> mpg cyl\n#> Mazda RX4 21.0 6\n#> Mazda RX4 Wag 21.0 6\n#> Datsun 710 22.8 4\n#> Hornet 4 Drive 21.4 6\n#> Hornet Sportabout 18.7 8\n#> Valiant 18.1 6\nnrow(mtcars[1:20, ])\n#> [1] 20\nx[!upper.tri(x) & !lower.tri(x)]\n#> [1] 1 4 9 16 25\n\ndiag(x)\n#> [1] 1 4 9 16 25\n(df <- tibble(x = c(1, 2, NA), y = c(NA, 5, NA)))\n#> # A tibble: 3 Γ— 2\n#> x y\n#> \n#> 1 1 NA\n#> 2 2 5\n#> 3 NA NA\n\nis.na(df)\n#> x y\n#> [1,] FALSE TRUE\n#> [2,] FALSE FALSE\n#> [3,] TRUE TRUE\n\nclass(is.na(df))\n#> [1] \"matrix\" \"array\""},{"path":"subsetting.html","id":"selecting-a-single-element-exercises-4.3.5","chapter":"4 Subsetting","heading":"4.2 Selecting a single element (Exercises 4.3.5)","text":"Q1. Brainstorm many ways possible extract third value cyl variable mtcars dataset.A1. Possible ways extract third value cyl variable mtcars dataset:Q2. Given linear model, e.g., mod <- lm(mpg ~ wt, data = mtcars), extract residual degrees freedom. extract R squared model summary (summary(mod))A2. Given objects class lm lists, can use subsetting operators extract elements want.extracting residual degrees freedomextracting R squared model summary","code":"\nmtcars[[\"cyl\"]][[3]]\n#> [1] 4\nmtcars[[c(2, 3)]]\n#> [1] 4\nmtcars[3, ][[\"cyl\"]]\n#> [1] 4\nmtcars[3, ]$cyl\n#> [1] 4\nmtcars[3, \"cyl\"]\n#> [1] 4\nmtcars[, \"cyl\"][[3]]\n#> [1] 4\nmtcars[3, 2]\n#> [1] 4\nmtcars$cyl[[3]]\n#> [1] 4\nmod <- lm(mpg ~ wt, data = mtcars)\nclass(mod)\n#> [1] \"lm\"\ntypeof(mod)\n#> [1] \"list\"\nmod$df.residual \n#> [1] 30\nmod[[\"df.residual\"]]\n#> [1] 30\nsummary(mod)$r.squared\n#> [1] 0.7528328\nsummary(mod)[[\"r.squared\"]]\n#> [1] 0.7528328"},{"path":"subsetting.html","id":"applications-exercises-4.5.9","chapter":"4 Subsetting","heading":"4.3 Applications (Exercises 4.5.9)","text":"Q1. randomly permute columns data frame? (important technique random forests.) Can simultaneously permute rows columns one step?A1. Let’s create small data frame work .randomly permute columns data frame, can combine [ sample() follows:randomly permute columnsrandomly permute rowsrandomly permute columns rowsQ2. select random sample m rows data frame? sample contiguous (.e., initial row, final row, every row )?A2. Let’s create small data frame work .select random sample m rows data frame, can combine [ sample() follows:random non-contiguous sample m rows data framerandom contiguous sample m rows data frameQ3. put columns data frame alphabetical order?A3. can sort columns data frame alphabetical order using [ order():","code":"\ndf <- head(mtcars)\n\n# original\ndf\n#> mpg cyl disp hp drat wt qsec vs am\n#> Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1\n#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1\n#> Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1\n#> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0\n#> Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0\n#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0\n#> gear carb\n#> Mazda RX4 4 4\n#> Mazda RX4 Wag 4 4\n#> Datsun 710 4 1\n#> Hornet 4 Drive 3 1\n#> Hornet Sportabout 3 2\n#> Valiant 3 1\ndf[sample.int(ncol(df))]\n#> drat wt carb am qsec vs hp mpg disp\n#> Mazda RX4 3.90 2.620 4 1 16.46 0 110 21.0 160\n#> Mazda RX4 Wag 3.90 2.875 4 1 17.02 0 110 21.0 160\n#> Datsun 710 3.85 2.320 1 1 18.61 1 93 22.8 108\n#> Hornet 4 Drive 3.08 3.215 1 0 19.44 1 110 21.4 258\n#> Hornet Sportabout 3.15 3.440 2 0 17.02 0 175 18.7 360\n#> Valiant 2.76 3.460 1 0 20.22 1 105 18.1 225\n#> cyl gear\n#> Mazda RX4 6 4\n#> Mazda RX4 Wag 6 4\n#> Datsun 710 4 4\n#> Hornet 4 Drive 6 3\n#> Hornet Sportabout 8 3\n#> Valiant 6 3\ndf[sample.int(nrow(df)), ]\n#> mpg cyl disp hp drat wt qsec vs am\n#> Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1\n#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1\n#> Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1\n#> Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0\n#> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0\n#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0\n#> gear carb\n#> Datsun 710 4 1\n#> Mazda RX4 Wag 4 4\n#> Mazda RX4 4 4\n#> Hornet Sportabout 3 2\n#> Hornet 4 Drive 3 1\n#> Valiant 3 1\ndf[sample.int(nrow(df)), sample.int(ncol(df))]\n#> qsec vs gear am wt drat carb disp hp\n#> Mazda RX4 16.46 0 4 1 2.620 3.90 4 160 110\n#> Hornet 4 Drive 19.44 1 3 0 3.215 3.08 1 258 110\n#> Datsun 710 18.61 1 4 1 2.320 3.85 1 108 93\n#> Mazda RX4 Wag 17.02 0 4 1 2.875 3.90 4 160 110\n#> Valiant 20.22 1 3 0 3.460 2.76 1 225 105\n#> Hornet Sportabout 17.02 0 3 0 3.440 3.15 2 360 175\n#> mpg cyl\n#> Mazda RX4 21.0 6\n#> Hornet 4 Drive 21.4 6\n#> Datsun 710 22.8 4\n#> Mazda RX4 Wag 21.0 6\n#> Valiant 18.1 6\n#> Hornet Sportabout 18.7 8\ndf <- head(mtcars)\n\n# original\ndf\n#> mpg cyl disp hp drat wt qsec vs am\n#> Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1\n#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1\n#> Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1\n#> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0\n#> Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0\n#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0\n#> gear carb\n#> Mazda RX4 4 4\n#> Mazda RX4 Wag 4 4\n#> Datsun 710 4 1\n#> Hornet 4 Drive 3 1\n#> Hornet Sportabout 3 2\n#> Valiant 3 1\n\n# number of rows to sample\nm <- 2L\ndf[sample(nrow(df), m), ]\n#> mpg cyl disp hp drat wt qsec vs am gear\n#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3\n#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4\n#> carb\n#> Valiant 1\n#> Mazda RX4 Wag 4\n# select a random starting position from available number of rows\nstart_row <- sample(nrow(df) - m + 1, size = 1)\n\n# adjust ending position while avoiding off-by-one error\nend_row <- start_row + m - 1\n\ndf[start_row:end_row, ]\n#> mpg cyl disp hp drat wt qsec vs am gear\n#> Mazda RX4 21 6 160 110 3.9 2.620 16.46 0 1 4\n#> Mazda RX4 Wag 21 6 160 110 3.9 2.875 17.02 0 1 4\n#> carb\n#> Mazda RX4 4\n#> Mazda RX4 Wag 4\n# columns in original order\nnames(mtcars)\n#> [1] \"mpg\" \"cyl\" \"disp\" \"hp\" \"drat\" \"wt\" \"qsec\" \"vs\" \n#> [9] \"am\" \"gear\" \"carb\"\n\n# columns in alphabetical order\nnames(mtcars[order(names(mtcars))])\n#> [1] \"am\" \"carb\" \"cyl\" \"disp\" \"drat\" \"gear\" \"hp\" \"mpg\" \n#> [9] \"qsec\" \"vs\" \"wt\""},{"path":"subsetting.html","id":"session-information-2","chapter":"4 Subsetting","heading":"4.4 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> tibble * 3.1.8.9002 2022-10-16 [1] local\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"control-flow.html","id":"control-flow","chapter":"5 Control flow","heading":"5 Control flow","text":"","code":""},{"path":"control-flow.html","id":"choices-exercises-5.2.4","chapter":"5 Control flow","heading":"5.1 Choices (Exercises 5.2.4)","text":"Q1. type vector following calls ifelse() return?Read documentation write rules words.A1. rules call ifelse() might return:type unstable, .e.Β type return depend type condition true (yes , .e.):works cases test argument evaluates logical type:test argument logical type, NA, return NA:test argument doesn’t resolve logical type, try coerce output logical type:also clarified docs function:vector length attributes (including dimensions \"class\") test data values values yes . mode answer coerced logical accommodate first values taken yes values taken .Q2. following code work?A2. code works conditional expressions () - even though numeric type - can successfully coerced logical type.","code":"\nifelse(TRUE, 1, \"no\")\nifelse(FALSE, 1, \"no\")\nifelse(NA, 1, \"no\")\nifelse(TRUE, 1, \"no\") # `numeric` returned\n#> [1] 1\nifelse(FALSE, 1, \"no\") # `character` returned\n#> [1] \"no\"\nifelse(NA_real_, 1, \"no\")\n#> [1] NA\nifelse(NaN, 1, \"no\")\n#> [1] NA\nifelse(NA, 1, \"no\")\n#> [1] NA\n# will work\nifelse(\"TRUE\", 1, \"no\")\n#> [1] 1\nifelse(\"false\", 1, \"no\")\n#> [1] \"no\"\n\n# won't work\nifelse(\"tRuE\", 1, \"no\")\n#> [1] NA\nifelse(NaN, 1, \"no\")\n#> [1] NA\nx <- 1:10\nif (length(x)) \"not empty\" else \"empty\"\n#> [1] \"not empty\"\n\nx <- numeric()\nif (length(x)) \"not empty\" else \"empty\"\n#> [1] \"empty\"\nas.logical(length(1:10))\n#> [1] TRUE\n\nas.logical(length(numeric()))\n#> [1] FALSE"},{"path":"control-flow.html","id":"loops-exercises-5.3.3","chapter":"5 Control flow","heading":"5.2 Loops (Exercises 5.3.3)","text":"Q1. code succeed without errors warnings?A1. works 1:length(x) works positive negative directions.case, since x length 0, go 1 0.Additionally, since --bound (OOB) value atomic vectors NA, related operations OOB values also produce NA.way avoid unintended behavior use seq_along() instead:Q2. following code evaluated, can say vector iterated?A2. iterator variable x initially takes values vector xs. can check printing x iteration:worth noting x updated iteration; otherwise, take increasingly bigger values xs, loop never end executing.Q3. following code tell index updated?A3. () loop index updated beginning iteration. Otherwise, encounter infinite loop.Also, worth contrasting behavior () loop () loop:","code":"\nx <- numeric()\nout <- vector(\"list\", length(x))\nfor (i in 1:length(x)) {\n out[i] <- x[i]^2\n}\nout\n1:2\n#> [1] 1 2\n1:0\n#> [1] 1 0\n1:-3\n#> [1] 1 0 -1 -2 -3\nx <- numeric()\nout <- vector(\"list\", length(x))\n\nfor (i in 1:length(x)) {\n print(paste(\"i:\", i, \", x[i]:\", x[i], \", out[i]:\", out[i]))\n\n out[i] <- x[i]^2\n}\n#> [1] \"i: 1 , x[i]: NA , out[i]: NULL\"\n#> [1] \"i: 0 , x[i]: , out[i]: \"\n\nout\n#> [[1]]\n#> [1] NA\nx <- numeric()\nout <- vector(\"list\", length(x))\n\nfor (i in seq_along(x)) {\n out[i] <- x[i]^2\n}\n\nout\n#> list()\nxs <- c(1, 2, 3)\nfor (x in xs) {\n xs <- c(xs, x * 2)\n}\nxs\n#> [1] 1 2 3 2 4 6\nxs <- c(1, 2, 3)\nfor (x in xs) {\n cat(\"x:\", x, \"\\n\")\n xs <- c(xs, x * 2)\n cat(\"xs:\", paste(xs), \"\\n\")\n}\n#> x: 1 \n#> xs: 1 2 3 2 \n#> x: 2 \n#> xs: 1 2 3 2 4 \n#> x: 3 \n#> xs: 1 2 3 2 4 6\nfor (i in 1:3) {\n i <- i * 2\n print(i)\n}\n#> [1] 2\n#> [1] 4\n#> [1] 6\nfor (i in 1:3) {\n cat(\"before: \", i, \"\\n\")\n i <- i * 2\n cat(\"after: \", i, \"\\n\")\n}\n#> before: 1 \n#> after: 2 \n#> before: 2 \n#> after: 4 \n#> before: 3 \n#> after: 6\ni <- 1\nwhile (i < 4) {\n cat(\"before: \", i, \"\\n\")\n i <- i * 2\n cat(\"after: \", i, \"\\n\")\n}\n#> before: 1 \n#> after: 2 \n#> before: 2 \n#> after: 4"},{"path":"control-flow.html","id":"session-information-3","chapter":"5 Control flow","heading":"5.3 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"functions.html","id":"functions","chapter":"6 Functions","heading":"6 Functions","text":"Attaching needed libraries:","code":"\nlibrary(tidyverse, warn.conflicts = FALSE)"},{"path":"functions.html","id":"function-fundamentals-exercises-6.2.5","chapter":"6 Functions","heading":"6.1 Function fundamentals (Exercises 6.2.5)","text":"Q1. Given name, like \"mean\", match.fun() lets find function. Given function, can find name? doesn’t make sense R?A1. Given name, match.fun() lets find function., given function, doesn’t make sense find name can multiple names bound function.Q2. ’s possible (although typically useful) call anonymous function. two approaches correct? ?A2. first expression correct since function evaluate 3(), syntactically allowed since literals can’t treated like functions.correct way call anonymous function.Q3. good rule thumb anonymous function fit one line shouldn’t need use {}. Review code. used anonymous function instead named function? used named function instead anonymous function?A3. Self activity.Q4. function allows tell object function? function allows tell function primitive function?A4. Use .function() check object function:Use .primitive() check function primitive:Q5. code makes list functions base package.Use answer following questions:base function arguments?base function arguments?many base functions arguments? ’s special functions?many base functions arguments? ’s special functions?adapt code find primitive functions?adapt code find primitive functions?A5. provided code following:base function arguments?can use formals() extract number arguments, function returns NULL primitive functions.Therefore, focus non-primitive functions.scan() function arguments.many base functions arguments? ’s special functions?time writing, 47 base (non-primitive) functions arguments.adapt code find primitive functions?Q6. three important components function?A6. Except primitive functions, functions 3 important components:formals()body()environment()Q7. printing function show environment created ?A7. package functions print environment:two exceptions enclosing environment won’t printed:primitive functionsfunctions created global environment","code":"\nmatch.fun(\"mean\")\n#> function (x, ...) \n#> UseMethod(\"mean\")\n#> \n#> \nf1 <- function(x) mean(x)\nf2 <- f1\n\nmatch.fun(\"f1\")\n#> function(x) mean(x)\n\nmatch.fun(\"f2\")\n#> function(x) mean(x)\nfunction(x) 3()\n#> function(x) 3()\n(function(x) 3)()\n#> [1] 3\nf <- (function(x) 3())\nf\n#> function(x) 3()\nf()\n#> Error in f(): attempt to apply non-function\n\nrlang::is_syntactic_literal(3)\n#> [1] TRUE\ng <- (function(x) 3)\ng\n#> function(x) 3\ng()\n#> [1] 3\n# these are functions\nf <- function(x) 3\nis.function(mean)\n#> [1] TRUE\nis.function(f)\n#> [1] TRUE\n\n# these aren't\nis.function(\"x\")\n#> [1] FALSE\nis.function(new.env())\n#> [1] FALSE\n# primitive\nis.primitive(sum)\n#> [1] TRUE\nis.primitive(`+`)\n#> [1] TRUE\n\n# not primitive\nis.primitive(mean)\n#> [1] FALSE\nis.primitive(read.csv)\n#> [1] FALSE\nobjs <- mget(ls(\"package:base\", all = TRUE), inherits = TRUE)\nfuns <- Filter(is.function, objs)\nobjs <- mget(ls(\"package:base\", all = TRUE), inherits = TRUE)\nfuns <- Filter(is.function, objs)\nformals(\"!\")\n#> NULL\n\nlength(formals(\"!\"))\n#> [1] 0\nfuns <- purrr::discard(funs, is.primitive)\ndf_formals <- purrr::map_df(funs, ~ length(formals(.))) %>%\n tidyr::pivot_longer(\n cols = dplyr::everything(),\n names_to = \"function\",\n values_to = \"argumentCount\"\n ) %>%\n dplyr::arrange(desc(argumentCount))\n\ndf_formals\n#> # A tibble: 1,125 Γ— 2\n#> `function` argumentCount\n#> \n#> 1 scan 22\n#> 2 format.default 16\n#> 3 source 16\n#> 4 formatC 15\n#> 5 library 13\n#> 6 merge.data.frame 13\n#> 7 prettyNum 13\n#> 8 system2 11\n#> 9 print.default 10\n#> 10 save 10\n#> # … with 1,115 more rows\ndplyr::filter(df_formals, argumentCount == 0)\n#> # A tibble: 47 Γ— 2\n#> `function` argumentCount\n#> \n#> 1 .First.sys 0\n#> 2 .NotYetImplemented 0\n#> 3 .OptRequireMethods 0\n#> 4 .standard_regexps 0\n#> 5 .tryResumeInterrupt 0\n#> 6 closeAllConnections 0\n#> 7 contributors 0\n#> 8 Cstack_info 0\n#> 9 date 0\n#> 10 default.stringsAsFactors 0\n#> # … with 37 more rows\nobjs <- mget(ls(\"package:base\", all = TRUE), inherits = TRUE)\nfuns <- Filter(is.function, objs)\nprimitives <- Filter(is.primitive, funs)\n\nlength(primitives)\n#> [1] 204\n\nnames(primitives)\n#> [1] \"-\" \":\" \n#> [3] \"::\" \":::\" \n#> [5] \"!\" \"!=\" \n#> [7] \"...elt\" \"...length\" \n#> [9] \"...names\" \".C\" \n#> [11] \".cache_class\" \".Call\" \n#> [13] \".Call.graphics\" \".class2\" \n#> [15] \".External\" \".External.graphics\" \n#> [17] \".External2\" \".Fortran\" \n#> [19] \".Internal\" \".isMethodsDispatchOn\"\n#> [21] \".Primitive\" \".primTrace\" \n#> [23] \".primUntrace\" \".subset\" \n#> [25] \".subset2\" \"(\" \n#> [27] \"[\" \"[[\" \n#> [29] \"[[<-\" \"[<-\" \n#> [31] \"{\" \"@\" \n#> [33] \"@<-\" \"*\" \n#> [35] \"/\" \"&\" \n#> [37] \"&&\" \"%*%\" \n#> [39] \"%/%\" \"%%\" \n#> [41] \"^\" \"+\" \n#> [43] \"<\" \"<-\" \n#> [45] \"<<-\" \"<=\" \n#> [47] \"=\" \"==\" \n#> [49] \">\" \">=\" \n#> [51] \"|\" \"||\" \n#> [53] \"~\" \"$\" \n#> [55] \"$<-\" \"abs\" \n#> [57] \"acos\" \"acosh\" \n#> [59] \"all\" \"any\" \n#> [61] \"anyNA\" \"Arg\" \n#> [63] \"as.call\" \"as.character\" \n#> [65] \"as.complex\" \"as.double\" \n#> [67] \"as.environment\" \"as.integer\" \n#> [69] \"as.logical\" \"as.numeric\" \n#> [71] \"as.raw\" \"asin\" \n#> [73] \"asinh\" \"atan\" \n#> [75] \"atanh\" \"attr\" \n#> [77] \"attr<-\" \"attributes\" \n#> [79] \"attributes<-\" \"baseenv\" \n#> [81] \"break\" \"browser\" \n#> [83] \"c\" \"call\" \n#> [85] \"ceiling\" \"class\" \n#> [87] \"class<-\" \"Conj\" \n#> [89] \"cos\" \"cosh\" \n#> [91] \"cospi\" \"cummax\" \n#> [93] \"cummin\" \"cumprod\" \n#> [95] \"cumsum\" \"digamma\" \n#> [97] \"dim\" \"dim<-\" \n#> [99] \"dimnames\" \"dimnames<-\" \n#> [101] \"emptyenv\" \"enc2native\" \n#> [103] \"enc2utf8\" \"environment<-\" \n#> [105] \"exp\" \"expm1\" \n#> [107] \"expression\" \"floor\" \n#> [109] \"for\" \"forceAndCall\" \n#> [111] \"function\" \"gamma\" \n#> [113] \"gc.time\" \"globalenv\" \n#> [115] \"if\" \"Im\" \n#> [117] \"interactive\" \"invisible\" \n#> [119] \"is.array\" \"is.atomic\" \n#> [121] \"is.call\" \"is.character\" \n#> [123] \"is.complex\" \"is.double\" \n#> [125] \"is.environment\" \"is.expression\" \n#> [127] \"is.finite\" \"is.function\" \n#> [129] \"is.infinite\" \"is.integer\" \n#> [131] \"is.language\" \"is.list\" \n#> [133] \"is.logical\" \"is.matrix\" \n#> [135] \"is.na\" \"is.name\" \n#> [137] \"is.nan\" \"is.null\" \n#> [139] \"is.numeric\" \"is.object\" \n#> [141] \"is.pairlist\" \"is.raw\" \n#> [143] \"is.recursive\" \"is.single\" \n#> [145] \"is.symbol\" \"isS4\" \n#> [147] \"lazyLoadDBfetch\" \"length\" \n#> [149] \"length<-\" \"levels<-\" \n#> [151] \"lgamma\" \"list\" \n#> [153] \"log\" \"log10\" \n#> [155] \"log1p\" \"log2\" \n#> [157] \"max\" \"min\" \n#> [159] \"missing\" \"Mod\" \n#> [161] \"names\" \"names<-\" \n#> [163] \"nargs\" \"next\" \n#> [165] \"nzchar\" \"oldClass\" \n#> [167] \"oldClass<-\" \"on.exit\" \n#> [169] \"pos.to.env\" \"proc.time\" \n#> [171] \"prod\" \"quote\" \n#> [173] \"range\" \"Re\" \n#> [175] \"rep\" \"repeat\" \n#> [177] \"retracemem\" \"return\" \n#> [179] \"round\" \"seq_along\" \n#> [181] \"seq_len\" \"seq.int\" \n#> [183] \"sign\" \"signif\" \n#> [185] \"sin\" \"sinh\" \n#> [187] \"sinpi\" \"sqrt\" \n#> [189] \"standardGeneric\" \"storage.mode<-\" \n#> [191] \"substitute\" \"sum\" \n#> [193] \"switch\" \"tan\" \n#> [195] \"tanh\" \"tanpi\" \n#> [197] \"tracemem\" \"trigamma\" \n#> [199] \"trunc\" \"unclass\" \n#> [201] \"untracemem\" \"UseMethod\" \n#> [203] \"while\" \"xtfrm\"\n# base\nmean\n#> function (x, ...) \n#> UseMethod(\"mean\")\n#> \n#> \n\n# other package function\npurrr::map\n#> function (.x, .f, ...) \n#> {\n#> .f <- as_mapper(.f, ...)\n#> .Call(map_impl, environment(), \".x\", \".f\", \"list\")\n#> }\n#> \n#> \nsum\n#> function (..., na.rm = FALSE) .Primitive(\"sum\")\nf <- function(x) mean(x)\nf\n#> function(x) mean(x)"},{"path":"functions.html","id":"lexical-scoping-exercises-6.4.5","chapter":"6 Functions","heading":"6.2 Lexical scoping (Exercises 6.4.5)","text":"Q1. following code return? ? Describe three c’s interpreted.A1. c(c = c):first c interpreted function call c()second c name vector elementthird c variable value 10You can also see lexical analysis expression:Q2. four principles govern R looks values?A2. Principles govern R looks values:Name masking (names defined inside function mask names defined outside function)Name masking (names defined inside function mask names defined outside function)Functions vs.Β variables (rule also applies function names)Functions vs.Β variables (rule also applies function names)fresh start (every time function called, new environment created host execution)fresh start (every time function called, new environment created host execution)Dynamic look-(R looks values function run, function created)Dynamic look-(R looks values function run, function created)Q3. following function return? Make prediction running code .A3. Correctly predicted πŸ˜‰Although multiple f() functions, order evaluation goes inside outside x^2 evaluated first f(x) * 2 evaluated last. results 202 (= ((10 ^ 2) + 1) * 2).","code":"\nc <- 10\nc(c = c)\nc <- 10\nc(c = c)\n#> c \n#> 10\np_expr <- parse(text = \"c(c = c)\", keep.source = TRUE)\ngetParseData(p_expr) %>% select(token, text)\n#> token text\n#> 12 expr \n#> 1 SYMBOL_FUNCTION_CALL c\n#> 3 expr \n#> 2 '(' (\n#> 4 SYMBOL_SUB c\n#> 5 EQ_SUB =\n#> 6 SYMBOL c\n#> 8 expr \n#> 7 ')' )\nf <- function(x) {\n f <- function(x) {\n f <- function() {\n x^2\n }\n f() + 1\n }\n f(x) * 2\n}\nf(10)\nf <- function(x) {\n f <- function(x) {\n f <- function() {\n x^2\n }\n f() + 1\n }\n f(x) * 2\n}\n\nf(10)\n#> [1] 202"},{"path":"functions.html","id":"lazy-evaluation-exercises-6.5.4","chapter":"6 Functions","heading":"6.3 Lazy evaluation (Exercises 6.5.4)","text":"Q1. important property && makes x_ok() work?different code? behaviour undesirable ?A1. && evaluates left right short-circuit evaluation, .e., first operand TRUE, R short-circuit even look second operand.Replacing && & undesirable performs element-wise logical comparisons returns vector values always useful decision (TRUE, FALSE, NA).Q2. function return? ? principle illustrate?A2. function returns 100 due lazy evaluation.function execution environment encounters x, evaluates argument x = z since name z already bound value 100 environment, x also bound value.can check looking memory addresses:Q3. function return? ? principle illustrate?A3. Let’s first look function returns:name masking. function call c(x, y), x accessed function environment, following promise evaluated function environment:, thus y gets assigned 1, x 2, since last value scope.Therefore, neither promise y = 0 global assignment y <- 10 ever consulted find value y.Q4. hist(), default value xlim range(breaks), default value breaks \"Sturges\", andExplain hist() works get correct xlim value.A4. xlim defines range histogram’s x-axis.default xlim = range(breaks) breaks = \"Sturges\" arguments reveal function uses Sturges’ algorithm compute number breaks.see implementation, run sloop::s3_get_method(\"hist.default\").hist() ensures chosen algorithm returns numeric vector containing least two unique elements xlim computed.Q5. Explain function works. confusing?A5. Let’s take step--step.function argument x missing function call. means stop(\"Error!\") evaluated function environment, global environment., due lazy evaluation, promise stop(\"Error!\") evaluated x accessed. happens print(x) called.print(x) leads x evaluated, evaluates stop function environment. , function environment, base::stop() masked locally defined stop() function, returns Sys.time() output.Q6. many arguments required calling library()?A6. Going solely signature,looks like following arguments required:, reality, one argument required: package. function internally checks arguments missing adjusts accordingly.better arguments NULL instead missing; avoid confusion.","code":"\nx_ok <- function(x) {\n !is.null(x) && length(x) == 1 && x > 0\n}\n\nx_ok(NULL)\nx_ok(1)\nx_ok(1:3)\nx_ok <- function(x) {\n !is.null(x) & length(x) == 1 & x > 0\n}\n\nx_ok(NULL)\nx_ok(1)\nx_ok(1:3)\nx_ok <- function(x) {\n !is.null(x) && length(x) == 1 && x > 0\n}\n\nx_ok(NULL)\n#> [1] FALSE\n\nx_ok(1)\n#> [1] TRUE\n\nx_ok(1:3)\n#> [1] FALSE\nx_ok <- function(x) {\n !is.null(x) & length(x) == 1 & x > 0\n}\n\nx_ok(NULL)\n#> logical(0)\n\nx_ok(1)\n#> [1] TRUE\n\nx_ok(1:3)\n#> [1] FALSE FALSE FALSE\nf2 <- function(x = z) {\n z <- 100\n x\n}\nf2()\nf2 <- function(x = z) {\n z <- 100\n print(lobstr::obj_addrs(list(x, z)))\n x\n}\n\nf2()\n#> [1] \"0x114d77808\" \"0x114d77808\"\n#> [1] 100\ny <- 10\nf1 <- function(x =\n {\n y <- 1\n 2\n },\n y = 0) {\n c(x, y)\n}\nf1()\ny\ny <- 10\nf1 <- function(x =\n {\n y <- 1\n 2\n },\n y = 0) {\n c(x, y)\n}\nf1()\n#> [1] 2 1\ny\n#> [1] 10\nx <- {\n y <- 1\n 2\n}\nrange(\"Sturges\")\n#> [1] \"Sturges\" \"Sturges\"\nhist(mtcars$wt, xlim = c(1, 6))\nnclass.Sturges(mtcars$wt)\n#> [1] 6\nshow_time <- function(x = stop(\"Error!\")) {\n stop <- function(...) Sys.time()\n print(x)\n}\n\nshow_time()\n#> [1] \"2022-11-12 11:48:38 CET\"\nformals(library)\n#> $package\n#> \n#> \n#> $help\n#> \n#> \n#> $pos\n#> [1] 2\n#> \n#> $lib.loc\n#> NULL\n#> \n#> $character.only\n#> [1] FALSE\n#> \n#> $logical.return\n#> [1] FALSE\n#> \n#> $warn.conflicts\n#> \n#> \n#> $quietly\n#> [1] FALSE\n#> \n#> $verbose\n#> getOption(\"verbose\")\n#> \n#> $mask.ok\n#> \n#> \n#> $exclude\n#> \n#> \n#> $include.only\n#> \n#> \n#> $attach.required\n#> missing(include.only)\nformals(library) %>%\n purrr::discard(is.null) %>%\n purrr::map_lgl(~ .x == \"\") %>%\n purrr::keep(~ isTRUE(.x)) %>%\n names()\n#> [1] \"package\" \"help\" \"warn.conflicts\"\n#> [4] \"mask.ok\" \"exclude\" \"include.only\""},{"path":"functions.html","id":"dot-dot-dot-exercises-6.6.1","chapter":"6 Functions","heading":"6.4 ... (dot-dot-dot) (Exercises 6.6.1)","text":"Q1. Explain following results:A1. Let’s look arguments functions:can seen, sum() function doesn’t na.omit argument. , input na.omit = TRUE treated 1 (logical implicitly coerced numeric), thus results. , expression evaluates sum(1, 2, 3, 1).mean() function, one parameter (x) ’s matched first argument (1). , expression evaluates mean(1).Q2. Explain find documentation named arguments following function call:A2. Typing ?plot console, see documentation, also shows signature:Since ... passed par(), can look ?par docs:.docs parameters interest reside .Q3. plot(1:10, col = \"red\") colour points, axes labels? Read source code plot.default() find .A3. Source code can found .plot.default() passes ... localTitle(), passes title().title() four parts: main, sub, xlab, ylab.single argument col work ambiguous element apply argument .","code":"\nsum(1, 2, 3)\n#> [1] 6\nmean(1, 2, 3)\n#> [1] 1\n\nsum(1, 2, 3, na.omit = TRUE)\n#> [1] 7\nmean(1, 2, 3, na.omit = TRUE)\n#> [1] 1\nstr(sum)\n#> function (..., na.rm = FALSE)\nstr(mean)\n#> function (x, ...)\nplot(1:10, col = \"red\", pch = 20, xlab = \"x\", col.lab = \"blue\")#> function (x, y, ...)#> function (..., no.readonly = FALSE)\nlocalTitle <- function(..., col, bg, pch, cex, lty, lwd) title(...)\n\ntitle <- function(main = NULL, sub = NULL, xlab = NULL, ylab = NULL,\n line = NA, outer = FALSE, ...) {\n main <- as.graphicsAnnot(main)\n sub <- as.graphicsAnnot(sub)\n xlab <- as.graphicsAnnot(xlab)\n ylab <- as.graphicsAnnot(ylab)\n .External.graphics(C_title, main, sub, xlab, ylab, line, outer, ...)\n invisible()\n}"},{"path":"functions.html","id":"exiting-a-function-exercises-6.7.5","chapter":"6 Functions","heading":"6.5 Exiting a function (Exercises 6.7.5)","text":"Q1. load() return? don’t normally see values?A1. load() function reloads datasets saved using save() function:normally don’t see value function loads datasets invisibly.can change setting verbose = TRUE:Q2. write.table() return? useful?A2. write.table() writes data frame file returns NULL invisibly.helpful function invisibly returned actual object written file, used.Q3. chdir parameter source() compare with_dir()? might prefer one ?A3. chdir parameter source() described :TRUE file pathname, R working directory temporarily changed directory containing file evaluatingThat , chdir allows changing working directory temporarily directory containing file sourced:withr::with_dir() temporarily changes current working directory:importantly, parameters dir allows temporarily changing working directory directory.Q4. Write function opens graphics device, runs supplied code, closes graphics device (always, regardless whether plotting code works).A4. function opens graphics device, runs supplied code, closes graphics device:Q5. can use .exit() implement simple version capture.output().Compare capture.output() capture.output2(). functions differ? features removed make key ideas easier see? rewritten key ideas ’re easier understand?A5. capture.output() significantly complex, can seen definition:key differences:capture.output() uses print() function print console:capture.output() can capture messages well:capture.output() takes account visibility expression:","code":"\nsave(iris, file = \"my_iris.rda\")\nload(\"my_iris.rda\")\nload(\"my_iris.rda\", verbose = TRUE)\n#> Loading objects:\n#> iris\n\n# cleanup\nunlink(\"my_iris.rda\")\nwrite.table(BOD, file = \"BOD.csv\")\n# cleanup\nunlink(\"BOD.csv\")\nwithr::with_dir\n#> function (new, code) \n#> {\n#> old <- setwd(dir = new)\n#> on.exit(setwd(old))\n#> force(code)\n#> }\n#> \n#> \nwith_png_device <- function(filename, code, ...) {\n grDevices::png(filename = filename, ...)\n on.exit(grDevices::dev.off(), add = TRUE)\n\n force(code)\n}\ncapture.output2 <- function(code) {\n temp <- tempfile()\n on.exit(file.remove(temp), add = TRUE, after = TRUE)\n\n sink(temp)\n on.exit(sink(), add = TRUE, after = TRUE)\n\n force(code)\n readLines(temp)\n}\n\ncapture.output2(cat(\"a\", \"b\", \"c\", sep = \"\\n\"))\n#> [1] \"a\" \"b\" \"c\"\ncapture.output\n#> function (..., file = NULL, append = FALSE, type = c(\"output\", \n#> \"message\"), split = FALSE) \n#> {\n#> type <- match.arg(type)\n#> rval <- NULL\n#> closeit <- TRUE\n#> if (is.null(file)) \n#> file <- textConnection(\"rval\", \"w\", local = TRUE)\n#> else if (is.character(file)) \n#> file <- file(file, if (append) \n#> \"a\"\n#> else \"w\")\n#> else if (inherits(file, \"connection\")) {\n#> if (!isOpen(file)) \n#> open(file, if (append) \n#> \"a\"\n#> else \"w\")\n#> else closeit <- FALSE\n#> }\n#> else stop(\"'file' must be NULL, a character string or a connection\")\n#> sink(file, type = type, split = split)\n#> on.exit({\n#> sink(type = type, split = split)\n#> if (closeit) close(file)\n#> })\n#> for (i in seq_len(...length())) {\n#> out <- withVisible(...elt(i))\n#> if (out$visible) \n#> print(out$value)\n#> }\n#> on.exit()\n#> sink(type = type, split = split)\n#> if (closeit) \n#> close(file)\n#> if (is.null(rval)) \n#> invisible(NULL)\n#> else rval\n#> }\n#> \n#> \ncapture.output(1)\n#> [1] \"[1] 1\"\n\ncapture.output2(1)\n#> character(0)\ncapture.output(message(\"Hi there!\"), \"a\", type = \"message\")\n#> Hi there!\n#> [1] \"a\"\n#> character(0)\ncapture.output(1, invisible(2), 3)\n#> [1] \"[1] 1\" \"[1] 3\""},{"path":"functions.html","id":"function-forms-exercises-6.8.6","chapter":"6 Functions","heading":"6.6 Function forms (Exercises 6.8.6)","text":"Q1. Rewrite following code snippets prefix form:A1. Prefix forms code snippets:Q2. Clarify following list odd function calls:A2. functions don’t dots (...) parameters, argument matching takes place following steps:exact matching named argumentspartial matchingposition-basedQ3. Explain following code fails:A3. provided book, replacement function defined :Let’s re-write provided code prefix format understand doesn’t work:Although works:following doesn’t code evaluates :get<- function R.Q4. Create replacement function modifies random location vector.A4. replacement function modifies random location vector:Let’s try :Q5. Write version + pastes inputs together character vectors behaves usual otherwise. words, make code work:A5. Infix operator re-create desired output:Q6. Create list replacement functions found base package. ones primitive functions? (Hint: use apropos().)A6. Replacement functions always <- end names., using apropos(), can find replacement functions search paths filter ones don’t belong {base} package:primitive replacement functions can listed using .primitive():Q7. valid names user-created infix functions?A7. mentioned respective section book:names infix functions flexible regular R functions: can contain sequence characters except %.Q8. Create infix xor() operator.A8. Exclusive logical operation TRUE arguments differ (one TRUE, FALSE).can create infix operator exclusive like :function vectorized inputs underlying logical operators vectorized.Q9. Create infix versions set functions intersect(), union(), setdiff(). might call %n%, %u%, %/% match conventions mathematics.A9. required infix operators can created following:can check outputs agree underlying functions:","code":"\n1 + 2 + 3\n\n1 + (2 + 3)\n\nif (length(x) <= 5) x[[5]] else x[[n]]\n# The binary `+` operator has left to right associative property.\n`+`(`+`(1, 2), 3)\n\n`+`(1, `(`(`+`(2, 3)))\n\n`if`(cond = `<=`(length(x), 5), cons.expr = `[[`(x, 5), alt.expr = `[[`(x, n))\nx <- sample(replace = TRUE, 20, x = c(1:10, NA))\ny <- runif(min = 0, max = 1, 20)\ncor(m = \"k\", y = y, u = \"p\", x = x)\nmodify(get(\"x\"), 1) <- 10\n#> Error: target of assignment expands to non-language object\n`modify<-` <- function(x, position, value) {\n x[position] <- value\n x\n}\nget(\"x\") <- `modify<-`(x = get(\"x\"), position = 1, value = 10)\nx <- 5\n`modify<-`(x = get(\"x\"), position = 1, value = 10)\n#> [1] 10\n`get<-`(\"x\", 10)\n#> Error in `get<-`(\"x\", 10): could not find function \"get<-\"\n`random_modify<-` <- function(x, value) {\n random_index <- sample(seq_along(x), size = 1)\n x[random_index] <- value\n return(x)\n}\nx1 <- rep(\"a\", 10)\nrandom_modify(x1) <- \"X\"\nx1\n#> [1] \"a\" \"a\" \"a\" \"a\" \"X\" \"a\" \"a\" \"a\" \"a\" \"a\"\n\nx2 <- rep(\"a\", 10)\nrandom_modify(x2) <- \"Y\"\nx2\n#> [1] \"a\" \"a\" \"a\" \"a\" \"a\" \"Y\" \"a\" \"a\" \"a\" \"a\"\n\nx3 <- rep(0, 15)\nrandom_modify(x3) <- -4\nx3\n#> [1] 0 0 0 0 -4 0 0 0 0 0 0 0 0 0 0\n\nx4 <- rep(0, 15)\nrandom_modify(x4) <- -1\nx4\n#> [1] 0 0 0 0 0 0 0 0 0 0 0 0 -1 0 0\n1 + 2\n#> [1] 3\n\n\"a\" + \"b\"\n#> [1] \"ab\"\n`+` <- function(x, y) {\n if (is.character(x) || is.character(y)) {\n paste0(x, y)\n } else {\n base::`+`(x, y)\n }\n}\n\n1 + 2\n#> [1] 3\n\n\"a\" + \"b\"\n#> [1] \"ab\"\n\nrm(\"+\", envir = .GlobalEnv)\nls_replacement <- apropos(\"<-\", where = TRUE, mode = \"function\")\n\nbase_index <- which(grepl(\"base\", searchpaths()))\n\nls_replacement <- ls_replacement[which(names(ls_replacement) == as.character(base_index))]\n\nunname(ls_replacement)\n#> [1] \".rowNamesDF<-\" \"[[<-\" \n#> [3] \"[[<-.data.frame\" \"[[<-.factor\" \n#> [5] \"[[<-.numeric_version\" \"[[<-.POSIXlt\" \n#> [7] \"[<-\" \"[<-.data.frame\" \n#> [9] \"[<-.Date\" \"[<-.difftime\" \n#> [11] \"[<-.factor\" \"[<-.numeric_version\" \n#> [13] \"[<-.POSIXct\" \"[<-.POSIXlt\" \n#> [15] \"@<-\" \"<-\" \n#> [17] \"<<-\" \"$<-\" \n#> [19] \"$<-.data.frame\" \"attr<-\" \n#> [21] \"attributes<-\" \"body<-\" \n#> [23] \"class<-\" \"colnames<-\" \n#> [25] \"comment<-\" \"diag<-\" \n#> [27] \"dim<-\" \"dimnames<-\" \n#> [29] \"dimnames<-.data.frame\" \"Encoding<-\" \n#> [31] \"environment<-\" \"formals<-\" \n#> [33] \"is.na<-\" \"is.na<-.default\" \n#> [35] \"is.na<-.factor\" \"is.na<-.numeric_version\"\n#> [37] \"length<-\" \"length<-.Date\" \n#> [39] \"length<-.difftime\" \"length<-.factor\" \n#> [41] \"length<-.POSIXct\" \"length<-.POSIXlt\" \n#> [43] \"levels<-\" \"levels<-.factor\" \n#> [45] \"mode<-\" \"mostattributes<-\" \n#> [47] \"names<-\" \"names<-.POSIXlt\" \n#> [49] \"oldClass<-\" \"parent.env<-\" \n#> [51] \"regmatches<-\" \"row.names<-\" \n#> [53] \"row.names<-.data.frame\" \"row.names<-.default\" \n#> [55] \"rownames<-\" \"split<-\" \n#> [57] \"split<-.data.frame\" \"split<-.default\" \n#> [59] \"storage.mode<-\" \"substr<-\" \n#> [61] \"substring<-\" \"units<-\" \n#> [63] \"units<-.difftime\"\nmget(ls_replacement, envir = baseenv()) %>%\n purrr::keep(is.primitive) %>%\n names()\n#> [1] \"[[<-\" \"[<-\" \"@<-\" \n#> [4] \"<-\" \"<<-\" \"$<-\" \n#> [7] \"attr<-\" \"attributes<-\" \"class<-\" \n#> [10] \"dim<-\" \"dimnames<-\" \"environment<-\" \n#> [13] \"length<-\" \"levels<-\" \"names<-\" \n#> [16] \"oldClass<-\" \"storage.mode<-\"\nlv1 <- c(TRUE, FALSE, TRUE, FALSE)\nlv2 <- c(TRUE, TRUE, FALSE, FALSE)\n\nxor(lv1, lv2)\n#> [1] FALSE TRUE TRUE FALSE\n`%xor%` <- function(x, y) {\n !((x & y) | !(x | y))\n}\n\nlv1 %xor% lv2\n#> [1] FALSE TRUE TRUE FALSE\n\nTRUE %xor% TRUE\n#> [1] FALSE\n`%n%` <- function(x, y) {\n intersect(x, y)\n}\n\n`%u%` <- function(x, y) {\n union(x, y)\n}\n\n`%/%` <- function(x, y) {\n setdiff(x, y)\n}\n(x <- c(sort(sample(1:20, 9)), NA))\n#> [1] 4 7 8 9 11 13 15 16 20 NA\n(y <- c(sort(sample(3:23, 7)), NA))\n#> [1] 9 10 13 15 17 19 20 NA\n\nidentical(intersect(x, y), x %n% y)\n#> [1] TRUE\nidentical(union(x, y), x %u% y)\n#> [1] TRUE\nidentical(setdiff(x, y), x %/% y)\n#> [1] TRUE"},{"path":"functions.html","id":"session-information-4","chapter":"6 Functions","heading":"6.7 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)\n#> backports 1.4.1 2021-12-13 [1] CRAN (R 4.2.0)\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> broom 1.0.1 2022-08-29 [1] CRAN (R 4.2.0)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cellranger 1.1.0 2016-07-27 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1)\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)\n#> dbplyr 2.2.1 2022-06-27 [1] CRAN (R 4.2.0)\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.1)\n#> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.2.0)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> forcats * 0.5.2 2022-08-19 [1] CRAN (R 4.2.1)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> gargle 1.2.1 2022-09-08 [1] CRAN (R 4.2.1)\n#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1)\n#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.2)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> googledrive 2.0.0 2021-07-08 [1] CRAN (R 4.2.0)\n#> googlesheets4 1.0.1 2022-08-13 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> P grid 4.2.2 2022-10-31 [1] local\n#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.1)\n#> haven 2.5.1 2022-08-22 [1] CRAN (R 4.2.0)\n#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0)\n#> hms 1.1.2 2022-08-19 [1] CRAN (R 4.2.0)\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> httr 1.4.4 2022-08-17 [1] CRAN (R 4.2.0)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> lobstr 1.1.2 2022-06-22 [1] CRAN (R 4.2.0)\n#> lubridate 1.9.0 2022-11-06 [1] CRAN (R 4.2.2)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> modelr 0.1.10 2022-11-11 [1] CRAN (R 4.2.2)\n#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0)\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)\n#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> readr * 2.1.3 2022-10-01 [1] CRAN (R 4.2.1)\n#> readxl 1.4.1 2022-08-17 [1] CRAN (R 4.2.0)\n#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.1)\n#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> rvest 1.0.3 2022-08-19 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr * 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> tibble * 3.1.8.9002 2022-10-16 [1] local\n#> tidyr * 1.2.1 2022-09-08 [1] CRAN (R 4.2.1)\n#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1)\n#> tidyverse * 1.3.2 2022-07-18 [1] CRAN (R 4.2.0)\n#> timechange 0.1.1 2022-11-04 [1] CRAN (R 4.2.2)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> tzdb 0.3.0 2022-03-28 [1] CRAN (R 4.2.0)\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"environments.html","id":"environments","chapter":"7 Environments","heading":"7 Environments","text":"Loading needed libraries:","code":"\nlibrary(rlang, warn.conflicts = FALSE)"},{"path":"environments.html","id":"environment-basics-exercises-7.2.7","chapter":"7 Environments","heading":"7.1 Environment basics (Exercises 7.2.7)","text":"Q1. List three ways environment differs list.A1. mentioned book, ways environments differ lists:Q2. Create environment illustrated picture.A2. Creating environment illustrated picture:binding loop memory address environment e:Q3. Create pair environments illustrated picture.A3. Creating specified environment:Q4. Explain e[[1]] e[c(\"\", \"b\")] don’t make sense e environment.A4. environment non-linear data structure, concept ordered elements. Therefore, indexing (e.g.Β e[[1]]) doesn’t make sense.Subsetting list vector returns subset underlying data structure. example, subsetting vector returns another vector. ’s unclear subsetting environment (e.g.Β e[c(\"\", \"b\")]) return data structure contain returns. can’t another environment since environments reference semantics.Q5. Create version env_poke() bind new names, never re-bind old names. programming languages , known single assignment languages.A5. Create version env_poke() doesn’t allow re-binding old names:Making sure behaves expected:Contrast behavior following:Q6. function ? differ <<- might prefer ?A6. downside <<- create new binding doesn’t exist given environment, something may wish:rebind() function let us know binding doesn’t exist, much safer:","code":"\nlibrary(rlang)\n\ne <- env()\ne$loop <- e\nenv_print(e)\n#> \n#> Parent: \n#> Bindings:\n#> β€’ loop: \nlobstr::ref(e$loop)\n#> β–ˆ [1:0x139a85110] \n#> └─loop = [1:0x139a85110]\ne1 <- env()\ne2 <- env()\n\ne1$loop <- e2\ne2$deloop <- e1\n\n# following should be the same\nlobstr::obj_addrs(list(e1, e2$deloop))\n#> [1] \"0x1083b4018\" \"0x1083b4018\"\nlobstr::obj_addrs(list(e2, e1$loop))\n#> [1] \"0x108409438\" \"0x108409438\"\nenv_poke2 <- function(env, nm, value) {\n if (env_has(env, nm)) {\n abort(\"Can't re-bind existing names.\")\n }\n\n env_poke(env, nm, value)\n}\ne <- env(a = 1, b = 2, c = 3)\n\n# re-binding old names not allowed\nenv_poke2(e, \"b\", 4)\n#> Error in `env_poke2()`:\n#> ! Can't re-bind existing names.\n\n# binding new names allowed\nenv_poke2(e, \"d\", 8)\ne$d\n#> [1] 8\ne <- env(a = 1, b = 2, c = 3)\n\ne$b\n#> [1] 2\n\n# re-binding old names allowed\nenv_poke(e, \"b\", 4)\ne$b\n#> [1] 4\nrebind <- function(name, value, env = caller_env()) {\n if (identical(env, empty_env())) {\n stop(\"Can't find `\", name, \"`\", call. = FALSE)\n } else if (env_has(env, name)) {\n env_poke(env, name, value)\n } else {\n rebind(name, value, env_parent(env))\n }\n}\nrebind(\"a\", 10)\n#> Error: Can't find `a`\na <- 5\nrebind(\"a\", 10)\na\n#> [1] 10\n# `x` doesn't exist\nexists(\"x\")\n#> [1] FALSE\n\n# so `<<-` will create one for us\n{\n x <<- 5\n}\n\n# in the global environment\nenv_has(global_env(), \"x\")\n#> x \n#> TRUE\nx\n#> [1] 5\nrebind <- function(name, value, env = caller_env()) {\n if (identical(env, empty_env())) {\n stop(\"Can't find `\", name, \"`\", call. = FALSE)\n } else if (env_has(env, name)) {\n env_poke(env, name, value)\n } else {\n rebind(name, value, env_parent(env))\n }\n}\n\n# doesn't exist\nexists(\"abc\")\n#> [1] FALSE\n\n# so function will produce an error instead of creating it for us\nrebind(\"abc\", 10)\n#> Error: Can't find `abc`\n\n# but it will work as expected when the variable already exists\nabc <- 5\nrebind(\"abc\", 10)\nabc\n#> [1] 10"},{"path":"environments.html","id":"recursing-over-environments-exercises-7.3.1","chapter":"7 Environments","heading":"7.2 Recursing over environments (Exercises 7.3.1)","text":"Q1. Modify () return environments contain binding name. Carefully think type object function need return.A1. modified version () returns environments contain binding name.Since anticipate one environment, dynamically update list time environment specified binding found. important initialize empty list since signifies given binding found environments.Let’s try :Q2. Write function called fget() finds function objects. two arguments, name env, obey regular scoping rules functions: ’s object matching name ’s function, look parent. added challenge, also add inherits argument controls whether function recurses parents looks one environment.A2. function recursively looks function objects:Let’s try :","code":"\nwhere <- function(name, env = caller_env()) {\n env_list <- list()\n\n while (!identical(env, empty_env())) {\n if (env_has(env, name)) {\n env_list <- append(env_list, env)\n }\n\n env <- env_parent(env)\n }\n\n return(env_list)\n}\nwhere(\"yyy\")\n#> list()\n\nx <- 5\nwhere(\"x\")\n#> [[1]]\n#> \n\nwhere(\"mean\")\n#> [[1]]\n#> \n\nlibrary(dplyr, warn.conflicts = FALSE)\nwhere(\"filter\")\n#> [[1]]\n#> \n#> attr(,\"name\")\n#> [1] \"package:dplyr\"\n#> attr(,\"path\")\n#> [1] \"/Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library/dplyr\"\n#> \n#> [[2]]\n#> \n#> attr(,\"name\")\n#> [1] \"package:stats\"\n#> attr(,\"path\")\n#> [1] \"/Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library/stats\"\ndetach(\"package:dplyr\")\nfget <- function(name, env = caller_env(), inherits = FALSE) {\n # we need only function objects\n f_value <- mget(name,\n envir = env,\n mode = \"function\",\n inherits = FALSE, # since we have our custom argument\n ifnotfound = list(NULL)\n )\n\n if (!is.null(f_value[[1]])) {\n # success case\n f_value[[1]]\n } else {\n if (inherits && !identical(env, empty_env())) {\n # recursive case\n env <- env_parent(env)\n fget(name, env, inherits = TRUE)\n } else {\n # base case\n stop(\"No function objects with matching name was found.\", call. = FALSE)\n }\n }\n}\nfget(\"mean\", inherits = FALSE)\n#> Error: No function objects with matching name was found.\n\nfget(\"mean\", inherits = TRUE)\n#> function (x, ...) \n#> UseMethod(\"mean\")\n#> \n#> \n\nmean <- 5\nfget(\"mean\", inherits = FALSE)\n#> Error: No function objects with matching name was found.\n\nmean <- function() NULL\nfget(\"mean\", inherits = FALSE)\n#> function() NULL\nrm(\"mean\")"},{"path":"environments.html","id":"special-environments-exercises-7.4.5","chapter":"7 Environments","heading":"7.3 Special environments (Exercises 7.4.5)","text":"Q1. search_envs() different env_parents(global_env())?A1. search_envs() lists chain environments currently attached search path contains exported functions packages. search path always ends {base} package environment. search path also includes global environment.env_parents() lists parent environments empty environment. course, global environment included list.Q2. Draw diagram shows enclosing environments function:A2. don’t access graphics software used create diagrams book, linking diagram official solutions manual, also find detailed description figure:Q3. Write enhanced version str() provides information functions. Show function found environment defined .A3. write required function, can first re-purpose fget() function wrote return environment found enclosing environment:Let’s try :can now write new version str() wrapper around function. need foresee users might enter function name either symbol string.Let’s first try base::mean():variant present global environment:","code":"\nsearch_envs()\n#> [[1]] $ \n#> [[2]] $ \n#> [[3]] $ \n#> [[4]] $ \n#> [[5]] $ \n#> [[6]] $ \n#> [[7]] $ \n#> [[8]] $ \n#> [[9]] $ \n#> [[10]] $ \n#> [[11]] $ \nenv_parents(global_env())\n#> [[1]] $ \n#> [[2]] $ \n#> [[3]] $ \n#> [[4]] $ \n#> [[5]] $ \n#> [[6]] $ \n#> [[7]] $ \n#> [[8]] $ \n#> [[9]] $ \n#> [[10]] $ \n#> [[11]] $ \nf1 <- function(x1) {\n f2 <- function(x2) {\n f3 <- function(x3) {\n x1 + x2 + x3\n }\n f3(3)\n }\n f2(2)\n}\nf1(1)\nfget2 <- function(name, env = caller_env()) {\n # we need only function objects\n f_value <- mget(name,\n envir = env,\n mode = \"function\",\n inherits = FALSE,\n ifnotfound = list(NULL)\n )\n\n if (!is.null(f_value[[1]])) {\n # success case\n list(\n \"where\" = env,\n \"enclosing\" = fn_env(f_value[[1]])\n )\n } else {\n if (!identical(env, empty_env())) {\n # recursive case\n env <- env_parent(env)\n fget2(name, env)\n } else {\n # base case\n stop(\"No function objects with matching name was found.\", call. = FALSE)\n }\n }\n}\nfget2(\"mean\")\n#> $where\n#> \n#> \n#> $enclosing\n#> \n\nmean <- function() NULL\nfget2(\"mean\")\n#> $where\n#> \n#> \n#> $enclosing\n#> \nrm(\"mean\")\nstr_function <- function(.f) {\n fget2(as_string(ensym(.f)))\n}\nstr_function(mean)\n#> $where\n#> \n#> \n#> $enclosing\n#> \n\nstr_function(\"mean\")\n#> $where\n#> \n#> \n#> $enclosing\n#> \nmean <- function() NULL\n\nstr_function(mean)\n#> $where\n#> \n#> \n#> $enclosing\n#> \n\nstr_function(\"mean\")\n#> $where\n#> \n#> \n#> $enclosing\n#> \n\nrm(\"mean\")"},{"path":"environments.html","id":"call-stacks-exercises-7.5.5","chapter":"7 Environments","heading":"7.4 Call stacks (Exercises 7.5.5)","text":"Q1. Write function lists variables defined environment called. return results ls().A1. function lists variables defined environment called:workhorse rlang::caller_env(), let’s also look definition:Let’s try :global environment:function environment:","code":"\n# let's first remove everything that exists in the global environment right now\n# to test with only newly defined objects\nrm(list = ls())\nrm(.Random.seed, envir = globalenv())\n\nls_env <- function(env = rlang::caller_env()) {\n sort(rlang::env_names(env))\n}\nrlang::caller_env\n#> function (n = 1) \n#> {\n#> parent.frame(n + 1)\n#> }\n#> \n#> \nx <- \"a\"\ny <- 1\n\nls_env()\n#> [1] \"ls_env\" \"x\" \"y\"\n\nls()\n#> [1] \"ls_env\" \"x\" \"y\"\nfoo <- function() {\n a <- \"x\"\n b <- 2\n\n print(ls_env())\n\n print(ls())\n}\n\nfoo()\n#> [1] \"a\" \"b\"\n#> [1] \"a\" \"b\""},{"path":"environments.html","id":"session-information-5","chapter":"7 Environments","heading":"7.5 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1)\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0)\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> lobstr 1.1.2 2022-06-22 [1] CRAN (R 4.2.0)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)\n#> png 0.1-7 2013-12-03 [1] CRAN (R 4.2.0)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> tibble 3.1.8.9002 2022-10-16 [1] local\n#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"conditions.html","id":"conditions","chapter":"8 Conditions","heading":"8 Conditions","text":"Attaching needed libraries:","code":"\nlibrary(rlang, warn.conflicts = FALSE)\nlibrary(testthat, warn.conflicts = FALSE)"},{"path":"conditions.html","id":"signalling-conditions-exercises-8.2.4","chapter":"8 Conditions","heading":"8.1 Signalling conditions (Exercises 8.2.4)","text":"Q1. Write wrapper around file.remove() throws error file deleted exist.A1. Let’s first create wrapper function around file.remove() throws error file deleted exist.Let’s first create file can delete immediately.function fail files provided don’t exist:work expected file exists:Q2. appendLF argument message() ? related cat()?A2. mentioned docs message(), appendLF argument decides:messages given character string newline appended?TRUE (default value), final newline regarded part message:FALSE, messages concatenated:hand, cat() converts arguments character vectors concatenates single character vector default:order get message()-like default behavior outputs, can set sep = \"\\n\":","code":"\nfileRemove <- function(...) {\n existing_files <- fs::file_exists(...)\n\n if (!all(existing_files)) {\n stop(\n cat(\n \"The following files to be deleted don't exist:\",\n names(existing_files[!existing_files]),\n sep = \"\\n\"\n ),\n call. = FALSE\n )\n }\n\n file.remove(...)\n}\nfs::file_create(\"random.R\")\nfileRemove(c(\"random.R\", \"XYZ.csv\"))\n#> The following files to be deleted don't exist:\n#> XYZ.csv\n#> Error:\nfileRemove(\"random.R\")\n#> [1] TRUE\nfoo <- function(appendLF) {\n message(\"Beetle\", appendLF = appendLF)\n message(\"Juice\", appendLF = appendLF)\n}\n\nfoo(appendLF = TRUE)\n#> Beetle\n#> Juice\nfoo <- function(appendLF) {\n message(\"Beetle\", appendLF = appendLF)\n message(\"Juice\", appendLF = appendLF)\n}\n\nfoo(appendLF = FALSE)\n#> BeetleJuice\nfoo <- function() {\n cat(\"Beetle\")\n cat(\"Juice\")\n}\n\nfoo()\n#> BeetleJuice\nfoo <- function() {\n cat(\"Beetle\", sep = \"\\n\")\n cat(\"Juice\", sep = \"\\n\")\n}\n\nfoo()\n#> Beetle\n#> Juice"},{"path":"conditions.html","id":"handling-conditions-exercises-8.4.5","chapter":"8 Conditions","heading":"8.2 Handling conditions (Exercises 8.4.5)","text":"Q1. extra information condition generated abort() contain compared condition generated stop() .e. ’s difference two objects? Read help ?abort learn .A1. Compared base::stop(), rlang::abort() contains two additional pieces information:trace: traceback capturing sequence calls lead current functionparent: Information another condition used parent create chained condition.Q2. Predict results evaluating following codeA2. Correctly predicted πŸ˜‰first three pieces code straightforward:last piece code challenging one illustrates tryCatch() works. docs:several handlers supplied single tryCatch first one considered recent second.Q3. Explain results running code:A3. surprising part output b last c.happens inner calling handler doesn’t handle message, bubbles outer calling handler.Q4. Read source code catch_cnd() explain works.A4. Let’s look source code catch_cnd():mentioned function docs:small wrapper around tryCatch() captures condition signalled evaluating argument.classes argument allows character vector condition classes catch, complex tidy evaluation generates necessary condition (; otherwise NULL).Q5. rewrite show_condition() use single handler?A5. source code rlang::catch_cond() gives us clue can .Conditions also class attribute, can use determine handler match condition.Let’s try new version examples used original version:","code":"\ncatch_cnd(stop(\"An error\"))\ncatch_cnd(abort(\"An error\"))\nlibrary(rlang)\n\nstopInfo <- catch_cnd(stop(\"An error\"))\nabortInfo <- catch_cnd(abort(\"An error\"))\n\nstr(stopInfo)\n#> List of 2\n#> $ message: chr \"An error\"\n#> $ call : language force(expr)\n#> - attr(*, \"class\")= chr [1:3] \"simpleError\" \"error\" \"condition\"\n\nstr(abortInfo)\n#> List of 4\n#> $ message: chr \"An error\"\n#> $ trace :Classes 'rlang_trace', 'rlib_trace', 'tbl' and 'data.frame': 8 obs. of 6 variables:\n#> ..$ call :List of 8\n#> .. ..$ : language catch_cnd(abort(\"An error\"))\n#> .. ..$ : language eval_bare(rlang::expr(tryCatch(!!!handlers, { force(expr) ...\n#> .. ..$ : language tryCatch(condition = ``, { force(expr) ...\n#> .. ..$ : language tryCatchList(expr, classes, parentenv, handlers)\n#> .. ..$ : language tryCatchOne(expr, names, parentenv, handlers[[1L]])\n#> .. ..$ : language doTryCatch(return(expr), name, parentenv, handler)\n#> .. ..$ : language force(expr)\n#> .. ..$ : language abort(\"An error\")\n#> ..$ parent : int [1:8] 0 1 1 3 4 5 1 0\n#> ..$ visible : logi [1:8] FALSE FALSE FALSE FALSE FALSE FALSE ...\n#> ..$ namespace : chr [1:8] \"rlang\" \"rlang\" \"base\" \"base\" ...\n#> ..$ scope : chr [1:8] \"::\" \"::\" \"::\" \"local\" ...\n#> ..$ error_frame: logi [1:8] FALSE FALSE FALSE FALSE FALSE FALSE ...\n#> ..- attr(*, \"version\")= int 2\n#> $ parent : NULL\n#> $ call : NULL\n#> - attr(*, \"class\")= chr [1:3] \"rlang_error\" \"error\" \"condition\"\nshow_condition <- function(code) {\n tryCatch(\n error = function(cnd) \"error\",\n warning = function(cnd) \"warning\",\n message = function(cnd) \"message\",\n {\n code\n NULL\n }\n )\n}\n\nshow_condition(stop(\"!\"))\nshow_condition(10)\nshow_condition(warning(\"?!\"))\nshow_condition({\n 10\n message(\"?\")\n warning(\"?!\")\n})\nshow_condition <- function(code) {\n tryCatch(\n error = function(cnd) \"error\",\n warning = function(cnd) \"warning\",\n message = function(cnd) \"message\",\n {\n code\n NULL\n }\n )\n}\n\nshow_condition(stop(\"!\"))\n#> [1] \"error\"\nshow_condition(10)\n#> NULL\nshow_condition(warning(\"?!\"))\n#> [1] \"warning\"\nshow_condition({\n 10\n message(\"?\")\n warning(\"?!\")\n})\n#> [1] \"message\"\nwithCallingHandlers(\n message = function(cnd) message(\"b\"),\n withCallingHandlers(\n message = function(cnd) message(\"a\"),\n message(\"c\")\n )\n)\n#> b\n#> a\n#> b\n#> c\nrlang::catch_cnd\n#> function (expr, classes = \"condition\") \n#> {\n#> stopifnot(is_character(classes))\n#> handlers <- rep_named(classes, list(identity))\n#> eval_bare(rlang::expr(tryCatch(!!!handlers, {\n#> force(expr)\n#> return(NULL)\n#> })))\n#> }\n#> \n#> \ncatch_cnd(10)\n#> NULL\n\ncatch_cnd(abort(message = \"an error\", class = \"class1\"))\n#> \n#> Error:\n#> ! an error\n#> ---\n#> Backtrace:\nshow_condition2 <- function(code) {\n tryCatch(\n condition = function(cnd) {\n if (inherits(cnd, \"error\")) {\n return(\"error\")\n }\n if (inherits(cnd, \"warning\")) {\n return(\"warning\")\n }\n if (inherits(cnd, \"message\")) {\n return(\"message\")\n }\n },\n {\n code\n NULL\n }\n )\n}\nshow_condition2(stop(\"!\"))\n#> [1] \"error\"\nshow_condition2(10)\n#> NULL\nshow_condition2(warning(\"?!\"))\n#> [1] \"warning\"\nshow_condition2({\n 10\n message(\"?\")\n warning(\"?!\")\n})\n#> [1] \"message\""},{"path":"conditions.html","id":"custom-conditions-exercises-8.5.4","chapter":"8 Conditions","heading":"8.3 Custom conditions (Exercises 8.5.4)","text":"Q1. Inside package, ’s occasionally useful check package installed using . Write function checks package installed (requireNamespace(\"pkg\", quietly = FALSE)) , throws custom condition includes package name metadata.A1. desired function:reference, also see source code following functions:rlang::is_installed()insight::check_if_installed()Q2. Inside package often need stop error something right. packages depend package might tempted check errors unit tests. help packages avoid relying error message part user interface rather API might change without notice?A2. example, let’s say another package developer wanted use check_if_pkg_installed() function just wrote.developer using package can write unit test like :dissuade developers rely error messages check errors, can instead provide custom condition, can used unit testing instead:unit test :test wouldn’t fail even decided change exact message.","code":"\nabort_missing_package <- function(pkg) {\n msg <- glue::glue(\"Problem loading `{pkg}` package, which is missing and must be installed.\")\n\n abort(\"error_missing_package\",\n message = msg,\n pkg = pkg\n )\n}\n\ncheck_if_pkg_installed <- function(pkg) {\n if (!requireNamespace(pkg, quietly = TRUE)) {\n abort_missing_package(pkg)\n }\n\n TRUE\n}\n\ncheck_if_pkg_installed(\"xyz123\")\n#> Error in `abort_missing_package()`:\n#> ! Problem loading `xyz123` package, which is missing and must be installed.\ncheck_if_pkg_installed(\"dplyr\")\n#> [1] TRUE\nexpect_error(\n check_if_pkg_installed(\"xyz123\"),\n \"Problem loading `xyz123` package, which is missing and must be installed.\"\n)\ne <- catch_cnd(check_if_pkg_installed(\"xyz123\"))\n\ninherits(e, \"error_missing_package\")\n#> [1] TRUE\nexpect_s3_class(e, \"error_missing_package\")"},{"path":"conditions.html","id":"applications-exercises-8.6.6","chapter":"8 Conditions","heading":"8.4 Applications (Exercises 8.6.6)","text":"Q1. Create suppressConditions() works like suppressMessages() suppressWarnings() suppresses everything. Think carefully handle errors.A1. create desired suppressConditions(), just need create equivalent suppressWarnings() suppressMessages() errors. suppress error message, can handle errors within tryCatch() return error object invisibly:Let’s try see works expected:condition messages now suppressed, note assign error object variable, can still extract useful information debugging:Q2. Compare following two implementations message2error(). main advantage withCallingHandlers() scenario? (Hint: look carefully traceback.)A2. withCallingHandlers(), condition handler called signaling function , , therefore, provides detailed call stack.tryCatch(), signalling function terminates condition raised, doesn’t provide detailed call stack.Q3. modify catch_cnds() definition wanted recreate original intermingling warnings messages?A3. Actually, won’t modify anything function defined chapter, since supports box.nothing additional here5! πŸ˜…Q4. catching interrupts dangerous? Run code find .A4. function catches interrupt way stop bottles_of_beer(), way usually stop using interrupt!RStudio IDE, can snap loop terminating R session.shows catching interrupt dangerous can result poor user experience.","code":"\nsuppressErrors <- function(expr) {\n tryCatch(\n error = function(cnd) invisible(cnd),\n expr\n )\n}\n\nsuppressConditions <- function(expr) {\n suppressErrors(suppressWarnings(suppressMessages(expr)))\n}\nsuppressConditions(1)\n#> [1] 1\n\nsuppressConditions({\n message(\"I'm messaging you\")\n warning(\"I'm warning you\")\n})\n\nsuppressConditions({\n stop(\"I'm stopping this\")\n})\ne <- suppressConditions({\n stop(\"I'm stopping this\")\n})\n\ne\n#> \nmessage2error <- function(code) {\n withCallingHandlers(code, message = function(e) stop(e))\n}\nmessage2error <- function(code) {\n tryCatch(code, message = function(e) stop(e))\n}\nmessage2error1 <- function(code) {\n withCallingHandlers(code, message = function(e) stop(\"error\"))\n}\n\nmessage2error1({\n 1\n message(\"hidden error\")\n NULL\n})\n#> Error in (function (e) : error\n\ntraceback()\n#> 9: stop(\"error\") at #2\n#> 8: (function (e)\n#> stop(\"error\"))(list(message = \"hidden error\\n\",\n#> call = message(\"hidden error\")))\n#> 7: signalCondition(cond)\n#> 6: doWithOneRestart(return(expr), restart)\n#> 5: withOneRestart(expr, restarts[[1L]])\n#> 4: withRestarts({\n#> signalCondition(cond)\n#> defaultHandler(cond)\n#> }, muffleMessage = function() NULL)\n#> 3: message(\"hidden error\") at #1\n#> 2: withCallingHandlers(code,\n#> message = function(e) stop(\"error\")) at #2\n#> 1: message2error1({\n#> 1\n#> message(\"hidden error\")\n#> NULL\n#> })\nmessage2error2 <- function(code) {\n tryCatch(code, message = function(e) (stop(\"error\")))\n}\n\nmessage2error2({\n 1\n stop(\"hidden error\")\n NULL\n})\n#> Error in value[[3L]](cond) : error\n\ntraceback()\n#> 6: stop(\"error\") at #2\n#> 5: value[[3L]](cond)\n#> 4: tryCatchOne(expr, names, parentenv, handlers[[1L]])\n#> 3: tryCatchList(expr, classes, parentenv, handlers)\n#> 2: tryCatch(code, message = function(e) (stop(\"error\"))) at #2\n#> 1: message2error2({\n#> 1\n#> message(\"hidden error\")\n#> NULL\n#> })\ncatch_cnds <- function(expr) {\n conds <- list()\n add_cond <- function(cnd) {\n conds <<- append(conds, list(cnd))\n cnd_muffle(cnd)\n }\n\n withCallingHandlers(\n message = add_cond,\n warning = add_cond,\n expr\n )\n\n conds\n}\n\ncatch_cnds({\n inform(\"a\")\n warn(\"b\")\n inform(\"c\")\n})\n#> [[1]]\n#> \n#> Message:\n#> a\n#> \n#> [[2]]\n#> \n#> Warning:\n#> b\n#> \n#> [[3]]\n#> \n#> Message:\n#> c\nbottles_of_beer <- function(i = 99) {\n message(\n \"There are \", i, \" bottles of beer on the wall, \",\n i, \" bottles of beer.\"\n )\n while (i > 0) {\n tryCatch(\n Sys.sleep(1),\n interrupt = function(err) {\n i <<- i - 1\n if (i > 0) {\n message(\n \"Take one down, pass it around, \", i,\n \" bottle\", if (i > 1) \"s\", \" of beer on the wall.\"\n )\n }\n }\n )\n }\n message(\n \"No more bottles of beer on the wall, \",\n \"no more bottles of beer.\"\n )\n}\nbottles_of_beer()\n#> There are 99 bottles of beer on the wall, 99 bottles of beer.\n#> Take one down, pass it around, 98 bottles of beer on the wall.\n#> Take one down, pass it around, 97 bottles of beer on the wall.\n#> Take one down, pass it around, 96 bottles of beer on the wall.\n#> Take one down, pass it around, 95 bottles of beer on the wall.\n#> Take one down, pass it around, 94 bottles of beer on the wall.\n#> Take one down, pass it around, 93 bottles of beer on the wall.\n#> Take one down, pass it around, 92 bottles of beer on the wall.\n#> Take one down, pass it around, 91 bottles of beer on the wall.\n#> ..."},{"path":"conditions.html","id":"session-information-6","chapter":"8 Conditions","heading":"8.5 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> brio 1.1.3 2021-11-30 [1] CRAN (R 4.2.0)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)\n#> desc 1.4.2 2022-09-08 [1] CRAN (R 4.2.1)\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)\n#> pkgload 1.3.1 2022-10-28 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rprojroot 2.0.3 2022-04-02 [1] CRAN (R 4.2.0)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> testthat * 3.1.5 2022-10-08 [1] CRAN (R 4.2.1)\n#> tibble 3.1.8.9002 2022-10-16 [1] local\n#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"functionals.html","id":"functionals","chapter":"9 Functionals","heading":"9 Functionals","text":"Attaching needed libraries:","code":"\nlibrary(purrr, warn.conflicts = FALSE)"},{"path":"functionals.html","id":"my-first-functional-map-exercises-9.2.6","chapter":"9 Functionals","heading":"9.1 My first functional: map() (Exercises 9.2.6)","text":"Q1. Use as_mapper() explore purrr generates anonymous functions integer, character, list helpers. helper allows extract attributes? Read documentation find .A1. Let’s handle two parts question separately.as_mapper() purrr-generated anonymous functions:Looking experimentation map() as_mapper(), can see , depending type input, as_mapper() creates extractor function using pluck().can extract attributes using purrr::attr_getter():Q2. map(1:3, ~ runif(2)) useful pattern generating random numbers, map(1:3, runif(2)) . ? Can explain returns result ?A2. shown as_mapper() outputs , second call appropriate generating random numbers translates pluck() function indices plucking taken randomly generated numbers, valid accessors get NULLs return.Q3. Use appropriate map() function :A3. Using appropriate map() function :Compute standard deviation every column numeric data frame:Compute standard deviation every numeric column mixed data frame:Compute number levels every factor data frame:Q4. following code simulates performance t-test non-normal data. Extract p-value test, visualise.A4.Extract p-value test:Visualise extracted p-values:Q5. following code uses map nested inside another map apply function every element nested list. fail, need make work?A5. function fails call effectively evaluates following:triple() one parameter (x), execution fails.fixed version:Q6. Use map() fit linear models mtcars dataset using formulas stored list:A6. Fitting linear models mtcars dataset using provided formulas:Q7. Fit model mpg ~ disp bootstrap replicates mtcars list , extract \\(R^2\\) model fit (Hint: can compute \\(R^2\\) summary().)A7. can done using map_dbl():","code":"\n# mapping by position -----------------------\n\nx <- list(1, list(2, 3, list(1, 2)))\n\nmap(x, 1)\n#> [[1]]\n#> [1] 1\n#> \n#> [[2]]\n#> [1] 2\nas_mapper(1)\n#> function (x, ...) \n#> pluck(x, 1, .default = NULL)\n#> \n\nmap(x, list(2, 1))\n#> [[1]]\n#> NULL\n#> \n#> [[2]]\n#> [1] 3\nas_mapper(list(2, 1))\n#> function (x, ...) \n#> pluck(x, 2, 1, .default = NULL)\n#> \n\n# mapping by name -----------------------\n\ny <- list(\n list(m = \"a\", list(1, m = \"mo\")),\n list(n = \"b\", list(2, n = \"no\"))\n)\n\nmap(y, \"m\")\n#> [[1]]\n#> [1] \"a\"\n#> \n#> [[2]]\n#> NULL\nas_mapper(\"m\")\n#> function (x, ...) \n#> pluck(x, \"m\", .default = NULL)\n#> \n\n# mixing position and name\nmap(y, list(2, \"m\"))\n#> [[1]]\n#> [1] \"mo\"\n#> \n#> [[2]]\n#> NULL\nas_mapper(list(2, \"m\"))\n#> function (x, ...) \n#> pluck(x, 2, \"m\", .default = NULL)\n#> \n\n# compact functions ----------------------------\n\nmap(y, ~ length(.x))\n#> [[1]]\n#> [1] 2\n#> \n#> [[2]]\n#> [1] 2\nas_mapper(~ length(.x))\n#> \n#> function (..., .x = ..1, .y = ..2, . = ..1) \n#> length(.x)\n#> attr(,\"class\")\n#> [1] \"rlang_lambda_function\" \"function\"\npluck(Titanic, attr_getter(\"class\"))\n#> [1] \"table\"\nmap(1:3, ~ runif(2))\n#> [[1]]\n#> [1] 0.2180892 0.9876342\n#> \n#> [[2]]\n#> [1] 0.3484619 0.3810470\n#> \n#> [[3]]\n#> [1] 0.02098596 0.74972687\nas_mapper(~ runif(2))\n#> \n#> function (..., .x = ..1, .y = ..2, . = ..1) \n#> runif(2)\n#> attr(,\"class\")\n#> [1] \"rlang_lambda_function\" \"function\"\n\nmap(1:3, runif(2))\n#> [[1]]\n#> NULL\n#> \n#> [[2]]\n#> NULL\n#> \n#> [[3]]\n#> NULL\nas_mapper(runif(2))\n#> function (x, ...) \n#> pluck(x, 0.597890264587477, 0.587997315218672, .default = NULL)\n#> a) Compute the standard deviation of every column in a numeric data frame.\n\na) Compute the standard deviation of every numeric column in a mixed data frame. (Hint: you'll need to do it in two steps.)\n\na) Compute the number of levels for every factor in a data frame.\nmap_dbl(mtcars, sd)\n#> mpg cyl disp hp drat \n#> 6.0269481 1.7859216 123.9386938 68.5628685 0.5346787 \n#> wt qsec vs am gear \n#> 0.9784574 1.7869432 0.5040161 0.4989909 0.7378041 \n#> carb \n#> 1.6152000\nkeep(iris, is.numeric) %>%\n map_dbl(sd)\n#> Sepal.Length Sepal.Width Petal.Length Petal.Width \n#> 0.8280661 0.4358663 1.7652982 0.7622377\nmodify_if(dplyr::starwars, is.character, as.factor) %>%\n keep(is.factor) %>%\n map_int(~ length(levels(.)))\n#> name hair_color skin_color eye_color sex \n#> 87 12 31 15 4 \n#> gender homeworld species \n#> 2 48 37\ntrials <- map(1:100, ~ t.test(rpois(10, 10), rpois(7, 10)))\ntrials <- map(1:100, ~ t.test(rpois(10, 10), rpois(7, 10)))\n\n(p <- map_dbl(trials, \"p.value\"))\n#> [1] 0.81695628 0.53177360 0.94750819 0.41026769 0.34655294\n#> [6] 0.05300287 0.56479901 0.85936864 0.77517391 0.64321161\n#> [11] 0.84462914 0.54144946 0.63070476 0.20325827 0.39824435\n#> [16] 0.67052432 0.39932663 0.44437632 0.51645941 0.96578745\n#> [21] 0.70219557 0.69931716 0.23946786 0.55100566 0.76028958\n#> [26] 0.38105366 0.64544126 0.15379307 0.86945196 0.09965658\n#> [31] 0.96425489 0.54239108 0.38985789 0.59019282 0.96247907\n#> [36] 0.54997487 0.66111391 0.30961551 0.10897334 0.55049635\n#> [41] 0.93882405 0.14836866 0.44307287 0.61583610 0.37284284\n#> [46] 0.38559622 0.42935767 0.26059293 0.07831619 0.93768396\n#> [51] 0.48459268 0.73571291 0.30288560 0.68521609 0.06374636\n#> [56] 0.11007808 0.98758443 0.17831882 0.94471538 0.19711729\n#> [61] 0.02094185 0.12370745 0.23247837 0.93842382 0.19160550\n#> [66] 0.49005550 0.98146240 0.09034183 0.94912080 0.55857523\n#> [71] 0.24692070 0.63658206 0.14290966 0.10309770 0.89516449\n#> [76] 0.25660092 0.16943034 0.41199780 0.82721280 0.74017418\n#> [81] 0.43724631 0.55944024 0.93615100 0.68788872 0.01416627\n#> [86] 0.60120497 0.54125910 0.91581929 0.78949327 0.57887371\n#> [91] 0.83217542 0.90108906 0.97474727 0.99129282 0.54436155\n#> [96] 0.74159859 0.06534957 0.10834529 0.19737786 0.93750342\nplot(p)\n\nhist(p)\nx <- list(\n list(1, c(3, 9)),\n list(c(3, 6), 7, c(4, 7, 6))\n)\n\ntriple <- function(x) x * 3\nmap(x, map, .f = triple)\n#> Error in .f(.x[[i]], ...): unused argument (function (.x, .f, ...) \n#> {\n#> .f <- as_mapper(.f, ...)\n#> .Call(map_impl, environment(), \".x\", \".f\", \"list\")\n#> })\nmap(.x = x, .f = ~ triple(x = .x, map))\nx <- list(\n list(1, c(3, 9)),\n list(c(3, 6), 7, c(4, 7, 6))\n)\n\ntriple <- function(x) x * 3\nmap(x, .f = ~ map(.x, ~ triple(.x)))\n#> [[1]]\n#> [[1]][[1]]\n#> [1] 3\n#> \n#> [[1]][[2]]\n#> [1] 9 27\n#> \n#> \n#> [[2]]\n#> [[2]][[1]]\n#> [1] 9 18\n#> \n#> [[2]][[2]]\n#> [1] 21\n#> \n#> [[2]][[3]]\n#> [1] 12 21 18\nformulas <- list(\n mpg ~ disp,\n mpg ~ I(1 / disp),\n mpg ~ disp + wt,\n mpg ~ I(1 / disp) + wt\n)\nformulas <- list(\n mpg ~ disp,\n mpg ~ I(1 / disp),\n mpg ~ disp + wt,\n mpg ~ I(1 / disp) + wt\n)\n\nmap(formulas, ~ lm(formula = ., data = mtcars))\n#> [[1]]\n#> \n#> Call:\n#> lm(formula = ., data = mtcars)\n#> \n#> Coefficients:\n#> (Intercept) disp \n#> 29.59985 -0.04122 \n#> \n#> \n#> [[2]]\n#> \n#> Call:\n#> lm(formula = ., data = mtcars)\n#> \n#> Coefficients:\n#> (Intercept) I(1/disp) \n#> 10.75 1557.67 \n#> \n#> \n#> [[3]]\n#> \n#> Call:\n#> lm(formula = ., data = mtcars)\n#> \n#> Coefficients:\n#> (Intercept) disp wt \n#> 34.96055 -0.01772 -3.35083 \n#> \n#> \n#> [[4]]\n#> \n#> Call:\n#> lm(formula = ., data = mtcars)\n#> \n#> Coefficients:\n#> (Intercept) I(1/disp) wt \n#> 19.024 1142.560 -1.798\nbootstrap <- function(df) {\n df[sample(nrow(df), replace = TRUE), , drop = FALSE]\n}\n\nbootstraps <- map(1:10, ~ bootstrap(mtcars))\nbootstrap <- function(df) {\n df[sample(nrow(df), replace = TRUE), , drop = FALSE]\n}\n\nbootstraps <- map(1:10, ~ bootstrap(mtcars))\n\nbootstraps %>%\n map(~ lm(mpg ~ disp, data = .x)) %>%\n map(summary) %>%\n map_dbl(\"r.squared\")\n#> [1] 0.7864562 0.8110818 0.7956331 0.7632399 0.7967824\n#> [6] 0.7364226 0.7203027 0.6653252 0.7732780 0.6753329"},{"path":"functionals.html","id":"map-variants-exercises-9.4.6","chapter":"9 Functionals","heading":"9.2 Map variants (Exercises 9.4.6)","text":"Q1. Explain results modify(mtcars, 1).A1. modify() returns object type input. Since input data frame certain dimensions .f = 1 translates plucking first element column, returns data frame dimensions plucked element recycled across rows.Q2. Rewrite following code use iwalk() instead walk2(). advantages disadvantages?A2. Let’s first rewrite provided code using iwalk():advantage using iwalk() need now deal single variable (cyls) instead two (cyls paths).disadvantage code difficult reason :\nwalk2(), ’s explicit .x (= cyls) .y (= paths) correspond , iwalk() (.e., .x = cyls .y = names(cyls)) .y argument β€œinvisible”.Q3. Explain following code transforms data frame using functions stored list.Compare contrast map2() approach map() approach:A3. map2() supplies functions stored trans anonymous functions via placeholder f, names columns specified mtcars[nm] supplied var argument anonymous function. Note function iterating indices vectors transformations column names.map() approach, function iterating indices vectors column names.latter approach can’t afford passing arguments placeholders anonymous function.Q4. write.csv() return, .e.Β happens use map2() instead walk2()?A4. use map2(), work, print NULLs console every list element.","code":"\nhead(modify(mtcars, 1))\n#> mpg cyl disp hp drat wt qsec vs am\n#> Mazda RX4 21 6 160 110 3.9 2.62 16.46 0 1\n#> Mazda RX4 Wag 21 6 160 110 3.9 2.62 16.46 0 1\n#> Datsun 710 21 6 160 110 3.9 2.62 16.46 0 1\n#> Hornet 4 Drive 21 6 160 110 3.9 2.62 16.46 0 1\n#> Hornet Sportabout 21 6 160 110 3.9 2.62 16.46 0 1\n#> Valiant 21 6 160 110 3.9 2.62 16.46 0 1\n#> gear carb\n#> Mazda RX4 4 4\n#> Mazda RX4 Wag 4 4\n#> Datsun 710 4 4\n#> Hornet 4 Drive 4 4\n#> Hornet Sportabout 4 4\n#> Valiant 4 4\ncyls <- split(mtcars, mtcars$cyl)\npaths <- file.path(temp, paste0(\"cyl-\", names(cyls), \".csv\"))\nwalk2(cyls, paths, write.csv)\ncyls <- split(mtcars, mtcars$cyl)\nnames(cyls) <- file.path(temp, paste0(\"cyl-\", names(cyls), \".csv\"))\niwalk(cyls, ~ write.csv(.x, .y))\ntrans <- list(\n disp = function(x) x * 0.0163871,\n am = function(x) factor(x, labels = c(\"auto\", \"manual\"))\n)\n\nnm <- names(trans)\nmtcars[nm] <- map2(trans, mtcars[nm], function(f, var) f(var))\nmtcars[nm] <- map(nm, ~ trans[[.x]](mtcars[[.x]]))\ntrans <- list(\n disp = function(x) x * 0.0163871,\n am = function(x) factor(x, labels = c(\"auto\", \"manual\"))\n)\n\nnm <- names(trans)\nmtcars[nm] <- map2(trans, mtcars[nm], function(f, var) f(var))\nmtcars[nm] <- map(nm, ~ trans[[.x]](mtcars[[.x]]))\nwithr::with_tempdir(\n code = {\n ls <- split(mtcars, mtcars$cyl)\n nm <- names(ls)\n map2(ls, nm, write.csv)\n }\n)\n#> $`4`\n#> NULL\n#> \n#> $`6`\n#> NULL\n#> \n#> $`8`\n#> NULL"},{"path":"functionals.html","id":"predicate-functionals-exercises-9.6.3","chapter":"9 Functionals","heading":"9.3 Predicate functionals (Exercises 9.6.3)","text":"Q1. isn’t .na() predicate function? base R function closest predicate version .na()?A1. mentioned docs:predicate function returns single TRUE FALSE..na() function return logical scalar, instead returns vector thus isn’t predicate function.closest equivalent predicate function base-R anyNA() function.Q2. simple_reduce() problem x length 0 length 1. Describe source problem might go fixing .A2. supplied function struggles inputs length 0 1 function tries subscript --bound values.problem can solved adding init argument, supplies default initial value:Let’s try :Depending function, can provide different init argument:Q3. Implement span() function Haskell: given list x predicate function f, span(x, f) returns location longest sequential run elements predicate true. (Hint: might find rle() helpful.)A3. Implementation span():Testing :Testing twice:Q4. Implement arg_max(). take function vector inputs, return elements input function returns highest value. example, arg_max(-10:5, function(x) x ^ 2) return -10. arg_max(-5:5, function(x) x ^ 2) return c(-5, 5). Also implement matching arg_min() function.A4. implementations specified functions:Implementing arg_max()Implementing arg_min()Q5. function scales vector falls range [0, 1]. apply every column data frame? apply every numeric column data frame?A5. use purrr package apply function. Key thing keep mind data frame list atomic vectors equal length.Applying function every column data frame: use anscombe example since numeric columns.Applying function every numeric column data frame: use iris example since columns numeric type.","code":"\n# contrast the following behavior of predicate functions\nis.character(c(\"x\", 2))\n#> [1] TRUE\nis.null(c(3, NULL))\n#> [1] FALSE\n\n# with this behavior\nis.na(c(NA, 1))\n#> [1] TRUE FALSE\nanyNA(c(NA, 1))\n#> [1] TRUE\nsimple_reduce <- function(x, f) {\n out <- x[[1]]\n for (i in seq(2, length(x))) {\n out <- f(out, x[[i]])\n }\n out\n}\nsimple_reduce(numeric(), sum)\n#> Error in x[[1]]: subscript out of bounds\nsimple_reduce(1, sum)\n#> Error in x[[i]]: subscript out of bounds\nsimple_reduce(1:3, sum)\n#> [1] 6\nsimple_reduce2 <- function(x, f, init = 0) {\n # initializer will become the first value\n if (length(x) == 0L) {\n return(init)\n }\n\n if (length(x) == 1L) {\n return(x[[1L]])\n }\n\n out <- x[[1]]\n\n for (i in seq(2, length(x))) {\n out <- f(out, x[[i]])\n }\n\n out\n}\nsimple_reduce2(numeric(), sum)\n#> [1] 0\nsimple_reduce2(1, sum)\n#> [1] 1\nsimple_reduce2(1:3, sum)\n#> [1] 6\nsimple_reduce2(numeric(), `*`, init = 1)\n#> [1] 1\nsimple_reduce2(1, `*`, init = 1)\n#> [1] 1\nsimple_reduce2(1:3, `*`, init = 1)\n#> [1] 6\nspan <- function(x, f) {\n running_lengths <- purrr::map_lgl(x, ~ f(.x)) %>% rle()\n\n df <- dplyr::tibble(\n \"lengths\" = running_lengths$lengths,\n \"values\" = running_lengths$values\n ) %>%\n dplyr::mutate(rowid = dplyr::row_number()) %>%\n dplyr::filter(values)\n\n # no sequence where condition is `TRUE`\n if (nrow(df) == 0L) {\n return(integer())\n }\n\n # only single sequence where condition is `TRUE`\n if (nrow(df) == 1L) {\n return((df$rowid):(df$lengths - 1 + df$rowid))\n }\n\n # multiple sequences where condition is `TRUE`; select max one\n if (nrow(df) > 1L) {\n df <- dplyr::filter(df, lengths == max(lengths))\n return((df$rowid):(df$lengths - 1 + df$rowid))\n }\n}\nspan(c(0, 0, 0, 0, 0), is.na)\n#> integer(0)\nspan(c(NA, 0, NA, NA, NA), is.na)\n#> [1] 3 4 5\nspan(c(NA, 0, 0, 0, 0), is.na)\n#> [1] 1\nspan(c(NA, NA, 0, 0, 0), is.na)\n#> [1] 1 2\nspan(c(3, 1, 2, 4, 5, 6), function(x) x > 3)\n#> [1] 2 3 4\nspan(c(3, 1, 2, 4, 5, 6), function(x) x > 9)\n#> integer(0)\nspan(c(3, 1, 2, 4, 5, 6), function(x) x == 3)\n#> [1] 1\nspan(c(3, 1, 2, 4, 5, 6), function(x) x %in% c(2, 4))\n#> [1] 2 3\narg_max <- function(.x, .f) {\n df <- dplyr::tibble(\n original = .x,\n transformed = purrr::map_dbl(.x, .f)\n )\n\n dplyr::filter(df, transformed == max(transformed))[[\"original\"]]\n}\n\narg_max(-10:5, function(x) x^2)\n#> [1] -10\narg_max(-5:5, function(x) x^2)\n#> [1] -5 5\narg_min <- function(.x, .f) {\n df <- dplyr::tibble(\n original = .x,\n transformed = purrr::map_dbl(.x, .f)\n )\n\n dplyr::filter(df, transformed == min(transformed))[[\"original\"]]\n}\n\narg_min(-10:5, function(x) x^2)\n#> [1] 0\narg_min(-5:5, function(x) x^2)\n#> [1] 0\nscale01 <- function(x) {\n rng <- range(x, na.rm = TRUE)\n (x - rng[1]) / (rng[2] - rng[1])\n}\npurrr::map_df(head(anscombe), .f = scale01)\n#> # A tibble: 6 Γ— 8\n#> x1 x2 x3 x4 y1 y2 y3 y4\n#> \n#> 1 0.333 0.333 0.333 NaN 0.362 0.897 0.116 0.266\n#> 2 0 0 0 NaN 0 0.0345 0 0 \n#> 3 0.833 0.833 0.833 NaN 0.209 0.552 1 0.633\n#> 4 0.167 0.167 0.167 NaN 0.618 0.578 0.0570 1 \n#> 5 0.5 0.5 0.5 NaN 0.458 1 0.174 0.880\n#> 6 1 1 1 NaN 1 0 0.347 0.416\npurrr::modify_if(head(iris), .p = is.numeric, .f = scale01)\n#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species\n#> 1 0.625 0.5555556 0.25 0 setosa\n#> 2 0.375 0.0000000 0.25 0 setosa\n#> 3 0.125 0.2222222 0.00 0 setosa\n#> 4 0.000 0.1111111 0.50 0 setosa\n#> 5 0.500 0.6666667 0.25 0 setosa\n#> 6 1.000 1.0000000 1.00 1 setosa"},{"path":"functionals.html","id":"base-functionals-exercises-9.7.3","chapter":"9 Functionals","heading":"9.4 Base functionals (Exercises 9.7.3)","text":"Q1. apply() arrange output? Read documentation perform experiments.A1. Let’s prepare array apply function different margins:can seen, apply() returns outputs organised first margins operated , results.Q2. eapply() rapply() ? purrr equivalents?A2. Let’s consider one--one.eapply()mentioned documentation:eapply() applies FUN named values environment returns results list.example:purrr doesn’t function iterate environments.rapply()rapply() recursive version lapply flexibility result structured (= β€œ..”).example:purrr something similar modify_depth().Q3. Challenge: read fixed point algorithm. Complete exercises using R.A3. mentioned suggested reading material:number \\(x\\) called fixed point function \\(f\\) \\(x\\) satisfies equation \\(f(x) = x\\). functions \\(f\\) can locate fixed point beginning initial guess applying \\(f\\) repeatedly, \\(f(x), f(f(x)), f(f(f(x))), ...\\) value change much. Using idea, can devise procedure fixed-point takes inputs function initial guess produces approximation fixed point function.Let’s first implement fixed-point algorithm:Let’s check works expected:solve one exercise reading material. Rest beyond scope solution manual.Show golden ratio \\(\\phi\\) fixed point transformation \\(x \\mapsto 1 + 1/x\\), use fact compute \\(\\phi\\) means fixed-point procedure.","code":"\n(m <- as.array(table(mtcars$cyl, mtcars$am, mtcars$vs)))\n#> , , = 0\n#> \n#> \n#> auto manual\n#> 4 0 1\n#> 6 0 3\n#> 8 12 2\n#> \n#> , , = 1\n#> \n#> \n#> auto manual\n#> 4 3 7\n#> 6 4 0\n#> 8 0 0\n\n# rows\napply(m, 1, function(x) x^2)\n#> \n#> 4 6 8\n#> [1,] 0 0 144\n#> [2,] 1 9 4\n#> [3,] 9 16 0\n#> [4,] 49 0 0\n\n# columns\napply(m, 2, function(x) x^2)\n#> \n#> auto manual\n#> [1,] 0 1\n#> [2,] 0 9\n#> [3,] 144 4\n#> [4,] 9 49\n#> [5,] 16 0\n#> [6,] 0 0\n\n# rows and columns\napply(m, c(1, 2), function(x) x^2)\n#> , , = auto\n#> \n#> \n#> 4 6 8\n#> 0 0 0 144\n#> 1 9 16 0\n#> \n#> , , = manual\n#> \n#> \n#> 4 6 8\n#> 0 1 9 4\n#> 1 49 0 0\nlibrary(rlang)\n#> \n#> Attaching package: 'rlang'\n#> The following objects are masked from 'package:purrr':\n#> \n#> %@%, as_function, flatten, flatten_chr,\n#> flatten_dbl, flatten_int, flatten_lgl,\n#> flatten_raw, invoke, splice\n#> The following object is masked from 'package:magrittr':\n#> \n#> set_names\n\ne <- env(\"x\" = 1, \"y\" = 2)\nrlang::env_print(e)\n#> \n#> Parent: \n#> Bindings:\n#> β€’ x: \n#> β€’ y: \n\neapply(e, as.character)\n#> $x\n#> [1] \"1\"\n#> \n#> $y\n#> [1] \"2\"\nX <- list(list(a = TRUE, b = list(c = c(4L, 3.2))), d = 9.0)\n\nrapply(X, as.character, classes = \"numeric\", how = \"replace\")\n#> [[1]]\n#> [[1]]$a\n#> [1] TRUE\n#> \n#> [[1]]$b\n#> [[1]]$b$c\n#> [1] \"4\" \"3.2\"\n#> \n#> \n#> \n#> $d\n#> [1] \"9\"\nX <- list(list(a = TRUE, b = list(c = c(4L, 3.2))), d = 9.0)\n\npurrr::modify_depth(X, .depth = 2L, .f = length)\n#> [[1]]\n#> [[1]]$a\n#> [1] 1\n#> \n#> [[1]]$b\n#> [1] 1\n#> \n#> \n#> $d\n#> [1] 1\nclose_enough <- function(x1, x2, tolerance = 0.001) {\n if (abs(x1 - x2) < tolerance) {\n return(TRUE)\n } else {\n return(FALSE)\n }\n}\n\nfind_fixed_point <- function(.f, .guess, tolerance = 0.001) {\n .next <- .f(.guess)\n is_close_enough <- close_enough(.next, .guess, tol = tolerance)\n\n if (is_close_enough) {\n return(.next)\n } else {\n find_fixed_point(.f, .next, tolerance)\n }\n}\nfind_fixed_point(cos, 1.0)\n#> [1] 0.7387603\n\n# cos(x) = x\ncos(find_fixed_point(cos, 1.0))\n#> [1] 0.7393039\ngolden_ratio_f <- function(x) 1 + (1 / x)\n\nfind_fixed_point(golden_ratio_f, 1.0)\n#> [1] 1.618182"},{"path":"functionals.html","id":"session-information-7","chapter":"9 Functionals","heading":"9.5 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0)\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)\n#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> tibble 3.1.8.9002 2022-10-16 [1] local\n#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"function-factories.html","id":"function-factories","chapter":"10 Function factories","heading":"10 Function factories","text":"Attaching needed libraries:","code":"\nlibrary(rlang, warn.conflicts = FALSE)\nlibrary(ggplot2, warn.conflicts = FALSE)"},{"path":"function-factories.html","id":"factory-fundamentals-exercises-10.2.6","chapter":"10 Function factories","heading":"10.1 Factory fundamentals (Exercises 10.2.6)","text":"Q1. definition force() simple:better force(x) instead just x?A1. Due lazy evaluation, argument function won’t evaluated value needed. sometimes may want eager evaluation, using force() makes intent clearer.Q2. Base R contains two function factories, approxfun() ecdf(). Read documentation experiment figure functions return.A2. two function factories-approxfun()function factory returns function performing linear (constant) interpolation.ecdf()function factory computes empirical cumulative distribution function.Q3. Create function pick() takes index, , argument returns function argument x subsets x .A3. write desired function, just need make sure argument eagerly evaluated.Testing specified test cases:Q4. Create function creates functions compute ithcentral moment numeric vector. can test running following code:A4. following function satisfied specified requirements:Testing specified test cases:Q5. happens don’t use closure? Make predictions, verify code .A5. case closures used context, counts stored global variable, can modified processes even deleted.Q6. happens use <- instead <<-? Make predictions, verify code .A6. case, function always return 1.","code":"\nforce\n#> function (x) \n#> x\n#> \n#> \nx <- 1:10\ny <- rnorm(10)\nf <- approxfun(x, y)\nf\n#> function (v) \n#> .approxfun(x, y, v, method, yleft, yright, f, na.rm)\n#> \n#> \nf(x)\n#> [1] -0.7786629 -0.3894764 -2.0337983 -0.9823731 0.2478901\n#> [6] -2.1038646 -0.3814180 2.0749198 1.0271384 0.4730142\ncurve(f(x), 0, 11)\nx <- rnorm(12)\nf <- ecdf(x)\nf\n#> Empirical CDF \n#> Call: ecdf(x)\n#> x[1:12] = -1.8793, -1.3221, -1.2392, ..., 1.1604, 1.7956\nf(seq(-2, 2, by = 0.1))\n#> [1] 0.00000000 0.00000000 0.08333333 0.08333333 0.08333333\n#> [6] 0.08333333 0.08333333 0.16666667 0.25000000 0.25000000\n#> [11] 0.33333333 0.33333333 0.33333333 0.41666667 0.41666667\n#> [16] 0.41666667 0.41666667 0.50000000 0.58333333 0.58333333\n#> [21] 0.66666667 0.75000000 0.75000000 0.75000000 0.75000000\n#> [26] 0.75000000 0.75000000 0.75000000 0.75000000 0.83333333\n#> [31] 0.83333333 0.83333333 0.91666667 0.91666667 0.91666667\n#> [36] 0.91666667 0.91666667 0.91666667 1.00000000 1.00000000\n#> [41] 1.00000000\npick(1)(x)\n# should be equivalent to\nx[[1]]\n\nlapply(mtcars, pick(5))\n# should be equivalent to\nlapply(mtcars, function(x) x[[5]])\npick <- function(i) {\n force(i)\n function(x) x[[i]]\n}\nx <- list(\"a\", \"b\", \"c\")\nidentical(x[[1]], pick(1)(x))\n#> [1] TRUE\n\nidentical(\n lapply(mtcars, pick(5)),\n lapply(mtcars, function(x) x[[5]])\n)\n#> [1] TRUE\nm1 <- moment(1)\nm2 <- moment(2)\nx <- runif(100)\nstopifnot(all.equal(m1(x), 0))\nstopifnot(all.equal(m2(x), var(x) * 99 / 100))\nmoment <- function(k) {\n force(k)\n\n function(x) (sum((x - mean(x))^k)) / length(x)\n}\nm1 <- moment(1)\nm2 <- moment(2)\nx <- runif(100)\n\nstopifnot(all.equal(m1(x), 0))\nstopifnot(all.equal(m2(x), var(x) * 99 / 100))\ni <- 0\nnew_counter2 <- function() {\n i <<- i + 1\n i\n}\nnew_counter2()\n#> [1] 1\n\nnew_counter2()\n#> [1] 2\n\nnew_counter2()\n#> [1] 3\n\ni <- 20\nnew_counter2()\n#> [1] 21\nnew_counter3 <- function() {\n i <- 0\n function() {\n i <- i + 1\n i\n }\n}\nnew_counter3()\n#> function() {\n#> i <- i + 1\n#> i\n#> }\n#> \n\nnew_counter3()\n#> function() {\n#> i <- i + 1\n#> i\n#> }\n#> \n#> "},{"path":"function-factories.html","id":"graphical-factories-exercises-10.3.4","chapter":"10 Function factories","heading":"10.2 Graphical factories (Exercises 10.3.4)","text":"Q1. Compare contrast ggplot2::label_bquote() scales::number_format().A1. compare contrast, let’s first look source code functions:ggplot2::label_bquote()scales::number_format()functions return formatting functions used style facets labels labels desired format ggplot2 plots.example, using plotmath expression facet label:display axes labels desired format:ggplot2::label_bquote() adds additional class returned function.scales::number_format() function simple pass-method forces evaluation parameters passes underlying scales::number() function.","code":"\nggplot2::label_bquote\n#> function (rows = NULL, cols = NULL, default) \n#> {\n#> cols_quoted <- substitute(cols)\n#> rows_quoted <- substitute(rows)\n#> call_env <- env_parent()\n#> fun <- function(labels) {\n#> quoted <- resolve_labeller(rows_quoted, cols_quoted, \n#> labels)\n#> if (is.null(quoted)) {\n#> return(label_value(labels))\n#> }\n#> evaluate <- function(...) {\n#> params <- list(...)\n#> params <- as_environment(params, call_env)\n#> eval(substitute(bquote(expr, params), list(expr = quoted)))\n#> }\n#> list(inject(mapply(evaluate, !!!labels, SIMPLIFY = FALSE)))\n#> }\n#> structure(fun, class = \"labeller\")\n#> }\n#> \n#> \nscales::number_format\n#> function (accuracy = NULL, scale = 1, prefix = \"\", suffix = \"\", \n#> big.mark = \" \", decimal.mark = \".\", style_positive = c(\"none\", \n#> \"plus\"), style_negative = c(\"hyphen\", \"minus\", \"parens\"), \n#> scale_cut = NULL, trim = TRUE, ...) \n#> {\n#> force_all(accuracy, scale, prefix, suffix, big.mark, decimal.mark, \n#> style_positive, style_negative, scale_cut, trim, ...)\n#> function(x) {\n#> number(x, accuracy = accuracy, scale = scale, prefix = prefix, \n#> suffix = suffix, big.mark = big.mark, decimal.mark = decimal.mark, \n#> style_positive = style_positive, style_negative = style_negative, \n#> scale_cut = scale_cut, trim = trim, ...)\n#> }\n#> }\n#> \n#> \nlibrary(ggplot2)\n\np <- ggplot(mtcars, aes(wt, mpg)) +\n geom_point()\np + facet_grid(. ~ vs, labeller = label_bquote(cols = alpha^.(vs)))\nlibrary(scales)\n\nggplot(mtcars, aes(wt, mpg)) +\n geom_point() +\n scale_y_continuous(labels = number_format(accuracy = 0.01, decimal.mark = \",\"))"},{"path":"function-factories.html","id":"statistical-factories-exercises-10.4.4","chapter":"10 Function factories","heading":"10.3 Statistical factories (Exercises 10.4.4)","text":"Q1. boot_model(), don’t need force evaluation df model?A1. don’t need force evaluation df model arguments automatically evaluated lm():Q2. might formulate Box-Cox transformation like ?A2. see formulate transformation like , can compare one mentioned book:Let’s look one example :can seen:boxcox2(), can vary x value lambda, whilein boxcox3(), can vary lambda vector.Thus, boxcox3() can handy exploring different transformations across inputs.Q3. don’t need worry boot_permute() stores copy data inside function generates?A3. look source code generated function factory, notice exact data frame (mtcars) referenced:don’t need worry copy made df function environment points memory address data frame. can confirm comparing memory addresses:can also check values bindings entered function factory:Q4. much time ll_poisson2() save compared ll_poisson1()? Use bench::mark() see much faster optimisation occurs. changing length x change results?A4. Let’s first compare performance functions example book:can seen, second version much faster first version.can also vary length vector confirm across wide range vector lengths, performance advantage observed.","code":"\nboot_model <- function(df, formula) {\n mod <- lm(formula, data = df)\n fitted <- unname(fitted(mod))\n resid <- unname(resid(mod))\n rm(mod)\n\n function() {\n fitted + sample(resid)\n }\n}\nboxcox3 <- function(x) {\n function(lambda) {\n if (lambda == 0) {\n log(x)\n } else {\n (x^lambda - 1) / lambda\n }\n }\n}\nboxcox2 <- function(lambda) {\n if (lambda == 0) {\n function(x) log(x)\n } else {\n function(x) (x^lambda - 1) / lambda\n }\n}\nboxcox2(1)\n#> function(x) (x^lambda - 1) / lambda\n#> \n\nboxcox3(mtcars$wt)\n#> function(lambda) {\n#> if (lambda == 0) {\n#> log(x)\n#> } else {\n#> (x^lambda - 1) / lambda\n#> }\n#> }\n#> \nboot_permute <- function(df, var) {\n n <- nrow(df)\n force(var)\n\n function() {\n col <- df[[var]]\n col[sample(n, replace = TRUE)]\n }\n}\n\nboot_permute(mtcars, \"mpg\")\n#> function() {\n#> col <- df[[var]]\n#> col[sample(n, replace = TRUE)]\n#> }\n#> \nboot_permute_env <- rlang::fn_env(boot_permute(mtcars, \"mpg\"))\nrlang::env_print(boot_permute_env)\n#> \n#> Parent: \n#> Bindings:\n#> β€’ n: \n#> β€’ df: \n#> β€’ var: \n\nidentical(\n lobstr::obj_addr(boot_permute_env$df),\n lobstr::obj_addr(mtcars)\n)\n#> [1] TRUE\nidentical(boot_permute_env$df, mtcars)\n#> [1] TRUE\nidentical(boot_permute_env$var, \"mpg\")\n#> [1] TRUE\nll_poisson1 <- function(x) {\n n <- length(x)\n\n function(lambda) {\n log(lambda) * sum(x) - n * lambda - sum(lfactorial(x))\n }\n}\n\nll_poisson2 <- function(x) {\n n <- length(x)\n sum_x <- sum(x)\n c <- sum(lfactorial(x))\n\n function(lambda) {\n log(lambda) * sum_x - n * lambda - c\n }\n}\n\nx1 <- c(41, 30, 31, 38, 29, 24, 30, 29, 31, 38)\n\nbench::mark(\n \"LL1\" = optimise(ll_poisson1(x1), c(0, 100), maximum = TRUE),\n \"LL2\" = optimise(ll_poisson2(x1), c(0, 100), maximum = TRUE)\n)\n#> # A tibble: 2 Γ— 6\n#> expression min median `itr/sec` mem_alloc `gc/sec`\n#> \n#> 1 LL1 15.79Β΅s 34.8Β΅s 20353. 12.8KB 30.5\n#> 2 LL2 8.49Β΅s 10.5Β΅s 56011. 0B 39.2\ngenerate_ll_benches <- function(n) {\n x_vec <- sample.int(n, n)\n\n bench::mark(\n \"LL1\" = optimise(ll_poisson1(x_vec), c(0, 100), maximum = TRUE),\n \"LL2\" = optimise(ll_poisson2(x_vec), c(0, 100), maximum = TRUE)\n )[1:4] %>%\n dplyr::mutate(length = n, .before = expression)\n}\n\n(df_bench <- purrr::map_dfr(\n .x = c(10, 20, 50, 100, 1000),\n .f = ~ generate_ll_benches(n = .x)\n))\n#> # A tibble: 10 Γ— 5\n#> length expression min median `itr/sec`\n#> \n#> 1 10 LL1 25.1Β΅s 38.2Β΅s 15958.\n#> 2 10 LL2 10.4Β΅s 14.2Β΅s 46876.\n#> 3 20 LL1 27.4Β΅s 37.9Β΅s 19378.\n#> 4 20 LL2 10.1Β΅s 13.8Β΅s 52872.\n#> 5 50 LL1 32.8Β΅s 46.2Β΅s 9780.\n#> 6 50 LL2 10Β΅s 15Β΅s 46956.\n#> 7 100 LL1 45.6Β΅s 62.6Β΅s 11691.\n#> 8 100 LL2 11Β΅s 14.8Β΅s 44373.\n#> 9 1000 LL1 633.4Β΅s 924.7Β΅s 823.\n#> 10 1000 LL2 36.6Β΅s 50.2Β΅s 14557.\n\nggplot(\n df_bench,\n aes(\n x = as.numeric(length),\n y = median,\n group = as.character(expression),\n color = as.character(expression)\n )\n) +\n geom_point() +\n geom_line() +\n labs(\n x = \"Vector length\",\n y = \"Median Execution Time\",\n colour = \"Function used\"\n )"},{"path":"function-factories.html","id":"function-factories-functionals-exercises-10.5.1","chapter":"10 Function factories","heading":"10.4 Function factories + functionals (Exercises 10.5.1)","text":"Q1. following commands equivalent (x, f(z))?A1. depends whether () used data frame list.Q2. Compare contrast effects env_bind() vs.Β attach() following code.A2. Let’s compare contrast effects env_bind() vs.Β attach().attach() adds funs search path. Since functions names functions {base} package, attached names mask ones {base} package.env_bind() adds functions funs global environment, instead masking names {base} package.Note \"funs\" output.","code":"(a) `x$f(x$z)`.\n(b) `f(x$z)`.\n(c) `x$f(z)`.\n(d) `f(z)`.\n(e) It depends.\nf <- mean\nz <- 1\nx <- list(f = mean, z = 1)\n\nidentical(with(x, f(z)), x$f(x$z))\n#> [1] TRUE\n\nidentical(with(x, f(z)), f(x$z))\n#> [1] TRUE\n\nidentical(with(x, f(z)), x$f(z))\n#> [1] TRUE\n\nidentical(with(x, f(z)), f(z))\n#> [1] TRUE\nfuns <- list(\n mean = function(x) mean(x, na.rm = TRUE),\n sum = function(x) sum(x, na.rm = TRUE)\n)\n\nattach(funs)\n#> The following objects are masked from package:base:\n#> \n#> mean, sum\n\nmean\n#> function(x) mean(x, na.rm = TRUE)\nhead(search())\n#> [1] \".GlobalEnv\" \"funs\" \"package:scales\" \n#> [4] \"package:ggplot2\" \"package:rlang\" \"package:magrittr\"\n\nmean <- function(x) stop(\"Hi!\")\nmean\n#> function(x) stop(\"Hi!\")\nhead(search())\n#> [1] \".GlobalEnv\" \"funs\" \"package:scales\" \n#> [4] \"package:ggplot2\" \"package:rlang\" \"package:magrittr\"\n\ndetach(funs)\nenv_bind(globalenv(), !!!funs)\nmean\n#> function(x) mean(x, na.rm = TRUE)\n\nmean <- function(x) stop(\"Hi!\")\nmean\n#> function(x) stop(\"Hi!\")\nenv_unbind(globalenv(), names(funs))"},{"path":"function-factories.html","id":"session-information-8","chapter":"10 Function factories","heading":"10.5 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)\n#> base * 4.2.2 2022-10-31 [?] local\n#> bench 1.1.2 2021-11-30 [1] CRAN (R 4.2.0)\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> farver 2.1.1 2022-07-06 [1] CRAN (R 4.2.1)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1)\n#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.2)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> P grid 4.2.2 2022-10-31 [1] local\n#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.1)\n#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0)\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> labeling 0.4.2 2020-10-20 [1] CRAN (R 4.2.0)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> lobstr 1.1.2 2022-06-22 [1] CRAN (R 4.2.0)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0)\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)\n#> profmem 0.6.0 2020-12-13 [1] CRAN (R 4.2.0)\n#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> scales * 1.2.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> tibble 3.1.8.9002 2022-10-16 [1] local\n#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"function-operators.html","id":"function-operators","chapter":"11 Function operators","heading":"11 Function operators","text":"Attaching needed libraries:","code":"\nlibrary(purrr, warn.conflicts = FALSE)"},{"path":"function-operators.html","id":"existing-function-operators-exercises-11.2.3","chapter":"11 Function operators","heading":"11.1 Existing function operators (Exercises 11.2.3)","text":"Q1. Base R provides function operator form Vectorize(). ? might use ?A1. Vectorize() function creates function vectorizes action provided function specified arguments (.e., acts element vector). see utility solving problem otherwise difficult solve.problem find indices matching numeric values given threshold creating hybrid following functions:%% (doesn’t provide way provide tolerance comparing numeric values),dplyr::near() (vectorized element-wise thus expects two vectors equal length)Let’s use :Note needed create new function neither existing functions want.solved complex task using Vectorize() function!Q2. Read source code possibly(). work?A2. Let’s look source code function:Looking code, can see possibly():uses tryCatch() error handlinghas parameter otherwise specify default value case error occurshas parameter quiet suppress error message (needed)Q3. Read source code safely(). work?A3. Let’s look source code function:Looking code, can see safely():uses list save results (function executes successfully) error (fails)uses tryCatch() error handlinghas parameter otherwise specify default value case error occurshas parameter quiet suppress error message (needed)","code":"\nwhich_near <- function(x, y, tolerance) {\n # Vectorize `dplyr::near()` function only over the `y` argument.\n # `Vectorize()` is a function operator and will return a function.\n customNear <- Vectorize(dplyr::near, vectorize.args = c(\"y\"), SIMPLIFY = FALSE)\n\n # Apply the vectorized function to vector arguments and then check where the\n # comparisons are equal (i.e. `TRUE`) using `which()`.\n #\n # Use `compact()` to remove empty elements from the resulting list.\n index_list <- purrr::compact(purrr::map(customNear(x, y, tol = tolerance), which))\n\n # If there are any matches, return the indices as an atomic vector of integers.\n if (length(index_list) > 0L) {\n index_vector <- purrr::simplify(index_list, \"integer\")\n return(index_vector)\n }\n\n # If there are no matches\n return(integer(0L))\n}\nx1 <- c(2.1, 3.3, 8.45, 8, 6)\nx2 <- c(6, 8.40, 3)\n\nwhich_near(x1, x2, tolerance = 0.1)\n#> [1] 5 3\nwhich(x1 %in% x2)\n#> [1] 5\n\nwhich(dplyr::near(x1, x2, tol = 0.1))\n#> Warning in x - y: longer object length is not a multiple of\n#> shorter object length\n#> integer(0)\npossibly\n#> function (.f, otherwise, quiet = TRUE) \n#> {\n#> .f <- as_mapper(.f)\n#> force(otherwise)\n#> function(...) {\n#> tryCatch(.f(...), error = function(e) {\n#> if (!quiet) \n#> message(\"Error: \", e$message)\n#> otherwise\n#> }, interrupt = function(e) {\n#> stop(\"Terminated by user\", call. = FALSE)\n#> })\n#> }\n#> }\n#> \n#> \nsafely\n#> function (.f, otherwise = NULL, quiet = TRUE) \n#> {\n#> .f <- as_mapper(.f)\n#> function(...) capture_error(.f(...), otherwise, quiet)\n#> }\n#> \n#> \n\npurrr:::capture_error\n#> function (code, otherwise = NULL, quiet = TRUE) \n#> {\n#> tryCatch(list(result = code, error = NULL), error = function(e) {\n#> if (!quiet) \n#> message(\"Error: \", e$message)\n#> list(result = otherwise, error = e)\n#> }, interrupt = function(e) {\n#> stop(\"Terminated by user\", call. = FALSE)\n#> })\n#> }\n#> \n#> "},{"path":"function-operators.html","id":"case-study-creating-your-own-function-operators-exercises-11.3.1","chapter":"11 Function operators","heading":"11.2 Case study: Creating your own function operators (Exercises 11.3.1)","text":"Q1. Weigh pros cons download.file %>% dot_every(10) %>% delay_by(0.1) versus download.file %>% delay_by(0.1) %>% dot_every(10).A1. Although chains piped operations produce number dots need amount time, subtle difference .download.file %>% dot_every(10) %>% delay_by(0.1), printing dot also delayed, first dot printed 10th URL download starts.download.file %>% delay_by(0.1) %>% dot_every(10), first dot printed 9th download finished, 10th download starts short delay.Q2. memoise download.file()? ?A2. Since download.file() meant download files Internet, memoising recommended following reasons:Memoization helpful giving input function returns output. necessarily case webpages since constantly change, may continue β€œdownload” outdated version webpage.Memoization helpful giving input function returns output. necessarily case webpages since constantly change, may continue β€œdownload” outdated version webpage.Memoization works caching results, can take significant amount memory.Memoization works caching results, can take significant amount memory.Q3. Create function operator reports whenever file created deleted working directory, using dir() setdiff(). global function effects might want track?A3. First, let’s create helper functions compare print added removed filenames:can write function operator use create functions necessary tracking:Let’s try :global function effects might want track:working directoryenvironment variablesconnectionslibrary pathsgraphics devicesetc.Q4. Write function operator logs timestamp message file every time function run.A4. following function operator logs timestamp message file every time function run:Q5. Modify delay_by() instead delaying fixed amount time, ensures certain amount time elapsed since function last called. , called g <- delay_by(1, f); g(); Sys.sleep(2); g() shouldn’t extra delay.A5. Modified version function meeting specified requirements:","code":"\nprint_multiple_entries <- function(header, entries) {\n message(paste0(header, \":\\n\"), paste0(entries, collapse = \"\\n\"))\n}\n\nfile_comparator <- function(old, new) {\n if (setequal(old, new)) {\n return()\n }\n\n removed <- setdiff(old, new)\n added <- setdiff(new, old)\n\n if (length(removed) > 0L) print_multiple_entries(\"- File removed\", removed)\n if (length(added) > 0L) print_multiple_entries(\"- File added\", added)\n}\ndir_tracker <- function(f) {\n force(f)\n function(...) {\n old_files <- dir()\n on.exit(file_comparator(old_files, dir()), add = TRUE)\n\n f(...)\n }\n}\n\nfile_creation_tracker <- dir_tracker(file.create)\nfile_deletion_tracker <- dir_tracker(file.remove)\nfile_creation_tracker(c(\"a.txt\", \"b.txt\"))\n#> - File added:\n#> a.txt\n#> b.txt\n#> [1] TRUE TRUE\n\nfile_deletion_tracker(c(\"a.txt\", \"b.txt\"))\n#> - File removed:\n#> a.txt\n#> b.txt\n#> [1] TRUE TRUE\n# helper function to write to a file connection\nwrite_line <- function(filepath, ...) {\n cat(..., \"\\n\", sep = \"\", file = filepath, append = TRUE)\n}\n\n# function operator\nlogger <- function(f, filepath) {\n force(f)\n force(filepath)\n\n write_line(filepath, \"Function created at: \", as.character(Sys.time()))\n\n function(...) {\n write_line(filepath, \"Function called at: \", as.character(Sys.time()))\n f(...)\n }\n}\n\n# check that the function works as expected with a tempfile\nwithr::with_tempfile(\"logfile\", code = {\n logged_runif <- logger(runif, logfile)\n\n Sys.sleep(sample.int(10, 1))\n logged_runif(1)\n\n Sys.sleep(sample.int(10, 1))\n logged_runif(2)\n\n Sys.sleep(sample.int(10, 1))\n logged_runif(3)\n\n cat(readLines(logfile), sep = \"\\n\")\n})\n#> Function created at: 2022-11-12 11:49:04\n#> Function called at: 2022-11-12 11:49:09\n#> Function called at: 2022-11-12 11:49:14\n#> Function called at: 2022-11-12 11:49:22\ndelay_by_atleast <- function(f, amount) {\n force(f)\n force(amount)\n\n # the last time the function was run\n last_time <- NULL\n\n function(...) {\n if (!is.null(last_time)) {\n wait <- (last_time - Sys.time()) + amount\n if (wait > 0) Sys.sleep(wait)\n }\n\n # update the time in the parent frame for the next run when the function finishes\n on.exit(last_time <<- Sys.time())\n\n f(...)\n }\n}"},{"path":"function-operators.html","id":"session-information-9","chapter":"11 Function operators","heading":"11.3 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)\n#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> tibble 3.1.8.9002 2022-10-16 [1] local\n#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"base-types.html","id":"base-types","chapter":"12 Base Types","heading":"12 Base Types","text":"exercises.","code":""},{"path":"s3.html","id":"s3","chapter":"13 S3","heading":"13 S3","text":"Attaching needed libraries:","code":"\nlibrary(sloop, warn.conflicts = FALSE)\nlibrary(dplyr, warn.conflicts = FALSE)\nlibrary(purrr, warn.conflicts = FALSE)"},{"path":"s3.html","id":"basics-exercises-13.2.1","chapter":"13 S3","heading":"13.1 Basics (Exercises 13.2.1)","text":"Q1. Describe difference t.test() t.data.frame(). function called?A1. difference t.test() t.data.frame() following:t.test() generic function perform t-test.t.test() generic function perform t-test.t.data.frame() method generic t() (matrix transform function) dispatched data.frame objects.t.data.frame() method generic t() (matrix transform function) dispatched data.frame objects.can also confirm function types using ftype():Q2. Make list commonly used base R functions contain . name S3 methods.A2. common R functions . S3 methods:.equal().* functions (like .data.frame(), .numeric(), etc.)install.packages().exit()\netc.example,Q3. .data.frame.data.frame() method ? confusing? avoid confusion code?A3. ’s S3 method generic .data.frame().can seen methods supported generic:Given number .s name, quite confusing figure name generic name class.Q4. Describe difference behaviour two calls.A4. difference behaviour specified calls.unclassing, mean generic dispatches .Date method:unclassing, mean generic dispatches .numeric method:Q5. class object following code return? base type built ? attributes use?A5. object based base type closure6, type function.class ecdf, superclasses.Apart class, following attributes:Q6. class object following code return? base type built ? attributes use?A6. object based base type integer.class table.Apart class, following attributes:","code":"\nftype(t.test)\n#> [1] \"S3\" \"generic\"\nftype(t.data.frame)\n#> [1] \"S3\" \"method\"\nftype(as.data.frame)\n#> [1] \"S3\" \"generic\"\nftype(on.exit)\n#> [1] \"primitive\"\nftype(as.data.frame.data.frame)\n#> [1] \"S3\" \"method\"\ns3_methods_generic(\"as.data.frame\") %>%\n dplyr::filter(class == \"data.frame\")\n#> # A tibble: 1 Γ— 4\n#> generic class visible source\n#> \n#> 1 as.data.frame data.frame TRUE base\nset.seed(1014)\nsome_days <- as.Date(\"2017-01-31\") + sample(10, 5)\nmean(some_days)\n#> [1] \"2017-02-06\"\nmean(unclass(some_days))\n#> [1] 17203.4\nsome_days <- as.Date(\"2017-01-31\") + sample(10, 5)\n\nsome_days\n#> [1] \"2017-02-06\" \"2017-02-09\" \"2017-02-05\" \"2017-02-08\"\n#> [5] \"2017-02-07\"\n\ns3_dispatch(mean(some_days))\n#> => mean.Date\n#> * mean.default\n\nmean(some_days)\n#> [1] \"2017-02-07\"\nunclass(some_days)\n#> [1] 17203 17206 17202 17205 17204\n\nmean(unclass(some_days))\n#> [1] 17204\n\ns3_dispatch(mean(unclass(some_days)))\n#> mean.double\n#> mean.numeric\n#> => mean.default\nx <- ecdf(rpois(100, 10))\nx\nx <- ecdf(rpois(100, 10))\nx\n#> Empirical CDF \n#> Call: ecdf(rpois(100, 10))\n#> x[1:18] = 2, 3, 4, ..., 18, 19\n\notype(x)\n#> [1] \"S3\"\ntypeof(x)\n#> [1] \"closure\"\ns3_class(x)\n#> [1] \"ecdf\" \"stepfun\" \"function\"\nattributes(x)\n#> $class\n#> [1] \"ecdf\" \"stepfun\" \"function\"\n#> \n#> $call\n#> ecdf(rpois(100, 10))\nx <- table(rpois(100, 5))\nx\nx <- table(rpois(100, 5))\nx\n#> \n#> 1 2 3 4 5 6 7 8 9 10 \n#> 7 7 18 13 14 14 16 4 4 3\n\notype(x)\n#> [1] \"S3\"\ntypeof(x)\n#> [1] \"integer\"\ns3_class(x)\n#> [1] \"table\"\nattributes(x)\n#> $dim\n#> [1] 10\n#> \n#> $dimnames\n#> $dimnames[[1]]\n#> [1] \"1\" \"2\" \"3\" \"4\" \"5\" \"6\" \"7\" \"8\" \"9\" \"10\"\n#> \n#> \n#> $class\n#> [1] \"table\""},{"path":"s3.html","id":"classes-exercises-13.3.4","chapter":"13 S3","heading":"13.2 Classes (Exercises 13.3.4)","text":"Q1. Write constructor data.frame objects. base type data frame built ? attributes use? restrictions placed individual elements? names?A1. data frame built top named list atomic vectors attributes row names:restriction imposed individual elements need length. Additionally, names need syntactically valid unique.Let’s try :Q2. Enhance factor() helper better behaviour one values found levels. base::factor() situation?A2. one values found levels, values converted NA base::factor():new constructor, can throw error inform user:Let’s try :Q3. Carefully read source code factor(). constructor ?A3. source code factor() can read .number ways base version flexible.allows labeling values:checks levels duplicated.levels argument can NULL.Q4. Factors optional β€œcontrasts” attribute. Read help C(), briefly describe purpose attribute. type ? Rewrite new_factor() constructor include attribute.A4. Categorical variables typically encoded dummy variables regression models default level compared first factor level. Contrats provide flexible way comparisons.can set \"contrasts\" attribute factor using stats::C().Alternatively, can set \"contrasts\" attribute using matrix (?contrasts):[Contrasts] can matrix one row level factor suitable function like contr.poly character string giving name functionThe constructor provided book:can updated also support contrasts:Q5. Read documentation utils::.roman(). write constructor class? need validator? might helper ?A5. utils::.roman() converts Indo-Arabic numerals Roman numerals. Removing class also reveals implemented using base type integer:Therefore, can create simple constructor create new instance class:docs mention following:numbers 1 3899 unique representation roman numbers, hence others result .roman(NA).Therefore, can warn user return NA validator function:helper function can coerce entered input integer type convenience:Let’s try :","code":"\nunclass(data.frame())\n#> named list()\n#> attr(,\"row.names\")\n#> integer(0)\nnew_data_frame <- function(x = list(), row.names = character()) {\n # row names should be character\n if (!all(is.character(row.names))) {\n stop(\"Row name should be of `chracter` type.\", call. = FALSE)\n }\n\n # all elements should have the same length\n unique_element_lengths <- unique(purrr::map_int(x, length))\n if (length(unique_element_lengths) > 1L) {\n stop(\"All list elements in `x` should have same length.\", call. = FALSE)\n }\n\n # if not provided, generate row names\n # this is necessary if there is at least one element in the list\n if (length(x) > 0L && length(row.names) == 0L) {\n row.names <- .set_row_names(unique_element_lengths)\n }\n\n structure(x, class = \"data.frame\", row.names = row.names)\n}\nnew_data_frame(list(\"x\" = 1, \"y\" = c(2, 3)))\n#> Error: All list elements in `x` should have same length.\n\nnew_data_frame(list(\"x\" = 1, \"y\" = c(2)), row.names = 1L)\n#> Error: Row name should be of `chracter` type.\n\nnew_data_frame(list())\n#> data frame with 0 columns and 0 rows\n\nnew_data_frame(list(\"x\" = 1, \"y\" = 2))\n#> x y\n#> 1 1 2\n\nnew_data_frame(list(\"x\" = 1, \"y\" = 2), row.names = \"row-1\")\n#> x y\n#> row-1 1 2\nbase::factor(c(\"a\", \"b\", \"c\"), levels = c(\"a\", \"c\"))\n#> [1] a c \n#> Levels: a c\nnew_factor <- function(x = integer(), levels = character()) {\n stopifnot(is.integer(x))\n stopifnot(is.character(levels))\n\n structure(\n x,\n levels = levels,\n class = \"factor\"\n )\n}\n\nvalidate_factor <- function(x) {\n values <- unclass(x)\n levels <- attr(x, \"levels\")\n\n if (!all(!is.na(values) & values > 0)) {\n stop(\n \"All `x` values must be non-missing and greater than zero\",\n call. = FALSE\n )\n }\n\n if (length(levels) < max(values)) {\n stop(\n \"There must be at least as many `levels` as possible values in `x`\",\n call. = FALSE\n )\n }\n\n x\n}\n\ncreate_factor <- function(x = character(), levels = unique(x)) {\n ind <- match(x, levels)\n\n if (any(is.na(ind))) {\n missing_values <- x[which(is.na(match(x, levels)))]\n\n stop(\n paste0(\n \"Following values from `x` are not present in `levels`:\\n\",\n paste0(missing_values, collapse = \"\\n\")\n ),\n call. = FALSE\n )\n }\n\n validate_factor(new_factor(ind, levels))\n}\ncreate_factor(c(\"a\", \"b\", \"c\"), levels = c(\"a\", \"c\"))\n#> Error: Following values from `x` are not present in `levels`:\n#> b\n\ncreate_factor(c(\"a\", \"b\", \"c\"), levels = c(\"a\", \"b\", \"c\"))\n#> [1] a b c\n#> Levels: a b c\nx <- c(\"a\", \"b\", \"b\")\nlevels <- c(\"a\", \"b\", \"c\")\nlabels <- c(\"one\", \"two\", \"three\")\n\nfactor(x, levels = levels, labels = labels)\n#> [1] one two two\n#> Levels: one two three\nx <- c(\"a\", \"b\", \"b\")\nlevels <- c(\"a\", \"b\", \"b\")\n\nfactor(x, levels = levels)\n#> Error in `levels<-`(`*tmp*`, value = as.character(levels)): factor level [3] is duplicated\n\ncreate_factor(x, levels = levels)\n#> [1] a b b\n#> Levels: a b b\n#> Warning in print.factor(x): duplicated level [3] in factor\nx <- c(\"a\", \"b\", \"b\")\n\nfactor(x, levels = NULL)\n#> [1] \n#> Levels:\n\ncreate_factor(x, levels = NULL)\n#> Error: Following values from `x` are not present in `levels`:\n#> a\n#> b\n#> b\nnew_factor <- function(x = integer(), levels = character()) {\n stopifnot(is.integer(x))\n stopifnot(is.character(levels))\n\n structure(\n x,\n levels = levels,\n class = \"factor\"\n )\n}\nnew_factor <- function(x = integer(),\n levels = character(),\n contrasts = NULL) {\n stopifnot(is.integer(x))\n stopifnot(is.character(levels))\n\n if (!is.null(contrasts)) {\n stopifnot(is.matrix(contrasts) && is.numeric(contrasts))\n }\n\n structure(\n x,\n levels = levels,\n class = \"factor\",\n contrasts = contrasts\n )\n}\nas.roman(1)\n#> [1] I\n\ntypeof(unclass(as.roman(1)))\n#> [1] \"integer\"\nnew_roman <- function(x = integer()) {\n stopifnot(is.integer(x))\n\n structure(x, class = \"roman\")\n}\nas.roman(10000)\n#> [1] \nvalidate_new_roman <- function(x) {\n int_values <- unclass(x)\n\n if (any(int_values < 1L | int_values > 3899L)) {\n warning(\n \"Integer should be between 1 and 3899. Returning `NA` otherwise.\",\n call. = FALSE\n )\n }\n\n x\n}\nroman <- function(x = integer()) {\n x <- as.integer(x)\n\n validate_new_roman(new_roman(x))\n}\nroman(1)\n#> [1] I\n\nroman(c(5, 20, 100, 150, 100000))\n#> Warning: Integer should be between 1 and 3899. Returning\n#> `NA` otherwise.\n#> [1] V XX C CL "},{"path":"s3.html","id":"generics-and-methods-exercises-13.4.4","chapter":"13 S3","heading":"13.3 Generics and methods (Exercises 13.4.4)","text":"Q1. Read source code t() t.test() confirm t.test() S3 generic S3 method. happens create object class test call t() ? ?A1. Looking source code functions, can see generic, can confirm using sloop:Looking S3 dispatch, can see since R can’t find S3 method test class generic function t(), dispatches default method, converts structure matrix:behaviour can observed vector:Q2. generics table class methods ?A2. table class methods following generics:Q3. generics ecdf class methods ?A3. ecdf class methods following generics:Q4. base generic greatest number defined methods?A4. answer question, first, let’s list functions base retain generics.Now ’s simple matter counting number methods per generic ordering data frame descending order count:reveals base generic function methods print().Q5. Carefully read documentation UseMethod() explain following code returns results . two usual rules function evaluation UseMethod() violate?A5. called directly, g.default() method takes x value argument y global environment:, g() function called, takes x argument, comes function environment:docs ?UseMethod() clarify case:local variables defined call UseMethod retainedThat , UseMethod() calls g.default(), variables defined inside generic also available g.default() method. arguments supplied function passed , however, affected code inside generic.Two rules function evaluation violated UseMethod():Name maskingA fresh startQ6. arguments [? hard question answer?A6. difficult say many formal arguments subsetting [ operator generic function methods vectors, matrices, arrays, lists, etc., different methods different number arguments:can sample see wide variation number formal arguments:","code":"\nx <- structure(1:10, class = \"test\")\nt(x)\nt\n#> function (x) \n#> UseMethod(\"t\")\n#> \n#> \nsloop::is_s3_generic(\"t\")\n#> [1] TRUE\n\nt.test\n#> function (x, ...) \n#> UseMethod(\"t.test\")\n#> \n#> \nsloop::is_s3_generic(\"t.test\")\n#> [1] TRUE\nx <- structure(1:10, class = \"test\")\nt(x)\n#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]\n#> [1,] 1 2 3 4 5 6 7 8 9 10\n#> attr(,\"class\")\n#> [1] \"test\"\ns3_dispatch(t(x))\n#> => t.test\n#> * t.default\nt(1:10)\n#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]\n#> [1,] 1 2 3 4 5 6 7 8 9 10\ns3_methods_class(\"table\")\n#> # A tibble: 11 Γ— 4\n#> generic class visible source \n#> \n#> 1 [ table TRUE base \n#> 2 aperm table TRUE base \n#> 3 as_tibble table FALSE registered S3method\n#> 4 as.data.frame table TRUE base \n#> 5 Axis table FALSE registered S3method\n#> 6 lines table FALSE registered S3method\n#> 7 plot table FALSE registered S3method\n#> 8 points table FALSE registered S3method\n#> 9 print table TRUE base \n#> 10 summary table TRUE base \n#> 11 tail table FALSE registered S3method\ns3_methods_class(\"ecdf\")\n#> # A tibble: 4 Γ— 4\n#> generic class visible source \n#> \n#> 1 plot ecdf TRUE stats \n#> 2 print ecdf FALSE registered S3method\n#> 3 quantile ecdf FALSE registered S3method\n#> 4 summary ecdf FALSE registered S3method\n# getting all functions names\nobjs <- mget(ls(\"package:base\", all = TRUE), inherits = TRUE)\nfuns <- Filter(is.function, objs)\n\n# extracting only generics\ngenFuns <- names(funs) %>%\n purrr::keep(~ sloop::is_s3_generic(.x))\npurrr::map_dfr(\n genFuns,\n ~ s3_methods_generic(.)\n) %>%\n dplyr::group_by(generic) %>%\n dplyr::tally() %>%\n dplyr::arrange(desc(n))\n#> # A tibble: 120 Γ— 2\n#> generic n\n#> \n#> 1 print 286\n#> 2 format 132\n#> 3 [ 53\n#> 4 summary 39\n#> 5 as.character 37\n#> 6 as.data.frame 31\n#> 7 plot 30\n#> 8 [[ 27\n#> 9 [<- 17\n#> 10 $ 15\n#> # … with 110 more rows\ng <- function(x) {\n x <- 10\n y <- 10\n UseMethod(\"g\")\n}\ng.default <- function(x) c(x = x, y = y)\nx <- 1\ny <- 1\ng(x)\n#> x y \n#> 1 10\ng.default(x)\n#> x y \n#> 1 1\ng(x)\n#> x y \n#> 1 10\ns3_methods_generic(\"[\") %>%\n dplyr::filter(source == \"base\")\n#> # A tibble: 17 Γ— 4\n#> generic class visible source\n#> \n#> 1 [ AsIs TRUE base \n#> 2 [ data.frame TRUE base \n#> 3 [ Date TRUE base \n#> 4 [ difftime TRUE base \n#> 5 [ Dlist TRUE base \n#> 6 [ DLLInfoList TRUE base \n#> 7 [ factor TRUE base \n#> 8 [ hexmode TRUE base \n#> 9 [ listof TRUE base \n#> 10 [ noquote TRUE base \n#> 11 [ numeric_version TRUE base \n#> 12 [ octmode TRUE base \n#> 13 [ POSIXct TRUE base \n#> 14 [ POSIXlt TRUE base \n#> 15 [ simple.list TRUE base \n#> 16 [ table TRUE base \n#> 17 [ warnings TRUE base\n# table\nnames(formals(`[.table`))\n#> [1] \"x\" \"i\" \"j\" \"...\" \"drop\"\n\n# Date\nnames(formals(`[.Date`))\n#> [1] \"x\" \"...\" \"drop\"\n\n# data frame\nnames(formals(`[.data.frame`))\n#> [1] \"x\" \"i\" \"j\" \"drop\"\n\n# etc."},{"path":"s3.html","id":"object-styles-exercises-13.5.1","chapter":"13 S3","heading":"13.4 Object styles (Exercises 13.5.1)","text":"Q1. Categorise objects returned lm(), factor(), table(), .Date(), .POSIXct() ecdf(), ordered(), () styles described .A1. Objects returned functions can categorized follows:Vector style objects (length represents . observations)factor()table().Date().POSIXct()ordered()Record style objects (equi-length vectors represent object components)None.Dataframe style objects (Record style two-dimensions)None.Scalar objects (list represent single thing)lm() (represent one regression model)ecdf() (represents one distribution)() special:\njust adds new class object indicate treated .Therefore, object style superclass’ object style.Q2. constructor function lm objects, new_lm(), look like? Use ?lm experimentation figure required fields types.A2. lm object scalar object, .e.Β object contains named list atomic vectors varying lengths types represent single thing (regression model).Based information, can write new constructor object:","code":"\nfactor_obj <- factor(c(\"a\", \"b\"))\nlength(factor_obj)\n#> [1] 2\nlength(unclass(factor_obj))\n#> [1] 2\ntab_object <- table(mtcars$am)\nlength(tab_object)\n#> [1] 2\nlength(unlist(tab_object))\n#> [1] 2\ndate_obj <- as.Date(\"02/27/92\", \"%m/%d/%y\")\nlength(date_obj)\n#> [1] 1\nlength(unclass(date_obj))\n#> [1] 1\nposix_obj <- as.POSIXct(1472562988, origin = \"1960-01-01\")\nlength(posix_obj)\n#> [1] 1\nlength(unclass(posix_obj))\n#> [1] 1\nordered_obj <- ordered(factor(c(\"a\", \"b\")))\nlength(ordered_obj)\n#> [1] 2\nlength(unclass(ordered_obj))\n#> [1] 2\nlm_obj <- lm(wt ~ mpg, mtcars)\nlength(lm_obj)\n#> [1] 12\nlength(unclass(lm_obj))\n#> [1] 12\necdf_obj <- ecdf(rnorm(12))\nlength(ecdf_obj)\n#> [1] 1\nlength(unclass(ecdf_obj))\n#> [1] 1\nx <- ecdf(rnorm(12))\nclass(x)\n#> [1] \"ecdf\" \"stepfun\" \"function\"\nclass(I(x))\n#> [1] \"AsIs\" \"ecdf\" \"stepfun\" \"function\"\nmod <- lm(wt ~ mpg, mtcars)\n\ntypeof(mod)\n#> [1] \"list\"\n\nattributes(mod)\n#> $names\n#> [1] \"coefficients\" \"residuals\" \"effects\" \n#> [4] \"rank\" \"fitted.values\" \"assign\" \n#> [7] \"qr\" \"df.residual\" \"xlevels\" \n#> [10] \"call\" \"terms\" \"model\" \n#> \n#> $class\n#> [1] \"lm\"\n\npurrr::map_chr(unclass(mod), typeof)\n#> coefficients residuals effects rank \n#> \"double\" \"double\" \"double\" \"integer\" \n#> fitted.values assign qr df.residual \n#> \"double\" \"integer\" \"list\" \"integer\" \n#> xlevels call terms model \n#> \"list\" \"language\" \"language\" \"list\"\n\npurrr::map_int(unclass(mod), length)\n#> coefficients residuals effects rank \n#> 2 32 32 1 \n#> fitted.values assign qr df.residual \n#> 32 2 5 1 \n#> xlevels call terms model \n#> 0 3 3 2\nnew_lm <- function(coefficients,\n residuals,\n effects,\n rank,\n fitted.values,\n assign,\n qr,\n df.residual,\n xlevels,\n call,\n terms,\n model) {\n stopifnot(\n is.double(coefficients),\n is.double(residuals),\n is.double(effects),\n is.integer(rank),\n is.double(fitted.values),\n is.integer(assign),\n is.list(qr),\n is.integer(df.residual),\n is.list(xlevels),\n is.language(call),\n is.language(terms),\n is.list(model)\n )\n\n structure(\n list(\n coefficients = coefficients,\n residuals = residuals,\n effects = effects,\n rank = rank,\n fitted.values = fitted.values,\n assign = assign,\n qr = qr,\n df.residual = df.residual,\n xlevels = xlevels,\n call = call,\n terms = terms,\n model = model\n ),\n class = \"lm\"\n )\n}"},{"path":"s3.html","id":"inheritance-exercises-13.6.3","chapter":"13 S3","heading":"13.5 Inheritance (Exercises 13.6.3)","text":"Q1. [.Date support subclasses? fail support subclasses?A1. [.Date method defined follows:.Date function looks like :, oldClass class().Therefore, reading code, can surmise :[.Date supports subclasses preserving class input.[.Date fails support subclasses preserving attributes input.example,Q2. R two classes representing date time data, POSIXct POSIXlt, inherit POSIXt. generics different behaviours two classes? generics share behaviour?A2. First, let’s demonstrate POSIXct POSIXlt indeed subclasses POSIXt superclass.Remember way S3 method dispatch works, generic method superclass, method also inherited subclass.can extract vector generics supported sub- super-classes:Methods specific subclasses:Let’s see example:Methods inherited subclasses superclass:Let’s see one example generic:Q3. expect code return? actually return? ?A3. Naively, expect code return \"a1\", actually returns \"a2\":S3 dispatch explains :mentioned book, UseMethod() functiontracks list potential next methods special variable, means modifying object ’s dispatched upon impact method gets called next.special variable .Class:.Class character vector classes used find next method. NextMethod adds attribute β€œprevious” .Class giving .Class last used dispatch, shifts .Class along used dispatch., can print .Class confirm adding new class x indeed doesn’t change .Class, therefore dispatch occurs \"a2\" class:","code":"\nsloop::s3_get_method(\"[.Date\")\n#> function (x, ..., drop = TRUE) \n#> {\n#> .Date(NextMethod(\"[\"), oldClass(x))\n#> }\n#> \n#> \n.Date\n#> function (xx, cl = \"Date\") \n#> `class<-`(xx, cl)\n#> \n#> \nx <- structure(Sys.Date(), name = \"myName\", class = c(\"subDate\", \"Date\"))\n\n# `$name` is gone\nattributes(x[1])\n#> $class\n#> [1] \"subDate\" \"Date\"\n\nx[1]\n#> [1] \"2022-11-12\"\ndt_lt <- as.POSIXlt(Sys.time(), \"GMT\")\nclass(dt_lt)\n#> [1] \"POSIXlt\" \"POSIXt\"\n\ndt_ct <- as.POSIXct(Sys.time(), \"GMT\")\nclass(dt_ct)\n#> [1] \"POSIXct\" \"POSIXt\"\n\ndt_t <- structure(dt_ct, class = \"POSIXt\")\nclass(dt_t)\n#> [1] \"POSIXt\"\n(t_generics <- s3_methods_class(\"POSIXt\")$generic)\n#> [1] \"-\" \"+\" \"all.equal\" \n#> [4] \"as.character\" \"Axis\" \"cut\" \n#> [7] \"diff\" \"hist\" \"is.numeric\" \n#> [10] \"julian\" \"Math\" \"months\" \n#> [13] \"Ops\" \"pretty\" \"quantile\" \n#> [16] \"quarters\" \"round\" \"seq\" \n#> [19] \"str\" \"trunc\" \"weekdays\"\n\n(lt_generics <- s3_methods_class(\"POSIXlt\")$generic)\n#> [1] \"[\" \"[[\" \"[[<-\" \n#> [4] \"[<-\" \"anyNA\" \"as.data.frame\"\n#> [7] \"as.Date\" \"as.double\" \"as.list\" \n#> [10] \"as.matrix\" \"as.POSIXct\" \"as.vector\" \n#> [13] \"c\" \"duplicated\" \"format\" \n#> [16] \"is.na\" \"length\" \"length<-\" \n#> [19] \"mean\" \"names\" \"names<-\" \n#> [22] \"print\" \"rep\" \"sort\" \n#> [25] \"summary\" \"Summary\" \"unique\" \n#> [28] \"weighted.mean\" \"xtfrm\"\n\n(ct_generics <- s3_methods_class(\"POSIXct\")$generic)\n#> [1] \"[\" \"[[\" \"[<-\" \n#> [4] \"as.data.frame\" \"as.Date\" \"as.list\" \n#> [7] \"as.POSIXlt\" \"c\" \"format\" \n#> [10] \"length<-\" \"mean\" \"print\" \n#> [13] \"rep\" \"split\" \"summary\" \n#> [16] \"Summary\" \"weighted.mean\" \"xtfrm\"\nunion(lt_generics, ct_generics)\n#> [1] \"[\" \"[[\" \"[[<-\" \n#> [4] \"[<-\" \"anyNA\" \"as.data.frame\"\n#> [7] \"as.Date\" \"as.double\" \"as.list\" \n#> [10] \"as.matrix\" \"as.POSIXct\" \"as.vector\" \n#> [13] \"c\" \"duplicated\" \"format\" \n#> [16] \"is.na\" \"length\" \"length<-\" \n#> [19] \"mean\" \"names\" \"names<-\" \n#> [22] \"print\" \"rep\" \"sort\" \n#> [25] \"summary\" \"Summary\" \"unique\" \n#> [28] \"weighted.mean\" \"xtfrm\" \"as.POSIXlt\" \n#> [31] \"split\"\ns3_dispatch(is.na(dt_lt))\n#> => is.na.POSIXlt\n#> is.na.POSIXt\n#> is.na.default\n#> * is.na (internal)\n\ns3_dispatch(is.na(dt_ct))\n#> is.na.POSIXct\n#> is.na.POSIXt\n#> is.na.default\n#> => is.na (internal)\n\ns3_dispatch(is.na(dt_t))\n#> is.na.POSIXt\n#> is.na.default\n#> => is.na (internal)\nsetdiff(t_generics, union(lt_generics, ct_generics))\n#> [1] \"-\" \"+\" \"all.equal\" \n#> [4] \"as.character\" \"Axis\" \"cut\" \n#> [7] \"diff\" \"hist\" \"is.numeric\" \n#> [10] \"julian\" \"Math\" \"months\" \n#> [13] \"Ops\" \"pretty\" \"quantile\" \n#> [16] \"quarters\" \"round\" \"seq\" \n#> [19] \"str\" \"trunc\" \"weekdays\"\ns3_dispatch(is.numeric(dt_lt))\n#> is.numeric.POSIXlt\n#> => is.numeric.POSIXt\n#> is.numeric.default\n#> * is.numeric (internal)\n\ns3_dispatch(is.numeric(dt_ct))\n#> is.numeric.POSIXct\n#> => is.numeric.POSIXt\n#> is.numeric.default\n#> * is.numeric (internal)\n\ns3_dispatch(is.numeric(dt_t))\n#> => is.numeric.POSIXt\n#> is.numeric.default\n#> * is.numeric (internal)\ngeneric2 <- function(x) UseMethod(\"generic2\")\ngeneric2.a1 <- function(x) \"a1\"\ngeneric2.a2 <- function(x) \"a2\"\ngeneric2.b <- function(x) {\n class(x) <- \"a1\"\n NextMethod()\n}\n\ngeneric2(structure(list(), class = c(\"b\", \"a2\")))\ngeneric2 <- function(x) UseMethod(\"generic2\")\ngeneric2.a1 <- function(x) \"a1\"\ngeneric2.a2 <- function(x) \"a2\"\ngeneric2.b <- function(x) {\n class(x) <- \"a1\"\n NextMethod()\n}\n\ngeneric2(structure(list(), class = c(\"b\", \"a2\")))\n#> [1] \"a2\"\nsloop::s3_dispatch(generic2(structure(list(), class = c(\"b\", \"a2\"))))\n#> => generic2.b\n#> -> generic2.a2\n#> generic2.default\ngeneric2.b <- function(x) {\n message(paste0(\"before: \", paste0(.Class, collapse = \", \")))\n class(x) <- \"a1\"\n message(paste0(\"after: \", paste0(.Class, collapse = \", \")))\n\n NextMethod()\n}\n\ninvisible(generic2(structure(list(), class = c(\"b\", \"a2\"))))\n#> before: b, a2\n#> after: b, a2"},{"path":"s3.html","id":"dispatch-details-exercises-13.7.5","chapter":"13 S3","heading":"13.6 Dispatch details (Exercises 13.7.5)","text":"Q1. Explain differences dispatch :A1. differences dispatch due classes arguments:x1 implicit class integer inherits numeric, x2 explicitly assigned class integer.Q2. classes method Math group generic base R? Read source code. methods work?A2. following classes method Math group generic base R:Reading source code methods:Math.factor() Math.Date() provide error message:Math.data.frame() defined follows (except first line code, deliberately added):can surmised code: method checks elements expected type., applies generic (tracked via environment variable .Generic) element list atomic vectors makes data frame:, produces error:Q3. Math.difftime() complicated described. ?A3. Math.difftime() source code looks like following:group generic bit complicated produces error generics, works others.","code":"\nlength.integer <- function(x) 10\n\nx1 <- 1:5\nclass(x1)\n#> [1] \"integer\"\ns3_dispatch(length(x1))\n#> * length.integer\n#> length.numeric\n#> length.default\n#> => length (internal)\n\nx2 <- structure(x1, class = \"integer\")\nclass(x2)\n#> [1] \"integer\"\ns3_dispatch(length(x2))\n#> => length.integer\n#> length.default\n#> * length (internal)\ns3_class(x1)\n#> [1] \"integer\" \"numeric\"\n\ns3_class(x2)\n#> [1] \"integer\"\ns3_methods_generic(\"Math\") %>%\n dplyr::filter(source == \"base\")\n#> # A tibble: 5 Γ— 4\n#> generic class visible source\n#> \n#> 1 Math data.frame TRUE base \n#> 2 Math Date TRUE base \n#> 3 Math difftime TRUE base \n#> 4 Math factor TRUE base \n#> 5 Math POSIXt TRUE base\nMath.factor <- function(x, ...) {\n stop(gettextf(\"%s not meaningful for factors\", sQuote(.Generic)))\n}\n\nMath.Date <- function(x, ...) {\n stop(gettextf(\"%s not defined for \\\"Date\\\" objects\", .Generic),\n domain = NA\n )\n}\nMath.data.frame <- function(x, ...) {\n message(paste0(\"Environment variable `.Generic` set to: \", .Generic))\n\n mode.ok <- vapply(x, function(x) {\n is.numeric(x) || is.logical(x) || is.complex(x)\n }, NA)\n\n if (all(mode.ok)) {\n x[] <- lapply(X = x, FUN = .Generic, ...)\n return(x)\n } else {\n vnames <- names(x)\n if (is.null(vnames)) vnames <- seq_along(x)\n stop(\n \"non-numeric-alike variable(s) in data frame: \",\n paste(vnames[!mode.ok], collapse = \", \")\n )\n }\n}\ndf1 <- data.frame(x = 1:2, y = 3:4)\nsqrt(df1)\n#> Environment variable `.Generic` set to: sqrt\n#> x y\n#> 1 1.000000 1.732051\n#> 2 1.414214 2.000000\ndf2 <- data.frame(x = c(TRUE, FALSE), y = c(\"a\", \"b\"))\nabs(df2)\n#> Environment variable `.Generic` set to: abs\n#> Error in Math.data.frame(df2): non-numeric-alike variable(s) in data frame: y\nMath.difftime <- function(x, ...) {\n switch(.Generic,\n \"abs\" = ,\n \"sign\" = ,\n \"floor\" = ,\n \"ceiling\" = ,\n \"trunc\" = ,\n \"round\" = ,\n \"signif\" = {\n units <- attr(x, \"units\")\n .difftime(NextMethod(), units)\n },\n ### otherwise :\n stop(gettextf(\"'%s' not defined for \\\"difftime\\\" objects\", .Generic),\n domain = NA\n )\n )\n}"},{"path":"s3.html","id":"session-information-10","chapter":"13 S3","heading":"13.7 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> codetools 0.2-18 2020-11-04 [1] CRAN (R 4.2.2)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1)\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)\n#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> sloop * 1.0.1 2019-02-17 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> tibble 3.1.8.9002 2022-10-16 [1] local\n#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"r6.html","id":"r6","chapter":"14 R6","heading":"14 R6","text":"Loading needed libraries:","code":"\nlibrary(R6)"},{"path":"r6.html","id":"classes-and-methods-exercises-14.2.6","chapter":"14 R6","heading":"14.1 Classes and methods (Exercises 14.2.6)","text":"Q1. Create bank account R6 class stores balance allows deposit withdraw money. Create subclass throws error attempt go overdraft. Create another subclass allows go overdraft, charges fee. Create superclass make sure works expected.A1. First, let’s create bank account R6 class stores balance allows deposit withdraw money:Let’s try :Create subclass errors attempt overdraw:Let’s try :Now let’s create subclass charges fee account overdrawn:Let’s try :Q2. Create R6 class represents shuffled deck cards. able draw cards deck $draw(n), return cards deck reshuffle $reshuffle(). Use following code make vector cards.A2. Let’s create needed class represents shuffled deck cards:Let’s try :Q3. can’t model bank account deck cards S3 class?A3. can’t model bank account deck cards S3 class instances classes immutable.hand, R6 classes encapsulate data represent state, can change course object’s lifecycle. words, objects mutable well-suited model bank account.Q4. Create R6 class allows get set current time zone. can access current time zone Sys.timezone() set Sys.setenv(TZ = \"newtimezone\"). setting time zone, make sure new time zone list provided OlsonNames().A4. R6 class manages current time zone:Let’s try :Q5. Create R6 class manages current working directory. $get() $set() methods.A5. R6 class manages current working directory:Let’s create instance class check methods work expected:Q6. can’t model time zone current working directory S3 class?A6. answer Q3:Objects represent real-life entities need mutable S3 class instances mutable.Q7. base type R6 objects built top ? attributes ?A7. Let’s create example class create instance class:R6 objects built top environment:class attribute, character vector \"R6\" last element superclasses elements:","code":"\nlibrary(R6)\n\nbankAccount <- R6::R6Class(\n \"bankAccount\",\n public = list(\n balance = 0,\n initialize = function(balance) {\n self$balance <- balance\n },\n deposit = function(amount) {\n self$balance <- self$balance + amount\n message(paste0(\"Current balance is: \", self$balance))\n invisible(self)\n },\n withdraw = function(amount) {\n self$balance <- self$balance - amount\n message(paste0(\"Current balance is: \", self$balance))\n invisible(self)\n }\n )\n)\nindra <- bankAccount$new(balance = 100)\n\nindra$deposit(20)\n#> Current balance is: 120\n\nindra$withdraw(10)\n#> Current balance is: 110\nbankAccountStrict <- R6::R6Class(\n \"bankAccountStrict\",\n inherit = bankAccount,\n public = list(\n withdraw = function(amount) {\n if (self$balance - amount < 0) {\n stop(\n paste0(\"Can't withdraw more than your current balance: \", self$balance),\n call. = FALSE\n )\n }\n\n super$withdraw(amount)\n }\n )\n)\nPritesh <- bankAccountStrict$new(balance = 100)\n\nPritesh$deposit(20)\n#> Current balance is: 120\n\nPritesh$withdraw(150)\n#> Error: Can't withdraw more than your current balance: 120\nbankAccountFee <- R6::R6Class(\n \"bankAccountFee\",\n inherit = bankAccount,\n public = list(\n withdraw = function(amount) {\n super$withdraw(amount)\n\n if (self$balance) {\n self$balance <- self$balance - 10\n message(\"You're withdrawing more than your current balance. You will be charged a fee of 10 euros.\")\n }\n }\n )\n)\nMangesh <- bankAccountFee$new(balance = 100)\n\nMangesh$deposit(20)\n#> Current balance is: 120\n\nMangesh$withdraw(150)\n#> Current balance is: -30\n#> You're withdrawing more than your current balance. You will be charged a fee of 10 euros.\nsuit <- c(\"β™ \", \"β™₯\", \"♦\", \"♣\")\nvalue <- c(\"A\", 2:10, \"J\", \"Q\", \"K\")\ncards <- paste0(rep(value, 4), suit)\nsuit <- c(\"β™ \", \"β™₯\", \"♦\", \"♣\")\nvalue <- c(\"A\", 2:10, \"J\", \"Q\", \"K\")\ncards <- paste(rep(value, 4), suit)\n\nDeck <- R6::R6Class(\n \"Deck\",\n public = list(\n initialize = function(deck) {\n private$cards <- sample(deck)\n },\n draw = function(n) {\n if (n > length(private$cards)) {\n stop(\n paste0(\"Can't draw more than remaining number of cards: \", length(private$cards)),\n call. = FALSE\n )\n }\n\n drawn_cards <- sample(private$cards, n)\n private$cards <- private$cards[-which(private$cards %in% drawn_cards)]\n message(paste0(\"Remaining number of cards: \", length(private$cards)))\n\n return(drawn_cards)\n },\n reshuffle = function() {\n private$cards <- sample(private$cards)\n invisible(self)\n }\n ),\n private = list(\n cards = NULL\n )\n)\nmyDeck <- Deck$new(cards)\n\nmyDeck$draw(4)\n#> Remaining number of cards: 48\n#> [1] \"2 β™ \" \"10 ♦\" \"9 ♦\" \"3 ♦\"\n\nmyDeck$reshuffle()$draw(5)\n#> Remaining number of cards: 43\n#> [1] \"6 ♦\" \"10 β™₯\" \"2 β™₯\" \"A β™₯\" \"8 β™₯\"\n\nmyDeck$draw(50)\n#> Error: Can't draw more than remaining number of cards: 43\nCurrentTimeZone <- R6::R6Class(\"CurrentTimeZone\",\n public = list(\n setTimeZone = function(tz) {\n stopifnot(tz %in% OlsonNames())\n Sys.setenv(TZ = tz)\n },\n getTimeZone = function() {\n Sys.timezone()\n }\n )\n)\nmyCurrentTimeZone <- CurrentTimeZone$new()\n\nmyCurrentTimeZone$getTimeZone()\n#> [1] \"Europe/Berlin\"\n\nmyCurrentTimeZone$setTimeZone(\"Asia/Kolkata\")\nmyCurrentTimeZone$getTimeZone()\n#> [1] \"Europe/Berlin\"\n\nmyCurrentTimeZone$setTimeZone(\"Europe/Berlin\")\nManageDirectory <- R6::R6Class(\"ManageDirectory\",\n public = list(\n setWorkingDirectory = function(dir) {\n setwd(dir)\n },\n getWorkingDirectory = function() {\n getwd()\n }\n )\n)\nmyDirManager <- ManageDirectory$new()\n\n# current working directory\nmyDirManager$getWorkingDirectory()\n\n# change and check if that worked\nmyDirManager$setWorkingDirectory(\"..\")\nmyDirManager$getWorkingDirectory()\n\n# revert this change\nmyDirManager$setWorkingDirectory(\"/Advanced-R-exercises\")\nExample <- R6::R6Class(\"Example\")\nmyExample <- Example$new()\ntypeof(myExample)\n#> [1] \"environment\"\n\nrlang::env_print(myExample)\n#> [L]\n#> Parent: \n#> Class: Example, R6\n#> Bindings:\n#> β€’ .__enclos_env__: \n#> β€’ clone: [L]\nattributes(myExample)\n#> $class\n#> [1] \"Example\" \"R6\""},{"path":"r6.html","id":"controlling-access-exercises-14.3.3","chapter":"14 R6","heading":"14.2 Controlling access (Exercises 14.3.3)","text":"Q1. Create bank account class prevents directly setting account balance, can still withdraw deposit . Throw error attempt go overdraft.A1. bank account class satisfies specified requirements:Let’s check works expected:Q2. Create class write-$password field. $check_password(password) method returns TRUE FALSE, way view complete password.A2. implementation class needed properties:, course, everything possible:Q3. Extend Rando class another active binding allows access previous random value. Ensure active binding way access value.A3. modified version Rando class meet specified requirements:Let’s try :Q4. Can subclasses access private fields/methods parent? Perform experiment find .A4. Unlike common OOP languages (e.g.Β C++), R6 subclasses (derived classes) also access private methods superclass (base class).instance, following example, Duck class private method $quack(), subclass Mallard can access using super$quack().","code":"\nSafeBankAccount <- R6::R6Class(\n classname = \"SafeBankAccount\",\n public = list(\n deposit = function(deposit_amount) {\n private$.balance <- private$.balance + deposit_amount\n print(paste(\"Current balance:\", private$.balance))\n\n invisible(self)\n },\n withdraw = function(withdrawal_amount) {\n if (withdrawal_amount > private$.balance) {\n stop(\"You can't withdraw more than your current balance.\", call. = FALSE)\n }\n\n private$.balance <- private$.balance - withdrawal_amount\n print(paste(\"Current balance:\", private$.balance))\n\n invisible(self)\n }\n ),\n private = list(\n .balance = 0\n )\n)\nmySafeBankAccount <- SafeBankAccount$new()\n\nmySafeBankAccount$deposit(100)\n#> [1] \"Current balance: 100\"\n\nmySafeBankAccount$withdraw(50)\n#> [1] \"Current balance: 50\"\n\nmySafeBankAccount$withdraw(100)\n#> Error: You can't withdraw more than your current balance.\nlibrary(R6)\n\ncheckCredentials <- R6Class(\n \"checkCredentials\",\n public = list(\n # setter\n set_password = function(password) {\n private$.password <- password\n },\n\n # checker\n check_password = function(password) {\n if (is.null(private$.password)) {\n stop(\"No password set to check against.\")\n }\n\n identical(password, private$.password)\n },\n\n # the default print method prints the private fields as well\n print = function() {\n cat(\"Password: XXXX\")\n\n # for method chaining\n invisible(self)\n }\n ),\n private = list(\n .password = NULL\n )\n)\n\nmyCheck <- checkCredentials$new()\n\nmyCheck$set_password(\"1234\")\nprint(myCheck)\n#> Password: XXXX\n\nmyCheck$check_password(\"abcd\")\n#> [1] FALSE\nmyCheck$check_password(\"1234\")\n#> [1] TRUE\nmyCheck$.__enclos_env__$private$.password\n#> [1] \"1234\"\nRando <- R6::R6Class(\"Rando\",\n active = list(\n random = function(value) {\n if (missing(value)) {\n newValue <- runif(1)\n private$.previousRandom <- private$.currentRandom\n private$.currentRandom <- newValue\n return(private$.currentRandom)\n } else {\n stop(\"Can't set `$random`\", call. = FALSE)\n }\n },\n previousRandom = function(value) {\n if (missing(value)) {\n if (is.null(private$.previousRandom)) {\n message(\"No random value has been generated yet.\")\n } else {\n return(private$.previousRandom)\n }\n } else {\n stop(\"Can't set `$previousRandom`\", call. = FALSE)\n }\n }\n ),\n private = list(\n .currentRandom = NULL,\n .previousRandom = NULL\n )\n)\nmyRando <- Rando$new()\n\n# first time\nmyRando$random\n#> [1] 0.5549124\nmyRando$previousRandom\n#> No random value has been generated yet.\n#> NULL\n\n# second time\nmyRando$random\n#> [1] 0.3482785\nmyRando$previousRandom\n#> [1] 0.5549124\n\n# third time\nmyRando$random\n#> [1] 0.2187275\nmyRando$previousRandom\n#> [1] 0.3482785\nDuck <- R6Class(\"Duck\",\n private = list(quack = function() print(\"Quack Quack\"))\n)\n\nMallard <- R6Class(\"Mallard\",\n inherit = Duck,\n public = list(quack = function() super$quack())\n)\n\nmyMallard <- Mallard$new()\nmyMallard$quack()\n#> [1] \"Quack Quack\""},{"path":"r6.html","id":"reference-semantics-exercises-14.4.4","chapter":"14 R6","heading":"14.3 Reference semantics (Exercises 14.4.4)","text":"Q1. Create class allows write line specified file. open connection file $initialize(), append line using cat() $append_line(), close connection $finalize().A1. class allows write line specified file:Let’s check works expected:","code":"\nfileEditor <- R6Class(\n \"fileEditor\",\n public = list(\n initialize = function(filePath) {\n private$.connection <- file(filePath, open = \"wt\")\n },\n append_line = function(text) {\n cat(\n text,\n file = private$.connection,\n sep = \"\\n\",\n append = TRUE\n )\n }\n ),\n private = list(\n .connection = NULL,\n # according to R6 docs, the destructor method should be private\n finalize = function() {\n print(\"Closing the file connection!\")\n close(private$.connection)\n }\n )\n)\ngreetMom <- function() {\n f <- tempfile()\n myfileEditor <- fileEditor$new(f)\n\n readLines(f)\n\n myfileEditor$append_line(\"Hi mom!\")\n myfileEditor$append_line(\"It's a beautiful day!\")\n\n readLines(f)\n}\n\ngreetMom()\n#> [1] \"Hi mom!\" \"It's a beautiful day!\"\n\n# force garbage collection\ngc()\n#> [1] \"Closing the file connection!\"\n#> used (Mb) gc trigger (Mb) limit (Mb) max used\n#> Ncells 768546 41.1 1395468 74.6 NA 1395468\n#> Vcells 1407126 10.8 8388608 64.0 16384 2601527\n#> (Mb)\n#> Ncells 74.6\n#> Vcells 19.9"},{"path":"r6.html","id":"session-information-11","chapter":"14 R6","heading":"14.4 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> R6 * 2.5.1.9000 2022-10-27 [1] local\n#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"s4.html","id":"s4","chapter":"15 S4","heading":"15 S4","text":"","code":""},{"path":"s4.html","id":"basics-exercises-15.2.1","chapter":"15 S4","heading":"15.1 Basics (Exercises 15.2.1)","text":"Q1. lubridate::period() returns S4 class. slots ? class slot? accessors provide?A1. Let’s first create instance Period class:following slots:Additionally, base type slot (numeric) can seen str() output:lubridate package provides accessors slots:Q2. ways can find help method? Read ?\"?\" summarise details.A2. \"?\" operator allows access documentation three ways. demonstrate different ways access documentation, let’s define new S4 class.Ways access documentation:general documentation generic can found ?topic:expression type?topic look overall documentation methods function f.","code":"\nlibrary(lubridate)\nx <- lubridate::period(c(2, 43, 6), c(\"hour\", \"second\", \"minute\"))\nx\n#> [1] \"2H 6M 43S\"\nslotNames(x)\n#> [1] \".Data\" \"year\" \"month\" \"day\" \"hour\" \"minute\"\nstr(x)\n#> Formal class 'Period' [package \"lubridate\"] with 6 slots\n#> ..@ .Data : num 43\n#> ..@ year : num 0\n#> ..@ month : num 0\n#> ..@ day : num 0\n#> ..@ hour : num 2\n#> ..@ minute: num 6\nyear(x)\n#> [1] 0\nmonth(x)\n#> [1] 0\nday(x)\n#> [1] 0\nhour(x)\n#> [1] 2\nminute(x)\n#> [1] 6\nsecond(x)\n#> [1] 43\npow <- function(x, exp) c(x, exp)\nsetGeneric(\"pow\")\n#> [1] \"pow\"\nsetMethod(\"pow\", c(\"numeric\", \"numeric\"), function(x, exp) x^exp)\n?pow\n?pow # produces the function documentation\n\nmethods?pow # looks for the overall methods documentation"},{"path":"s4.html","id":"classes-exercises-15.3.6","chapter":"15 S4","heading":"15.2 Classes (Exercises 15.3.6)","text":"Q1. Extend Person class fields match utils::person(). Think slots need, class slot , ’ll need check validity method.A1. code extends Person class described book match closely utils::person().Let’s make sure validation works expected:Q2. happens define new S4 class doesn’t slots? (Hint: read virtual classes ?setClass.)A2. define new S4 class doesn’t slots, create virtual classes:can’t create instance class:useful? mentioned ?setClass docs:Classes exist actual objects can created, virtual classes.common useful form virtual class class union, virtual class defined call setClassUnion() rather call setClass().virtual classes can still inherited:addition specifying slots, another way create virtual classes:Calls setClass() also create virtual class, either Class argument supplied (slots superclasses) contains= argument includes special class name \"VIRTUAL\".Q3. Imagine going reimplement factors, dates, data frames S4. Sketch setClass() calls use define classes. Think appropriate slots prototype.A3. reimplementation following classes S4 might definitions like following.factorFor simplicity, won’t provide options factor() provides. Note x pseudo-class accept objects type.DateJust like base-R version, integer values.data.frameThe tricky part supporting ... argument data.frame(). , can let users pass (named) list.","code":"\nsetClass(\"Person\",\n slots = c(\n age = \"numeric\",\n given = \"character\",\n family = \"character\",\n middle = \"character\",\n email = \"character\",\n role = \"character\",\n comment = \"character\"\n ),\n prototype = list(\n age = NA_real_,\n given = NA_character_,\n family = NA_character_,\n middle = NA_character_,\n email = NA_character_,\n role = NA_character_,\n comment = NA_character_\n )\n)\n\n# Helper function to create an instance of the `Person` class\nPerson <- function(given,\n family,\n middle = NA_character_,\n age = NA_real_,\n email = NA_character_,\n role = NA_character_,\n comment = NA_character_) {\n age <- as.double(age)\n\n new(\"Person\",\n age = age,\n given = given,\n family = family,\n middle = middle,\n email = email,\n role = role,\n comment = comment\n )\n}\n\n# Validator to ensure that each slot is of length one and that the specified\n# role is one of the possible roles\nsetValidity(\"Person\", function(object) {\n invalid_length <- NULL\n slot_lengths <- c(\n length(object@age),\n length(object@given),\n length(object@middle),\n length(object@family),\n length(object@email),\n length(object@comment)\n )\n\n if (any(slot_lengths > 1L)) {\n invalid_length <- \"\\nFollowing slots must be of length 1:\\n @age, @given, @family, @middle, @email, @comment\"\n }\n\n possible_roles <- c(\n NA_character_, \"aut\", \"com\", \"cph\", \"cre\", \"ctb\", \"ctr\", \"dtc\", \"fnd\", \"rev\", \"ths\", \"trl\"\n )\n\n if (any(!object@role %in% possible_roles)) {\n invalid_length <- paste(\n invalid_length,\n \"\\nSlot @role(s) must be one of the following:\\n\",\n paste(possible_roles, collapse = \", \")\n )\n }\n\n if (!is.null(invalid_length)) {\n return(invalid_length)\n } else {\n return(TRUE)\n }\n})\n#> Class \"Person\" [in \".GlobalEnv\"]\n#> \n#> Slots:\n#> \n#> Name: age given family middle email\n#> Class: numeric character character character character\n#> \n#> Name: role comment\n#> Class: character character\n# length of first argument not 1\nPerson(c(\"Indrajeet\", \"Surendra\"), \"Patil\")\n#> Error in validObject(.Object): invalid class \"Person\" object: \n#> Following slots must be of length 1:\n#> @age, @given, @family, @middle, @email, @comment\n\n# role not recognized\nPerson(\"Indrajeet\", \"Patil\", role = \"xyz\")\n#> Error in validObject(.Object): invalid class \"Person\" object: \n#> Slot @role(s) must be one of the following:\n#> NA, aut, com, cph, cre, ctb, ctr, dtc, fnd, rev, ths, trl\n\n# all okay\nPerson(\"Indrajeet\", \"Patil\", role = c(\"aut\", \"cph\"))\n#> An object of class \"Person\"\n#> Slot \"age\":\n#> [1] NA\n#> \n#> Slot \"given\":\n#> [1] \"Indrajeet\"\n#> \n#> Slot \"family\":\n#> [1] \"Patil\"\n#> \n#> Slot \"middle\":\n#> [1] NA\n#> \n#> Slot \"email\":\n#> [1] NA\n#> \n#> Slot \"role\":\n#> [1] \"aut\" \"cph\"\n#> \n#> Slot \"comment\":\n#> [1] NA\nsetClass(\"Empty\")\n\nisVirtualClass(\"Empty\")\n#> [1] TRUE\nnew(\"Empty\")\n#> Error in new(\"Empty\"): trying to generate an object from a virtual class (\"Empty\")\nsetClass(\"Nothing\", contains = \"Empty\")\nsetClass(\"Factor\",\n slots = c(\n x = \"ANY\",\n levels = \"character\",\n ordered = \"logical\"\n ),\n prototype = list(\n x = character(),\n levels = character(),\n ordered = FALSE\n )\n)\n\nnew(\"Factor\", x = letters[1:3], levels = LETTERS[1:3])\n#> An object of class \"Factor\"\n#> Slot \"x\":\n#> [1] \"a\" \"b\" \"c\"\n#> \n#> Slot \"levels\":\n#> [1] \"A\" \"B\" \"C\"\n#> \n#> Slot \"ordered\":\n#> [1] FALSE\n\nnew(\"Factor\", x = 1:3, levels = letters[1:3])\n#> An object of class \"Factor\"\n#> Slot \"x\":\n#> [1] 1 2 3\n#> \n#> Slot \"levels\":\n#> [1] \"a\" \"b\" \"c\"\n#> \n#> Slot \"ordered\":\n#> [1] FALSE\n\nnew(\"Factor\", x = c(TRUE, FALSE, TRUE), levels = c(\"x\", \"y\", \"x\"))\n#> An object of class \"Factor\"\n#> Slot \"x\":\n#> [1] TRUE FALSE TRUE\n#> \n#> Slot \"levels\":\n#> [1] \"x\" \"y\" \"x\"\n#> \n#> Slot \"ordered\":\n#> [1] FALSE\nsetClass(\"Date2\",\n slots = list(\n data = \"integer\"\n ),\n prototype = list(\n data = integer()\n )\n)\n\nnew(\"Date2\", data = 1342L)\n#> An object of class \"Date2\"\n#> Slot \"data\":\n#> [1] 1342\nsetClass(\"DataFrame\",\n slots = c(\n data = \"list\",\n row.names = \"character\"\n ),\n prototype = list(\n data = list(),\n row.names = character(0L)\n )\n)\n\nnew(\"DataFrame\", data = list(x = c(\"a\", \"b\"), y = c(1L, 2L)))\n#> An object of class \"DataFrame\"\n#> Slot \"data\":\n#> $x\n#> [1] \"a\" \"b\"\n#> \n#> $y\n#> [1] 1 2\n#> \n#> \n#> Slot \"row.names\":\n#> character(0)"},{"path":"s4.html","id":"generics-and-methods-exercises-15.4.5","chapter":"15 S4","heading":"15.3 Generics and methods (Exercises 15.4.5)","text":"Q1. Add age() accessors Person class.A1. first define generic method class:Q2. definition generic, necessary repeat name generic twice?A2. Let’s look generic just defined; generic name \"age\" repeated twice.:\"age\" passed argument name provides name genericthe \"age\" passed argument def supplies method dispatchThis reminiscent defined S3 generic, also repeat name twice:Q3. show() method defined Section Show method use (object)[[1]]? (Hint: try printing employee subclass.)A3. wish define show() method specific class, need disregard super-/sub-classes.Always using first element ensures method defined class question:Q4. happens define method different argument names generic?A4. Let’s experiment method defined Q1. study behavior.original method worked expected since argument name generic method matched:case, either get warning get error depending many arguments specified:","code":"\nIndra <- Person(\"Indrajeet\", \"Patil\", role = c(\"aut\", \"cph\"), age = 34)\n\nsetGeneric(\"age\", function(x) standardGeneric(\"age\"))\n#> [1] \"age\"\nsetMethod(\"age\", \"Person\", function(x) x@age)\n\nage(Indra)\n#> [1] 34\nsetGeneric(name = \"age\", def = function(x) standardGeneric(\"age\"))\nage <- function(x) {\n UseMethod(\"age\")\n}\nAlice <- new(\"Employee\")\n\nis(Alice)\n#> [1] \"Employee\" \"Person\"\n\nis(Alice)[[1]]\n#> [1] \"Employee\"\nsetMethod(\"age\", \"Person\", function(x) x@age)\nsetMethod(\"age\", \"Person\", function(object) object@age)\n#> Warning: For function 'age', signature 'Person': argument in\n#> method definition changed from (object) to (x)\n\nsetMethod(\"age\", \"Person\", function(object, x) object@age)\n#> Error in rematchDefinition(definition, fdef, mnames, fnames, signature): methods can add arguments to the generic 'age' only if '...' is an argument to the generic\n\nsetMethod(\"age\", \"Person\", function(...) ...elt(1)@age)\n#> Warning: For function 'age', signature 'Person': argument in\n#> method definition changed from (...) to (x)\n\nsetMethod(\"age\", \"Person\", function(x, ...) x@age)\n#> Error in rematchDefinition(definition, fdef, mnames, fnames, signature): methods can add arguments to the generic 'age' only if '...' is an argument to the generic"},{"path":"s4.html","id":"method-dispatch-exercises-15.5.5","chapter":"15 S4","heading":"15.4 Method dispatch (Exercises 15.5.5)","text":"Q1. Draw method graph f(πŸ˜…, 😽).A1. don’t prepare visual illustrations used book, linking illustration official solution manual:Q2. Draw method graph f(πŸ˜ƒ, πŸ˜‰, πŸ˜™).A2. don’t prepare visual illustrations used book, linking illustration official solution manual:Q3. Take last example shows multiple dispatch two classes use multiple inheritance. happens define method terminal classes? method dispatch save us much work ?A3. one class distance 2 terminal nodes four distance 1 two terminal nodes , introduce ambiguity.Method dispatch save us much work resolve ambiguity define five methods (one per class combination).","code":""},{"path":"s4.html","id":"s4-and-s3-exercises-15.6.3","chapter":"15 S4","heading":"15.5 S4 and S3 (Exercises 15.6.3)","text":"Q1. full setOldClass() definition look like ordered factor (.e.Β add slots prototype definition )?A1. can register old-style/S3 ordered class formally defined class using setOldClass().Let’s use see works expected.Q2. Define length method Person class.A2. Person class can used create objects represent multiple people, let’s say length() method returns many persons object.can define S3 method class:Alternatively, can also write S4 method:","code":"\nsetClass(\"factor\",\n contains = \"integer\",\n slots = c(\n levels = \"character\"\n ),\n prototype = structure(\n integer(),\n levels = character()\n )\n)\nsetOldClass(\"factor\", S4Class = \"factor\")\n#> Warning in rm(list = what, pos = classWhere): object\n#> '.__C__factor' not found\n\nsetClass(\"Ordered\",\n contains = \"factor\",\n slots = c(\n levels = \"character\",\n ordered = \"logical\"\n ),\n prototype = structure(\n integer(),\n levels = character(),\n ordered = logical()\n )\n)\n\nsetOldClass(\"ordered\", S4Class = \"Ordered\")\nx <- new(\"Ordered\", 1L:4L, levels = letters[1:4], ordered = TRUE)\n\nx\n#> Object of class \"Ordered\"\n#> [1] a b c d\n#> Levels: a b c d\n#> Slot \"ordered\":\n#> [1] TRUE\n\nstr(x)\n#> Formal class 'Ordered' [package \".GlobalEnv\"] with 4 slots\n#> ..@ .Data : int [1:4] 1 2 3 4\n#> ..@ levels : chr [1:4] \"a\" \"b\" \"c\" \"d\"\n#> ..@ ordered : logi TRUE\n#> ..@ .S3Class: chr \"factor\"\n\nclass(x)\n#> [1] \"Ordered\"\n#> attr(,\"package\")\n#> [1] \".GlobalEnv\"\nFriends <- new(\"Person\", name = c(\"Vishu\", \"Aditi\"))\nlength.Person <- function(x) length(x@name)\n\nlength(Friends)\n#> [1] 2\nsetMethod(\"length\", \"Person\", function(x) length(x@name))\n\nlength(Friends)\n#> [1] 2"},{"path":"s4.html","id":"session-information-12","chapter":"15 S4","heading":"15.6 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1)\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> emo 0.0.0.9000 2022-05-17 [1] Github (hadley/emo@3f03b11)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0)\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lubridate * 1.9.0 2022-11-06 [1] CRAN (R 4.2.2)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> timechange * 0.1.1 2022-11-04 [1] CRAN (R 4.2.2)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"trade-offs.html","id":"trade-offs","chapter":"16 Trade-offs","heading":"16 Trade-offs","text":"exercises.","code":""},{"path":"big-picture.html","id":"big-picture","chapter":"17 Big Picture","heading":"17 Big Picture","text":"exercises.","code":""},{"path":"expressions.html","id":"expressions","chapter":"18 Expressions","heading":"18 Expressions","text":"Attaching needed libraries:","code":"\nlibrary(rlang, warn.conflicts = FALSE)\nlibrary(lobstr, warn.conflicts = FALSE)"},{"path":"expressions.html","id":"abstract-syntax-trees-exercises-18.2.4","chapter":"18 Expressions","heading":"18.1 Abstract syntax trees (Exercises 18.2.4)","text":"Q1. Reconstruct code represented trees :A1. reconstructed code.can confirm drawing ASTs :Q2. Draw following trees hand check answers ast().A2. Successfully drawn hand. Checking using ast():Q3. ’s happening ASTs ? (Hint: carefully read ?\"^\".)A3. str2expression() helps make sense ASTs.non-syntactic names parsed names. Thus, backticks removed AST.mentioned docs ^:** translated parser ^rightward assignment parsed leftward assignment:Q4. special AST ?A4. mentioned section:Like objects R, functions can also possess number additional attributes(). One attribute used base R srcref, short source reference. points source code used create function. srcref used printing , unlike body(), contains code comments formatting.Therefore, last leaf AST, although specified function call, represents source reference attribute.Q5. call tree statement multiple else conditions look like? ?A5. nothing special tree. just shows nested loop structure inherent code multiple else statements.","code":"#> β–ˆβ”€f \n#> β””β”€β–ˆβ”€g \n#> β””β”€β–ˆβ”€h\n#> β–ˆβ”€`+` \n#> β”œβ”€β–ˆβ”€`+` \n#> β”‚ β”œβ”€1 \n#> β”‚ └─2 \n#> └─3\n#> β–ˆβ”€`*` \n#> β”œβ”€β–ˆβ”€`(` \n#> β”‚ β””β”€β–ˆβ”€`+` \n#> β”‚ β”œβ”€x \n#> β”‚ └─y \n#> └─z\nf(g(h()))\n1 + 2 + 3\n(x + y) * z\nast(f(g(h())))\n#> β–ˆβ”€f \n#> β””β”€β–ˆβ”€g \n#> β””β”€β–ˆβ”€h\n\nast(1 + 2 + 3)\n#> β–ˆβ”€`+` \n#> β”œβ”€β–ˆβ”€`+` \n#> β”‚ β”œβ”€1 \n#> β”‚ └─2 \n#> └─3\n\nast((x + y) * z)\n#> β–ˆβ”€`*` \n#> β”œβ”€β–ˆβ”€`(` \n#> β”‚ β””β”€β–ˆβ”€`+` \n#> β”‚ β”œβ”€x \n#> β”‚ └─y \n#> └─z\nf(g(h(i(1, 2, 3))))\nf(1, g(2, h(3, i())))\nf(g(1, 2), h(3, i(4, 5)))\nast(f(g(h(i(1, 2, 3)))))\n#> β–ˆβ”€f \n#> β””β”€β–ˆβ”€g \n#> β””β”€β–ˆβ”€h \n#> β””β”€β–ˆβ”€i \n#> β”œβ”€1 \n#> β”œβ”€2 \n#> └─3\n\nast(f(1, g(2, h(3, i()))))\n#> β–ˆβ”€f \n#> β”œβ”€1 \n#> β””β”€β–ˆβ”€g \n#> β”œβ”€2 \n#> β””β”€β–ˆβ”€h \n#> β”œβ”€3 \n#> β””β”€β–ˆβ”€i\n\nast(f(g(1, 2), h(3, i(4, 5))))\n#> β–ˆβ”€f \n#> β”œβ”€β–ˆβ”€g \n#> β”‚ β”œβ”€1 \n#> β”‚ └─2 \n#> β””β”€β–ˆβ”€h \n#> β”œβ”€3 \n#> β””β”€β–ˆβ”€i \n#> β”œβ”€4 \n#> └─5\nast(`x` + `y`)\n#> β–ˆβ”€`+` \n#> β”œβ”€x \n#> └─y\nast(x**y)\n#> β–ˆβ”€`^` \n#> β”œβ”€x \n#> └─y\nast(1 -> x)\n#> β–ˆβ”€`<-` \n#> β”œβ”€x \n#> └─1\nstr2expression(\"`x` + `y`\")\n#> expression(x + y)\nstr2expression(\"x**y\")\n#> expression(x^y)\nstr2expression(\"1 -> x\")\n#> expression(x <- 1)\nast(function(x = 1, y = 2) {})\n#> β–ˆβ”€`function` \n#> β”œβ”€β–ˆβ”€x = 1 \n#> β”‚ └─y = 2 \n#> β”œβ”€β–ˆβ”€`{` \n#> └─\nast(if (FALSE) 1 else if (FALSE) 2 else if (FALSE) 3 else 4)\n#> β–ˆβ”€`if` \n#> β”œβ”€FALSE \n#> β”œβ”€1 \n#> β””β”€β–ˆβ”€`if` \n#> β”œβ”€FALSE \n#> β”œβ”€2 \n#> β””β”€β–ˆβ”€`if` \n#> β”œβ”€FALSE \n#> β”œβ”€3 \n#> └─4"},{"path":"expressions.html","id":"expressions-exercises-18.3.5","chapter":"18 Expressions","heading":"18.2 Expressions (Exercises 18.3.5)","text":"Q1. two six types atomic vector can’t appear expression? ? Similarly, can’t create expression contains atomic vector length greater one?A1. six types atomic vectors, two can’t appear expression : complex raw.Complex numbers created via function call (using +), can seen AST:Similarly, raw vectors (using raw()):Contrast atomic vectors:reason, can’t create expression contains atomic vector length greater one since function call uses c() function:Q2. happens subset call object remove first element? e.g.Β expr(read.csv(\"foo.csv\", header = TRUE))[-1]. ?A2. captured function call like following creates call object:mentioned respective section:first element call object function position.Therefore, first element call object removed, next one moves function position, get observed output:Q3. Describe differences following call objects.A4. differences constructed call objects due different type arguments supplied first two parameters call2() function.Types arguments supplied .fn:Types arguments supplied dynamic dots:following outputs can understood using following properties:.fn argument closure, function inlined constructed function callwhen x symbol, value passed function callImportantly, constructed call objects evaluate give result:Q4. call_standardise() doesn’t work well following calls. ? makes mean() special?A4. ellipsis mean() function signature:mentioned respective section:function uses ... ’s possible standardise arguments.mean() S3 generic dots passed underlying S3 methods., output can improved using specific method. example:Q5. code make sense?A5. doesn’t make sense first position call object reserved function (function position), assigning names element just ignored R:Q6. Construct expression (x > 1) \"\" else \"b\" using multiple calls call2(). code structure reflect structure AST?A6. Using multiple calls construct required expression:construction follows prefix form expression, revealed AST:","code":"\nx_complex <- expr(1 + 1i)\ntypeof(x_complex)\n#> [1] \"language\"\n\nast(1 + 1i)\n#> β–ˆβ”€`+` \n#> β”œβ”€1 \n#> └─1i\nx_raw <- expr(raw(2))\ntypeof(x_raw)\n#> [1] \"language\"\n\nast(raw(2))\n#> β–ˆβ”€raw \n#> └─2\nx_int <- expr(2L)\ntypeof(x_int)\n#> [1] \"integer\"\n\nast(2L)\n#> 2L\nx_vec <- expr(c(1, 2))\ntypeof(x_vec)\n#> [1] \"language\"\n\nast(c(1, 2))\n#> β–ˆβ”€c \n#> β”œβ”€1 \n#> └─2\nexpr(read.csv(\"foo.csv\", header = TRUE))\n#> read.csv(\"foo.csv\", header = TRUE)\n\ntypeof(expr(read.csv(\"foo.csv\", header = TRUE)))\n#> [1] \"language\"\nexpr(read.csv(\"foo.csv\", header = TRUE))[-1]\n#> \"foo.csv\"(header = TRUE)\nx <- 1:10\ncall2(median, x, na.rm = TRUE)\ncall2(expr(median), x, na.rm = TRUE)\ncall2(median, expr(x), na.rm = TRUE)\ncall2(expr(median), expr(x), na.rm = TRUE)\ntypeof(median)\n#> [1] \"closure\"\ntypeof(expr(median))\n#> [1] \"symbol\"\nx <- 1:10\ntypeof(x)\n#> [1] \"integer\"\ntypeof(expr(x))\n#> [1] \"symbol\"\nx <- 1:10\n\ncall2(median, x, na.rm = TRUE)\n#> (function (x, na.rm = FALSE, ...) \n#> UseMethod(\"median\"))(1:10, na.rm = TRUE)\n\ncall2(expr(median), x, na.rm = TRUE)\n#> median(1:10, na.rm = TRUE)\n\ncall2(median, expr(x), na.rm = TRUE)\n#> (function (x, na.rm = FALSE, ...) \n#> UseMethod(\"median\"))(x, na.rm = TRUE)\n\ncall2(expr(median), expr(x), na.rm = TRUE)\n#> median(x, na.rm = TRUE)\nx <- 1:10\n\neval(call2(median, x, na.rm = TRUE))\n#> [1] 5.5\n\neval(call2(expr(median), x, na.rm = TRUE))\n#> [1] 5.5\n\neval(call2(median, expr(x), na.rm = TRUE))\n#> [1] 5.5\n\neval(call2(expr(median), expr(x), na.rm = TRUE))\n#> [1] 5.5\ncall_standardise(quote(mean(1:10, na.rm = TRUE)))\n#> mean(x = 1:10, na.rm = TRUE)\ncall_standardise(quote(mean(n = T, 1:10)))\n#> mean(x = 1:10, n = T)\ncall_standardise(quote(mean(x = 1:10, , TRUE)))\n#> mean(x = 1:10, , TRUE)\nmean\n#> function (x, ...) \n#> UseMethod(\"mean\")\n#> \n#> \ncall_standardise(quote(mean.default(n = T, 1:10)))\n#> mean.default(x = 1:10, na.rm = T)\nx <- expr(foo(x = 1))\nnames(x) <- c(\"x\", \"y\")\nx <- expr(foo(x = 1))\nx\n#> foo(x = 1)\n\nnames(x) <- c(\"x\", \"y\")\nx\n#> foo(y = 1)\nx <- 5\ncall_obj1 <- call2(\">\", expr(x), 1)\ncall_obj1\n#> x > 1\n\ncall_obj2 <- call2(\"if\", cond = call_obj1, cons.expr = \"a\", alt.expr = \"b\")\ncall_obj2\n#> if (x > 1) \"a\" else \"b\"\nast(if (x > 1) \"a\" else \"b\")\n#> β–ˆβ”€`if` \n#> β”œβ”€β–ˆβ”€`>` \n#> β”‚ β”œβ”€x \n#> β”‚ └─1 \n#> β”œβ”€\"a\" \n#> └─\"b\""},{"path":"expressions.html","id":"parsing-and-grammar-exercises-18.4.4","chapter":"18 Expressions","heading":"18.3 Parsing and grammar (Exercises 18.4.4)","text":"Q1. R uses parentheses two slightly different ways illustrated two calls:Compare contrast two uses referencing AST.A1. Let’s first look AST:, can see ( used two separate ways:function right \"`(`\"part prefix syntax (f()), AST f((1)), see one \"`(`\" (first use case), f(), part function syntax (second use case).Q2. = can also used two ways. Construct simple example shows uses.A2. simple example illustrating = can also used two ways:assignmentfor named arguments function callsWe can also look AST:Q3. -2^2 yield 4 -4? ?A3. expression -2^2 evaluates -4 operator ^ higher precedence unary - operator:can also seen AST:less confusing way write :Q4. !1 + !1 return? ?A3. expression !1 + !1 evaluates FALSE.! operator higher precedence unary + operator. Thus, !1 evaluates FALSE, added 1 + FALSE, evaluates 1, logically negated !1, FALSE.can easily seen AST:Q5. x1 <- x2 <- x3 <- 0 work? Describe two reasons.A5. two reasons following works expected:<- operator right associative.Therefore, order assignment :<- operator invisibly returns assigned value.easy surmise AST:Q6. Compare ASTs x + y %+% z x ^ y %+% z. learned precedence custom infix functions?A6. Looking ASTs expressions,can say custom infix operator %+% :higher precedence + operatorlower precedence ^ operatorQ7. happens call parse_expr() string generates multiple expressions? e.g.Β parse_expr(\"x + 1; y + 1\")A7. produced error:expected based docs:parse_expr() returns one expression. text contains one expression (separated semicolons new lines), error issued.instead need use parse_exprs():Q8. happens attempt parse invalid expression? e.g.Β \"+\" \"f())\".A8. invalid expression produces error:Since underlying parse() function produces error:Q9. deparse() produces vectors input long. example, following call produces vector length two:expr_text() instead?A9. difference deparse() expr_text() latter turns (possibly multi-line) expression single string.Q10. pairwise.t.test() assumes deparse() always returns length one character vector. Can construct input violates expectation? happens?A10 Since R 4.0, possible violate expectation since new implementation produces single string matter input:New function deparse1() produces one string, wrapping deparse(), used typically deparse1(substitute(*))","code":"\nf((1))\n`(`(1 + 1)\nast(f((1)))\n#> β–ˆβ”€f \n#> β””β”€β–ˆβ”€`(` \n#> └─1\nast(`(`(1 + 1))\n#> β–ˆβ”€`(` \n#> β””β”€β–ˆβ”€`+` \n#> β”œβ”€1 \n#> └─1\nm <- mean(x = 1)\nast({\n m <- mean(x = 1)\n})\n#> β–ˆβ”€`{` \n#> β””β”€β–ˆβ”€`<-` \n#> β”œβ”€m \n#> β””β”€β–ˆβ”€mean \n#> └─x = 1\n-2^2\n#> [1] -4\nast(-2^2)\n#> β–ˆβ”€`-` \n#> β””β”€β–ˆβ”€`^` \n#> β”œβ”€2 \n#> └─2\n-(2^2)\n#> [1] -4\nast(!1 + !1)\n#> β–ˆβ”€`!` \n#> β””β”€β–ˆβ”€`+` \n#> β”œβ”€1 \n#> β””β”€β–ˆβ”€`!` \n#> └─1\nx1 <- x2 <- x3 <- 0\n(x3 <- 0)\n(x2 <- x3)\n(x1 <- x2)\n(x <- 1)\n#> [1] 1\nast(x1 <- x2 <- x3 <- 0)\n#> β–ˆβ”€`<-` \n#> β”œβ”€x1 \n#> β””β”€β–ˆβ”€`<-` \n#> β”œβ”€x2 \n#> β””β”€β–ˆβ”€`<-` \n#> β”œβ”€x3 \n#> └─0\nast(x + y %+% z)\n#> β–ˆβ”€`+` \n#> β”œβ”€x \n#> β””β”€β–ˆβ”€`%+%` \n#> β”œβ”€y \n#> └─z\n\nast(x^y %+% z)\n#> β–ˆβ”€`%+%` \n#> β”œβ”€β–ˆβ”€`^` \n#> β”‚ β”œβ”€x \n#> β”‚ └─y \n#> └─z\nparse_expr(\"x + 1; y + 1\")\n#> Error in `parse_expr()`:\n#> ! `x` must contain exactly 1 expression, not 2.\nparse_exprs(\"x + 1; y + 1\")\n#> [[1]]\n#> x + 1\n#> \n#> [[2]]\n#> y + 1\nparse_expr(\"a +\")\n#> Error in parse(text = elt): :2:0: unexpected end of input\n#> 1: a +\n#> ^\n\nparse_expr(\"f())\")\n#> Error in parse(text = elt): :1:4: unexpected ')'\n#> 1: f())\n#> ^\nparse(text = \"a +\")\n#> Error in parse(text = \"a +\"): :2:0: unexpected end of input\n#> 1: a +\n#> ^\n\nparse(text = \"f())\")\n#> Error in parse(text = \"f())\"): :1:4: unexpected ')'\n#> 1: f())\n#> ^\nexpr <- expr(g(a + b + c + d + e + f + g + h + i + j + k + l +\n m + n + o + p + q + r + s + t + u + v + w + x + y + z))\ndeparse(expr)\nexpr <- expr(g(a + b + c + d + e + f + g + h + i + j + k + l +\n m + n + o + p + q + r + s + t + u + v + w + x + y + z))\n\ndeparse(expr)\n#> [1] \"g(a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + \"\n#> [2] \" p + q + r + s + t + u + v + w + x + y + z)\"\n\nexpr_text(expr)\n#> [1] \"g(a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + \\n p + q + r + s + t + u + v + w + x + y + z)\""},{"path":"expressions.html","id":"walking-ast-with-recursive-functions-exercises-18.5.3","chapter":"18 Expressions","heading":"18.4 Walking AST with recursive functions (Exercises 18.5.3)","text":"Q1. logical_abbr() returns TRUE T(1, 2, 3). modify logical_abbr_rec() ignores function calls use T F?A1. avoid function calls use T F, just need ignore function position call objects:Let’s try :Q2. logical_abbr() works expressions. currently fails give function. ? modify logical_abbr() make work? components function need recurse ?A2. Surprisingly, logical_abbr() currently doesn’t fail closures:see , let’s see type object produced capture user provided closure:Given closures converted call objects, surprise function works:function fails can’t find negative case. example, instead returning FALSE, produces error reasons remain (yet) elusive :Q3. Modify find_assign also detect assignment using replacement functions, .e.Β names(x) <- y.A3. Although simple assignment (x <- y) assignment using replacement functions (names(x) <- y) <- operator call, latter case, names(x) call object symbol:’s can detect kind assignment checking second element expression symbol language type object.Let’s try :Q4. Write function extracts calls specified function.A4. function extracts calls specified function:","code":"\nlogical_abbr_rec(expr(T(1, 2, 3)))\n#> [1] FALSE\n\nlogical_abbr_rec(expr(F(1, 2, 3)))\n#> [1] FALSE\n\nlogical_abbr_rec(expr(T))\n#> [1] TRUE\n\nlogical_abbr_rec(expr(F))\n#> [1] TRUE\nlogical_abbr(function(x = TRUE) {\n g(x + T)\n})\nprint_enexpr <- function(.f) {\n print(typeof(enexpr(.f)))\n print(is.call(enexpr(.f)))\n}\n\nprint_enexpr(function(x = TRUE) {\n g(x + T)\n})\n#> [1] \"language\"\n#> [1] TRUE\nlogical_abbr(function(x = TRUE) {\n g(x + T)\n})\n#> [1] TRUE\nlogical_abbr(function(x = TRUE) {\n g(x + TRUE)\n})\n#> Error: Don't know how to handle type integer\nexpr1 <- expr(names(x) <- y)\nas.list(expr1)\n#> [[1]]\n#> `<-`\n#> \n#> [[2]]\n#> names(x)\n#> \n#> [[3]]\n#> y\ntypeof(expr1[[2]])\n#> [1] \"language\"\n\nexpr2 <- expr(x <- y)\nas.list(expr2)\n#> [[1]]\n#> `<-`\n#> \n#> [[2]]\n#> x\n#> \n#> [[3]]\n#> y\ntypeof(expr2[[2]])\n#> [1] \"symbol\"\nexpr_type <- function(x) {\n if (is_syntactic_literal(x)) {\n \"constant\"\n } else if (is.symbol(x)) {\n \"symbol\"\n } else if (is.call(x)) {\n \"call\"\n } else if (is.pairlist(x)) {\n \"pairlist\"\n } else {\n typeof(x)\n }\n}\n\nswitch_expr <- function(x, ...) {\n switch(expr_type(x),\n ...,\n stop(\"Don't know how to handle type \", typeof(x), call. = FALSE)\n )\n}\n\nflat_map_chr <- function(.x, .f, ...) {\n purrr::flatten_chr(purrr::map(.x, .f, ...))\n}\n\nextract_symbol <- function(x) {\n if (is_symbol(x[[2]])) {\n as_string(x[[2]])\n } else {\n extract_symbol(as.list(x[[2]]))\n }\n}\n\nfind_assign_call <- function(x) {\n if (is_call(x, \"<-\") && is_symbol(x[[2]])) {\n lhs <- as_string(x[[2]])\n children <- as.list(x)[-1]\n } else if (is_call(x, \"<-\") && is_call(x[[2]])) {\n lhs <- extract_symbol(as.list(x[[2]]))\n children <- as.list(x)[-1]\n } else {\n lhs <- character()\n children <- as.list(x)\n }\n\n c(lhs, flat_map_chr(children, find_assign_rec))\n}\n\nfind_assign_rec <- function(x) {\n switch_expr(x,\n # Base cases\n constant = ,\n symbol = character(),\n\n # Recursive cases\n pairlist = flat_map_chr(x, find_assign_rec),\n call = find_assign_call(x)\n )\n}\n\nfind_assign <- function(x) find_assign_rec(enexpr(x))\nfind_assign(names(x))\n#> character(0)\n\nfind_assign(names(x) <- y)\n#> [1] \"x\"\n\nfind_assign(names(f(x)) <- y)\n#> [1] \"x\"\n\nfind_assign(names(x) <- y <- z <- NULL)\n#> [1] \"x\" \"y\" \"z\"\n\nfind_assign(a <- b <- c <- 1)\n#> [1] \"a\" \"b\" \"c\"\n\nfind_assign(system.time(x <- print(y <- 5)))\n#> [1] \"x\" \"y\"\nfind_function_call <- function(x, .f) {\n if (is_call(x)) {\n if (is_call(x, .f)) {\n list(x)\n } else {\n purrr::map(as.list(x), ~ find_function_call(.x, .f)) %>%\n purrr::compact() %>%\n unlist(use.names = FALSE)\n }\n }\n}\n\n# example-1: with infix operator `:`\nfind_function_call(expr(mean(1:2)), \":\")\n#> [[1]]\n#> 1:2\n\nfind_function_call(expr(sum(mean(1:2))), \":\")\n#> [[1]]\n#> 1:2\n\nfind_function_call(expr(list(1:5, 4:6, 3:9)), \":\")\n#> [[1]]\n#> 1:5\n#> \n#> [[2]]\n#> 4:6\n#> \n#> [[3]]\n#> 3:9\n\nfind_function_call(expr(list(1:5, sum(4:6), mean(3:9))), \":\")\n#> [[1]]\n#> 1:5\n#> \n#> [[2]]\n#> 4:6\n#> \n#> [[3]]\n#> 3:9\n\n# example-2: with assignment operator `<-`\nfind_function_call(expr(names(x)), \"<-\")\n#> NULL\n\nfind_function_call(expr(names(x) <- y), \"<-\")\n#> [[1]]\n#> names(x) <- y\n\nfind_function_call(expr(names(f(x)) <- y), \"<-\")\n#> [[1]]\n#> names(f(x)) <- y\n\nfind_function_call(expr(names(x) <- y <- z <- NULL), \"<-\")\n#> [[1]]\n#> names(x) <- y <- z <- NULL\n\nfind_function_call(expr(a <- b <- c <- 1), \"<-\")\n#> [[1]]\n#> a <- b <- c <- 1\n\nfind_function_call(expr(system.time(x <- print(y <- 5))), \"<-\")\n#> [[1]]\n#> x <- print(y <- 5)"},{"path":"expressions.html","id":"session-information-13","chapter":"18 Expressions","heading":"18.5 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1)\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> lobstr * 1.1.2 2022-06-22 [1] CRAN (R 4.2.0)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"quasiquotation.html","id":"quasiquotation","chapter":"19 Quasiquotation","heading":"19 Quasiquotation","text":"Attaching needed libraries:","code":"\nlibrary(rlang)\nlibrary(purrr)\nlibrary(lobstr)\nlibrary(dplyr)\nlibrary(ggplot2)"},{"path":"quasiquotation.html","id":"motivation-exercises-19.2.2","chapter":"19 Quasiquotation","heading":"19.1 Motivation (Exercises 19.2.2)","text":"Q1. function following base R code, identify arguments quoted evaluated.A1. identify arguments quoted evaluated, can use trick mentioned book:’re ever unsure whether argument quoted evaluated, try executing code outside function. doesn’t work something different, argument quoted.library(MASS)package argument library() quoted:subset(mtcars, cyl == 4)argument x evaluated, argument subset quoted.(mtcars2, sum(vs))argument data evaluated, expr argument quoted.sum(mtcars2$)argument ... evaluated.rm(mtcars2)trick using far won’t work since trying print mtcars2 always fail rm() made pass .can instead look docs ...:… objects removed, names (unquoted) character strings (quoted).Thus, argument evaluated, rather quoted.Q2. function following tidyverse code, identify arguments quoted evaluated.A2. seen answer Q1., library() quotes first argument:following code:%>% (lazily) evaluates argumentgroup_by() summarise() quote argumentsIn following code:ggplot() evaluates data argumentaes() quotes arguments","code":"\nlibrary(MASS)\n\nmtcars2 <- subset(mtcars, cyl == 4)\n\nwith(mtcars2, sum(vs))\nsum(mtcars2$am)\n\nrm(mtcars2)\nlibrary(MASS)\n\nMASS\n#> Error in eval(expr, envir, enclos): object 'MASS' not found\nmtcars2 <- subset(mtcars, cyl == 4)\n\ninvisible(mtcars)\n\ncyl == 4\n#> Error in eval(expr, envir, enclos): object 'cyl' not found\nwith(mtcars2, sum(vs))\n#> [1] 10\n\ninvisible(mtcars2)\n\nsum(vs)\n#> Error in eval(expr, envir, enclos): object 'vs' not found\nsum(mtcars2$am)\n#> [1] 8\n\nmtcars2$am\n#> [1] 1 0 0 1 1 1 0 1 1 1 1\nrm(mtcars2)\nlibrary(dplyr)\nlibrary(ggplot2)\n\nby_cyl <- mtcars %>%\n group_by(cyl) %>%\n summarise(mean = mean(mpg))\n\nggplot(by_cyl, aes(cyl, mean)) +\n geom_point()\nlibrary(dplyr)\nlibrary(ggplot2)\nby_cyl <- mtcars %>%\n group_by(cyl) %>%\n summarise(mean = mean(mpg))\nggplot(by_cyl, aes(cyl, mean)) +\n geom_point()"},{"path":"quasiquotation.html","id":"quoting-exercises-19.3.6","chapter":"19 Quasiquotation","heading":"19.2 Quoting (Exercises 19.3.6)","text":"Q1. expr() implemented? Look source code.A1. Looking source code, can see expr() simple wrapper around enexpr(), captures returns user-entered expressions:example:turn, enexpr() calls native code:Q2. Compare contrast following two functions. Can predict output running ?A2. exprs() captures returns expressions specified developer instead values:hand, enexprs() captures user-entered expressions returns values:Q3. happens try use enexpr() expression (.e.Β enexpr(x + y)? happens enexpr() passed missing argument?A3. try use enexpr() expression, fails works symbol.enexpr() passed missing argument, returns missing argument:Q4. exprs() exprs(= ) different? Think input output.A4. key difference exprs() exprs(= ) former return unnamed list, latter return named list. former interpreted unnamed argument, latter named argument.cases, treated symbol:, argument missing latter case, since name corresponding value provided:Q5. differences exprs() alist()? Read documentation named arguments exprs() find .A5. additional differences exprs() alist().Names: inputs named, exprs() provides way name automatically using .named argument.Ignoring empty arguments: .ignore_empty argument exprs() gives much finer control empty arguments, alist() doesn’t provide way ignore arguments.Names injection: Using .unquote_names argument exprs(), can inject name argument.Q6. documentation substitute() says:Substitution takes place examining component parse tree\nfollows:bound symbol env, unchanged.promise object (.e., formal argument function)\nexpression slot promise replaces symbol.ordinary variable, value substituted, unless\nenv .GlobalEnv case symbol left unchanged.Create examples illustrate cases.A6. See examples illustrate -mentioned cases.bound symbol env, unchanged.Symbol x bound env, remains unchanged.promise object (.e., formal argument function)\nexpression slot promise replaces symbol.ordinary variable, value substituted, unless\nenv .GlobalEnv case symbol left unchanged.","code":"\nrlang::expr\n#> function (expr) \n#> {\n#> enexpr(expr)\n#> }\n#> \n#> \nx <- expr(x <- 1)\nx\n#> x <- 1\nrlang::enexpr\n#> function (arg) \n#> {\n#> .Call(ffi_enexpr, substitute(arg), parent.frame())\n#> }\n#> \n#> \nf1 <- function(x, y) {\n exprs(x = x, y = y)\n}\nf2 <- function(x, y) {\n enexprs(x = x, y = y)\n}\nf1(a + b, c + d)\nf2(a + b, c + d)\nf1 <- function(x, y) {\n exprs(x = x, y = y)\n}\n\nf1(a + b, c + d)\n#> $x\n#> x\n#> \n#> $y\n#> y\nf2 <- function(x, y) {\n enexprs(x = x, y = y)\n}\n\nf2(a + b, c + d)\n#> $x\n#> a + b\n#> \n#> $y\n#> c + d\nenexpr(x + y)\n#> Error in `enexpr()`:\n#> ! `arg` must be a symbol\narg <- missing_arg()\n\nenexpr(arg)\n\nis_missing(enexpr(arg))\n#> [1] TRUE\nexprs(a)\n#> [[1]]\n#> a\n\nexprs(a = )\n#> $a\nmap_lgl(exprs(a), is_symbol)\n#> \n#> TRUE\n\nmap_lgl(exprs(a = ), is_symbol)\n#> a \n#> TRUE\nmap_lgl(exprs(a), is_missing)\n#> \n#> FALSE\n\nmap_lgl(exprs(a = ), is_missing)\n#> a \n#> TRUE\nalist(\"x\" = 1, TRUE, \"z\" = expr(x + y))\n#> $x\n#> [1] 1\n#> \n#> [[2]]\n#> [1] TRUE\n#> \n#> $z\n#> expr(x + y)\n\nexprs(\"x\" = 1, TRUE, \"z\" = expr(x + y), .named = TRUE)\n#> $x\n#> [1] 1\n#> \n#> $`TRUE`\n#> [1] TRUE\n#> \n#> $z\n#> expr(x + y)\nalist(\"x\" = 1, , TRUE, )\n#> $x\n#> [1] 1\n#> \n#> [[2]]\n#> \n#> \n#> [[3]]\n#> [1] TRUE\n#> \n#> [[4]]\n\nexprs(\"x\" = 1, , TRUE, , .ignore_empty = \"trailing\")\n#> $x\n#> [1] 1\n#> \n#> [[2]]\n#> \n#> \n#> [[3]]\n#> [1] TRUE\n\nexprs(\"x\" = 1, , TRUE, , .ignore_empty = \"none\")\n#> $x\n#> [1] 1\n#> \n#> [[2]]\n#> \n#> \n#> [[3]]\n#> [1] TRUE\n#> \n#> [[4]]\n\nexprs(\"x\" = 1, , TRUE, , .ignore_empty = \"all\")\n#> $x\n#> [1] 1\n#> \n#> [[2]]\n#> [1] TRUE\nalist(foo := bar)\n#> [[1]]\n#> `:=`(foo, bar)\n\nexprs(foo := bar, .unquote_names = FALSE)\n#> [[1]]\n#> `:=`(foo, bar)\n\nexprs(foo := bar, .unquote_names = TRUE)\n#> $foo\n#> bar\nsubstitute(x + y, env = list(y = 2))\n#> x + 2\nmsg <- \"old\"\ndelayedAssign(\"myVar\", msg) # creates a promise\nsubstitute(myVar)\n#> myVar\nmsg <- \"new!\"\nmyVar\n#> [1] \"new!\"\nsubstitute(x + y, env = env(x = 2, y = 1))\n#> 2 + 1\n\nx <- 2\ny <- 1\nsubstitute(x + y, env = .GlobalEnv)\n#> x + y"},{"path":"quasiquotation.html","id":"unquoting-exercises-19.4.8","chapter":"19 Quasiquotation","heading":"19.3 Unquoting (Exercises 19.4.8)","text":"Q1. Given following components:Use quasiquotation construct following calls:A1. Using quasiquotation construct specified calls:Q2. following two calls print , actually different:’s difference? one natural?A2. can see difference two expression convert lists:can seen, second element call object, b integer vector:can also noticed ASTs expressions:first call natural, since second one inlines vector directly call, something rarely done.","code":"\nxy <- expr(x + y)\nxz <- expr(x + z)\nyz <- expr(y + z)\nabc <- exprs(a, b, c)\n(x + y) / (y + z)\n-(x + z)^(y + z)\n(x + y) + (y + z) - (x + y)\natan2(x + y, y + z)\nsum(x + y, x + y, y + z)\nsum(a, b, c)\nmean(c(a, b, c), na.rm = TRUE)\nfoo(a = x + y, b = y + z)\nxy <- expr(x + y)\nxz <- expr(x + z)\nyz <- expr(y + z)\nabc <- exprs(a, b, c)\n\nexpr((!!xy) / (!!yz))\n#> (x + y)/(y + z)\n\nexpr(-(!!xz)^(!!yz))\n#> -(x + z)^(y + z)\n\nexpr(((!!xy)) + (!!yz) - (!!xy))\n#> (x + y) + (y + z) - (x + y)\n\ncall2(\"atan2\", expr(!!xy), expr(!!yz))\n#> atan2(x + y, y + z)\n\ncall2(\"sum\", expr(!!xy), expr(!!xy), expr(!!yz))\n#> sum(x + y, x + y, y + z)\n\ncall2(\"sum\", !!!abc)\n#> sum(a, b, c)\n\nexpr(mean(c(!!!abc), na.rm = TRUE))\n#> mean(c(a, b, c), na.rm = TRUE)\n\ncall2(\"foo\", a = expr(!!xy), b = expr(!!yz))\n#> foo(a = x + y, b = y + z)\n(a <- expr(mean(1:10)))\n#> mean(1:10)\n(b <- expr(mean(!!(1:10))))\n#> mean(1:10)\nidentical(a, b)\n#> [1] FALSE\nas.list(expr(mean(1:10)))\n#> [[1]]\n#> mean\n#> \n#> [[2]]\n#> 1:10\n\nas.list(expr(mean(!!(1:10))))\n#> [[1]]\n#> mean\n#> \n#> [[2]]\n#> [1] 1 2 3 4 5 6 7 8 9 10\nwaldo::compare(a, b)\n#> `old[[2]]` is a call\n#> `new[[2]]` is an integer vector (1, 2, 3, 4, 5, ...)\nast(expr(mean(1:10)))\n#> β–ˆβ”€expr \n#> β””β”€β–ˆβ”€mean \n#> β””β”€β–ˆβ”€`:` \n#> β”œβ”€1 \n#> └─10\n\nast(expr(mean(!!(1:10))))\n#> β–ˆβ”€expr \n#> β””β”€β–ˆβ”€mean \n#> └─"},{"path":"quasiquotation.html","id":"dot-dot-dot-exercises-19.6.5","chapter":"19 Quasiquotation","heading":"19.4 ... (dot-dot-dot) (Exercises 19.6.5)","text":"Q1. One way implement exec() shown . Describe works. key ideas?A1. keys ideas underlie implementation exec() function following:constructs call using function f argument ..., evaluates call environment .env.constructs call using function f argument ..., evaluates call environment .env.uses dynamic dots via list2(), means can splice arguments using !!!, can inject names using :=, trailing commas problem.uses dynamic dots via list2(), means can splice arguments using !!!, can inject names using :=, trailing commas problem.example:Q2. Carefully read source code interaction(), expand.grid(), par(). Compare contrast techniques use switching dots list behaviour.A2. Source code reveals following comparison table:functions capture dots list.Using dots, functions check:list entered argument checking number argumentsif count 1, checking argument listQ3. Explain problem definition set_attr()A3. set_attr() function signature parameter called x, additionally uses dynamic dots pass multiple arguments specify additional attributes x., shown example, creates problem attribute named x. Naming arguments won’t help either:can avoid issues renaming parameter:","code":"\nexec <- function(f, ..., .env = caller_env()) {\n args <- list2(...)\n do.call(f, args, envir = .env)\n}\nvec <- c(1:5, NA)\nargs_list <- list(trim = 0, na.rm = TRUE)\n\nexec(mean, vec, !!!args_list, , .env = caller_env())\n#> [1] 3\n\nrm(\"exec\")\nset_attr <- function(x, ...) {\n attr <- rlang::list2(...)\n attributes(x) <- attr\n x\n}\nset_attr(1:10, x = 10)\n#> Error in attributes(x) <- attr: attributes must be named\nset_attr <- function(x, ...) {\n attr <- rlang::list2(...)\n attributes(x) <- attr\n x\n}\nset_attr(x = 1:10, x = 10)\n#> Error in set_attr(x = 1:10, x = 10): formal argument \"x\" matched by multiple actual arguments\nset_attr <- function(.x, ...) {\n attr <- rlang::list2(...)\n attributes(.x) <- attr\n .x\n}\n\nset_attr(.x = 1:10, x = 10)\n#> [1] 1 2 3 4 5 6 7 8 9 10\n#> attr(,\"x\")\n#> [1] 10"},{"path":"quasiquotation.html","id":"case-studies-exercises-19.7.5","chapter":"19 Quasiquotation","heading":"19.5 Case studies (Exercises 19.7.5)","text":"Q1. linear-model example, replace expr() reduce(summands, ~ expr(!!.x + !!.y)) call2(): reduce(summands, call2, \"+\"). Compare contrast two approaches. think easier read?A1. can rewrite linear() function chapter using call2() follows:personally find version call2() much readable since !! syntax bit esoteric.Q2. Re-implement Box-Cox transform defined using unquoting new_function():A2. Re-implementation Box-Cox transform using unquoting new_function():Let’s try see produces output :Q3. Re-implement simple compose() defined using quasiquotation new_function():A3. Following re-implementation compose() using quasiquotation new_function():Checking new version behaves way original version:","code":"\nlinear <- function(var, val) {\n var <- ensym(var)\n coef_name <- map(seq_along(val[-1]), ~ expr((!!var)[[!!.x]]))\n\n summands <- map2(val[-1], coef_name, ~ expr((!!.x * !!.y)))\n summands <- c(val[[1]], summands)\n\n reduce(summands, ~ call2(\"+\", .x, .y))\n}\n\nlinear(x, c(10, 5, -4))\n#> 10 + (5 * x[[1L]]) + (-4 * x[[2L]])\nbc <- function(lambda) {\n if (lambda == 0) {\n function(x) log(x)\n } else {\n function(x) (x^lambda - 1) / lambda\n }\n}\nbc_new <- function(lambda) {\n lambda <- enexpr(lambda)\n\n if (!!lambda == 0) {\n new_function(\n exprs(x = ),\n expr(log(x))\n )\n } else {\n new_function(\n exprs(x = ),\n expr((x^(!!lambda) - 1) / (!!lambda))\n )\n }\n}\nbc(0)(1)\n#> [1] 0\nbc_new(0)(1)\n#> [1] 0\n\nbc(2)(2)\n#> [1] 1.5\nbc_new(2)(2)\n#> [1] 1.5\ncompose <- function(f, g) {\n function(...) f(g(...))\n}\ncompose_new <- function(f, g) {\n f <- enexpr(f) # or ensym(f)\n g <- enexpr(g) # or ensym(g)\n\n new_function(\n exprs(... = ),\n expr((!!f)((!!g)(...)))\n )\n}\nnot_null <- compose(`!`, is.null)\nnot_null(4)\n#> [1] TRUE\n\nnot_null2 <- compose_new(`!`, is.null)\nnot_null2(4)\n#> [1] TRUE"},{"path":"quasiquotation.html","id":"session-information-14","chapter":"19 Quasiquotation","heading":"19.6 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> crayon 1.5.2 2022-09-29 [1] CRAN (R 4.2.1)\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> DBI 1.1.3.9002 2022-10-17 [1] Github (r-dbi/DBI@2aec388)\n#> diffobj 0.3.5 2021-10-05 [1] CRAN (R 4.2.0)\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> farver 2.1.1 2022-07-06 [1] CRAN (R 4.2.1)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.1)\n#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.2)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> P grid 4.2.2 2022-10-31 [1] local\n#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.1)\n#> highr 0.9 2021-04-16 [1] CRAN (R 4.2.0)\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> labeling 0.4.2 2020-10-20 [1] CRAN (R 4.2.0)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> lobstr * 1.1.2 2022-06-22 [1] CRAN (R 4.2.0)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> MASS * 7.3-58.1 2022-08-03 [1] CRAN (R 4.2.2)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0)\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)\n#> purrr * 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rematch2 2.1.2 2020-05-01 [1] CRAN (R 4.2.0)\n#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> tibble 3.1.8.9002 2022-10-16 [1] local\n#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> waldo 0.4.0 2022-03-16 [1] CRAN (R 4.2.0)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"evaluation.html","id":"evaluation","chapter":"20 Evaluation","heading":"20 Evaluation","text":"Attaching needed libraries:","code":"\nlibrary(rlang)"},{"path":"evaluation.html","id":"evaluation-basics-exercises-20.2.4","chapter":"20 Evaluation","heading":"20.1 Evaluation basics (Exercises 20.2.4)","text":"Q1. Carefully read documentation source(). environment use default? supply local = TRUE? provide custom environment?A1. parameter local source() decides environment parsed expressions evaluated.default local = FALSE, corresponds user’s workspace (global environment, .e.).local = TRUE, environment source() called used.specify custom environment, sys.source() function can used, provides envir parameter.Q2. Predict results following lines code:A2. Correctly predicted πŸ˜‰Q3. Fill function bodies re-implement get() using sym() eval(), assign() using sym(), expr(), eval(). Don’t worry multiple ways choosing environment get() assign() support; assume user supplies explicitly.A3. required re-implementations:get()assign()Q4. Modify source2() returns result every expression, just last one. Can eliminate loop?A4. can use purrr::map() iterate every expression return result every expression:Q5. can make base::local() slightly easier understand spreading multiple lines:Explain local() works words. (Hint: might want print(call) help understand substitute() , read documentation remind environment new.env() inherit .)A5. order figure function works, let’s add suggested print(call):docs substitute() mention:Substituting quoting often cause confusion argument expression(…). result call expression constructor function needs evaluated eval give actual expression object.Thus, get actual expression object, quoted expression needs evaluated using eval():Finally, generated call evaluated caller environment. final function call looks like following:Note bindings x y found inner environment, bindings functions eval(), quote(), etc. found outer environment.","code":"\nwithr::with_tempdir(\n code = {\n f <- tempfile()\n writeLines(\"rlang::env_print()\", f)\n foo <- function() source(f, local = FALSE)\n foo()\n }\n)\n#> \n#> Parent: \n#> Bindings:\n#> β€’ .Random.seed: \n#> β€’ foo: \n#> β€’ f: \nwithr::with_tempdir(\n code = {\n f <- tempfile()\n writeLines(\"rlang::env_print()\", f)\n foo <- function() source(f, local = TRUE)\n foo()\n }\n)\n#> \n#> Parent: \neval(expr(eval(expr(eval(expr(2 + 2))))))\neval(eval(expr(eval(expr(eval(expr(2 + 2)))))))\nexpr(eval(expr(eval(expr(eval(expr(2 + 2)))))))\neval(expr(eval(expr(eval(expr(2 + 2))))))\n#> [1] 4\n\neval(eval(expr(eval(expr(eval(expr(2 + 2)))))))\n#> [1] 4\n\nexpr(eval(expr(eval(expr(eval(expr(2 + 2)))))))\n#> eval(expr(eval(expr(eval(expr(2 + 2))))))\n# name is a string\nget2 <- function(name, env) {}\nassign2 <- function(name, value, env) {}\nget2 <- function(name, env = caller_env()) {\n name <- sym(name)\n eval(name, env)\n}\n\nx <- 2\n\nget2(\"x\")\n#> [1] 2\nget(\"x\")\n#> [1] 2\n\ny <- 1:4\nassign(\"y[1]\", 2)\n\nget2(\"y[1]\")\n#> [1] 2\nget(\"y[1]\")\n#> [1] 2\nassign2 <- function(name, value, env = caller_env()) {\n name <- sym(name)\n eval(expr(!!name <- !!value), env)\n}\n\nassign(\"y1\", 4)\ny1\n#> [1] 4\n\nassign2(\"y2\", 4)\ny2\n#> [1] 4\nsource2 <- function(path, env = caller_env()) {\n file <- paste(readLines(path, warn = FALSE), collapse = \"\\n\")\n exprs <- parse_exprs(file)\n purrr::map(exprs, ~ eval(.x, env))\n}\n\nwithr::with_tempdir(\n code = {\n f <- tempfile(fileext = \".R\")\n writeLines(\"1 + 1; 2 + 4\", f)\n source2(f)\n }\n)\n#> [[1]]\n#> [1] 2\n#> \n#> [[2]]\n#> [1] 6\nlocal3 <- function(expr, envir = new.env()) {\n call <- substitute(eval(quote(expr), envir))\n eval(call, envir = parent.frame())\n}\nlocal3 <- function(expr, envir = new.env()) {\n call <- substitute(eval(quote(expr), envir))\n print(call)\n\n eval(call, envir = parent.frame())\n}\n\nlocal3({\n x <- 10\n y <- 200\n x + y\n})\n#> eval(quote({\n#> x <- 10\n#> y <- 200\n#> x + y\n#> }), new.env())\n#> [1] 210\nis_expression(eval(quote({\n x <- 10\n y <- 200\n x + y\n}), new.env()))\n#> [1] TRUE\n# outer environment\neval(\n # inner environment\n eval(quote({\n x <- 10\n y <- 200\n x + y\n }), new.env()),\n envir = parent.frame()\n)"},{"path":"evaluation.html","id":"quosures-exercises-20.3.6","chapter":"20 Evaluation","heading":"20.2 Quosures (Exercises 20.3.6)","text":"Q1. Predict following quosures return evaluated.A1. Correctly predicted πŸ˜‰Q2. Write enenv() function captures environment associated argument. (Hint: require two function calls.)A2. can make use get_env() helper get environment associated argument:","code":"\nq1 <- new_quosure(expr(x), env(x = 1))\nq1\n#> \n#> expr: ^x\n#> env: 0x103b6fd78\nq2 <- new_quosure(expr(x + !!q1), env(x = 10))\nq2\n#> \n#> expr: ^x + (^x)\n#> env: 0x14af4ada0\nq3 <- new_quosure(expr(x + !!q2), env(x = 100))\nq3\n#> \n#> expr: ^x + (^x + (^x))\n#> env: 0x103f88b78\nq1 <- new_quosure(expr(x), env(x = 1))\neval_tidy(q1)\n#> [1] 1\n\nq2 <- new_quosure(expr(x + !!q1), env(x = 10))\neval_tidy(q2)\n#> [1] 11\n\nq3 <- new_quosure(expr(x + !!q2), env(x = 100))\neval_tidy(q3)\n#> [1] 111\nenenv <- function(x) {\n x <- enquo(x)\n get_env(x)\n}\n\nenenv(x)\n#> \n\nfoo <- function(x) enenv(x)\nfoo()\n#> "},{"path":"evaluation.html","id":"data-masks-exercises-20.4.6","chapter":"20 Evaluation","heading":"20.3 Data masks (Exercises 20.4.6)","text":"Q1. use loop transform2() instead map()? Consider transform2(df, x = x * 2, x = x * 2).A1. see map() appropriate function, let’s create version function map() see happens.use () loop, iteration, updating x column current expression evaluation. , repeatedly modifying column works.use map() instead, trying evaluate expressions time; .e., column attempted modify using multiple expressions.Q2. ’s alternative implementation subset2():Compare contrast subset3() subset2(). advantages disadvantages?A2. Let’s first juxtapose functions outputs can compare better.Disadvantages subset3() subset2()filtering conditions specified rows don’t evaluate logical, function doesn’t fail informatively. Indeed, silently returns incorrect result.Advantages subset3() subset2()might argue function shorter advantage, much subjective preference.Q3. following function implements basics dplyr::arrange(). Annotate line comment explaining . Can explain !!.na.last strictly correct, omitting !! unlikely cause problems?A3. Annotated version function:see doesn’t matter whether whether unquote .na.last argument , let’s look smaller example:can seen:without unquoting, .na.last found function environmentwith unquoting, .na.last included order call object ","code":"\ntransform2 <- function(.data, ...) {\n dots <- enquos(...)\n\n for (i in seq_along(dots)) {\n name <- names(dots)[[i]]\n dot <- dots[[i]]\n\n .data[[name]] <- eval_tidy(dot, .data)\n }\n\n .data\n}\n\ntransform3 <- function(.data, ...) {\n dots <- enquos(...)\n\n purrr::map(dots, function(x, .data = .data) {\n name <- names(x)\n dot <- x\n\n .data[[name]] <- eval_tidy(dot, .data)\n\n .data\n })\n}\ndf <- data.frame(x = 1:3)\ntransform2(df, x = x * 2, x = x * 2)\n#> x\n#> 1 4\n#> 2 8\n#> 3 12\ndf <- data.frame(x = 1:3)\ntransform3(df, x = x * 2, x = x * 2)\n#> Error in eval_tidy(dot, .data): promise already under evaluation: recursive default argument reference or earlier problems?\nsubset3 <- function(data, rows) {\n rows <- enquo(rows)\n eval_tidy(expr(data[!!rows, , drop = FALSE]), data = data)\n}\ndf <- data.frame(x = 1:3)\nsubset3(df, x == 1)\nsubset2 <- function(data, rows) {\n rows <- enquo(rows)\n rows_val <- eval_tidy(rows, data)\n stopifnot(is.logical(rows_val))\n\n data[rows_val, , drop = FALSE]\n}\n\ndf <- data.frame(x = 1:3)\nsubset2(df, x == 1)\n#> x\n#> 1 1\nsubset3 <- function(data, rows) {\n rows <- enquo(rows)\n eval_tidy(expr(data[!!rows, , drop = FALSE]), data = data)\n}\n\nsubset3(df, x == 1)\n#> x\n#> 1 1\nrm(\"x\")\nexists(\"x\")\n#> [1] FALSE\n\nsubset2(df, x + 1)\n#> Error in subset2(df, x + 1): is.logical(rows_val) is not TRUE\n\nsubset3(df, x + 1)\n#> x\n#> 2 2\n#> 3 3\n#> NA NA\narrange2 <- function(.df, ..., .na.last = TRUE) {\n args <- enquos(...)\n order_call <- expr(order(!!!args, na.last = !!.na.last))\n ord <- eval_tidy(order_call, .df)\n stopifnot(length(ord) == nrow(.df))\n .df[ord, , drop = FALSE]\n}\narrange2 <- function(.df, ..., .na.last = TRUE) {\n # capture user-supplied expressions (and corresponding environments) as quosures\n args <- enquos(...)\n\n # create a call object by splicing a list of quosures\n order_call <- expr(order(!!!args, na.last = !!.na.last))\n\n # and evaluate the constructed call in the data frame\n ord <- eval_tidy(order_call, .df)\n\n # sanity check\n stopifnot(length(ord) == nrow(.df))\n\n .df[ord, , drop = FALSE]\n}\nx <- TRUE\neval(expr(c(x = !!x)))\n#> x \n#> TRUE\neval(expr(c(x = x)))\n#> x \n#> TRUE"},{"path":"evaluation.html","id":"using-tidy-evaluation-exercises-20.5.4","chapter":"20 Evaluation","heading":"20.4 Using tidy evaluation (Exercises 20.5.4)","text":"Q1. ’ve included alternative implementation threshold_var() . makes different approach used ? makes harder?A1. First, let’s compare two definitions function make sure produce output:key difference subsetting operator used:old version uses non-quoting [[ operator. Thus, var argument first needs converted string.new version uses quoting $ operator. Thus, var argument first quoted unquoted (using !!).","code":"\nthreshold_var <- function(df, var, val) {\n var <- ensym(var)\n subset2(df, `$`(.data, !!var) >= !!val)\n}\nthreshold_var_old <- function(df, var, val) {\n var <- as_string(ensym(var))\n subset2(df, .data[[var]] >= !!val)\n}\n\nthreshold_var_new <- threshold_var\n\ndf <- data.frame(x = 1:10)\n\nidentical(\n threshold_var(df, x, 8),\n threshold_var(df, x, 8)\n)\n#> [1] TRUE"},{"path":"evaluation.html","id":"base-evaluation-exercises-20.6.3","chapter":"20 Evaluation","heading":"20.5 Base evaluation (Exercises 20.6.3)","text":"Q1. function fail?A1. doesn’t work lm_call call evaluated caller_env(), finds binding base::data() function, data execution environment.make work, need unquote data expression:Q2. model building, typically response data relatively constant rapidly experiment different predictors. Write small wrapper allows reduce duplication code .A2. small wrapper allows enter predictors:function flexible enough also allow changing data dependent variable:Q3. Another way write resample_lm() include resample expression (data[sample(nrow(data), replace = TRUE), , drop = FALSE]) data argument. Implement approach. advantages? disadvantages?A3. variant resample_lm(), providing resampled data argument.makes use R’s lazy evaluation function arguments. , resample_data argument evaluated needed function.","code":"\nlm3a <- function(formula, data) {\n formula <- enexpr(formula)\n lm_call <- expr(lm(!!formula, data = data))\n eval(lm_call, caller_env())\n}\n\nlm3a(mpg ~ disp, mtcars)$call\n#> Error in as.data.frame.default(data, optional = TRUE):\n#> cannot coerce class β€˜\"function\"’ to a data.frame\nlm3a <- function(formula, data) {\n formula <- enexpr(formula)\n lm_call <- expr(lm(!!formula, data = !!data))\n eval(lm_call, caller_env())\n}\n\nis_call(lm3a(mpg ~ disp, mtcars)$call)\n#> [1] TRUE\nlm(mpg ~ disp, data = mtcars)\nlm(mpg ~ I(1 / disp), data = mtcars)\nlm(mpg ~ disp * cyl, data = mtcars)\nlm_custom <- function(data = mtcars, x, y = mpg) {\n x <- enexpr(x)\n y <- enexpr(y)\n data <- enexpr(data)\n\n lm_call <- expr(lm(formula = !!y ~ !!x, data = !!data))\n\n eval(lm_call, caller_env())\n}\n\nidentical(\n lm_custom(x = disp),\n lm(mpg ~ disp, data = mtcars)\n)\n#> [1] TRUE\n\nidentical(\n lm_custom(x = I(1 / disp)),\n lm(mpg ~ I(1 / disp), data = mtcars)\n)\n#> [1] TRUE\n\nidentical(\n lm_custom(x = disp * cyl),\n lm(mpg ~ disp * cyl, data = mtcars)\n)\n#> [1] TRUE\nlm_custom(data = iris, x = Sepal.Length, y = Petal.Width)\n#> \n#> Call:\n#> lm(formula = Petal.Width ~ Sepal.Length, data = iris)\n#> \n#> Coefficients:\n#> (Intercept) Sepal.Length \n#> -3.2002 0.7529\nresample_lm3 <- function(formula,\n data,\n resample_data = data[sample(nrow(data), replace = TRUE), , drop = FALSE],\n env = current_env()) {\n formula <- enexpr(formula)\n lm_call <- expr(lm(!!formula, data = resample_data))\n expr_print(lm_call)\n eval(lm_call, env)\n}\n\ndf <- data.frame(x = 1:10, y = 5 + 3 * (1:10) + round(rnorm(10), 2))\nresample_lm3(y ~ x, data = df)\n#> lm(y ~ x, data = resample_data)\n#> \n#> Call:\n#> lm(formula = y ~ x, data = resample_data)\n#> \n#> Coefficients:\n#> (Intercept) x \n#> 2.654 3.420"},{"path":"evaluation.html","id":"session-information-15","chapter":"20 Evaluation","heading":"20.6 Session information","text":"","code":"\nsessioninfo::session_info(include_base = TRUE)\n#> ─ Session info ───────────────────────────────────────────\n#> setting value\n#> version R version 4.2.2 (2022-10-31)\n#> os macOS Ventura 13.0\n#> system aarch64, darwin20\n#> ui X11\n#> language (EN)\n#> collate en_US.UTF-8\n#> ctype en_US.UTF-8\n#> tz Europe/Berlin\n#> date 2022-11-12\n#> pandoc 2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────\n#> ! package * version date (UTC) lib source\n#> base * 4.2.2 2022-10-31 [?] local\n#> bookdown 0.30 2022-11-09 [1] CRAN (R 4.2.2)\n#> bslib 0.4.1 2022-11-02 [1] CRAN (R 4.2.2)\n#> cachem 1.0.6 2021-08-19 [1] CRAN (R 4.2.0)\n#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)\n#> P compiler 4.2.2 2022-10-31 [1] local\n#> P datasets * 4.2.2 2022-10-31 [1] local\n#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)\n#> downlit 0.4.2 2022-07-05 [1] CRAN (R 4.2.1)\n#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.2)\n#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)\n#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)\n#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)\n#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)\n#> P graphics * 4.2.2 2022-10-31 [1] local\n#> P grDevices * 4.2.2 2022-10-31 [1] local\n#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.1)\n#> jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)\n#> jsonlite 1.8.3 2022-10-21 [1] CRAN (R 4.2.1)\n#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.1)\n#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.1)\n#> magrittr * 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)\n#> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0)\n#> P methods * 4.2.2 2022-10-31 [1] local\n#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)\n#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.1)\n#> R6 2.5.1.9000 2022-10-27 [1] local\n#> rlang * 1.0.6 2022-09-24 [1] CRAN (R 4.2.1)\n#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.2)\n#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.1)\n#> sass 0.4.2 2022-07-16 [1] CRAN (R 4.2.1)\n#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)\n#> P stats * 4.2.2 2022-10-31 [1] local\n#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.1)\n#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.1)\n#> P tools 4.2.2 2022-10-31 [1] local\n#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)\n#> P utils * 4.2.2 2022-10-31 [1] local\n#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.1)\n#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)\n#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.1)\n#> xml2 1.3.3.9000 2022-10-10 [1] local\n#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)\n#> \n#> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library\n#> \n#> P ── Loaded and on-disk path mismatch.\n#> \n#> ──────────────────────────────────────────────────────────"},{"path":"translation.html","id":"translation","chapter":"21 Translation","heading":"21 Translation","text":"Needed libraries:","code":"\nlibrary(rlang)\nlibrary(purrr)"},{"path":"translation.html","id":"html-exercises-21.2.6","chapter":"21 Translation","heading":"21.1 HTML (Exercises 21.2.6)","text":"Q1. escaping rules - - - - - - - - - - -
-
-
-

-4 Subsetting -

-

Attaching the needed libraries:

- -
-

-4.1 Selecting multiple elements (Exercises 4.2.6) -

-

Q1. Fix each of the following common data frame subsetting errors:

-
mtcars[mtcars$cyl = 4, ]
-mtcars[-1:4, ]
-mtcars[mtcars$cyl <= 5]
-mtcars[mtcars$cyl == 4 | 6, ]
-

A1. Fixed versions of these commands:

-
-# `==` instead of `=`
-mtcars[mtcars$cyl == 4, ]
-
-# `-(1:4)` instead of `-1:4`
-mtcars[-(1:4), ]
-
-# `,` was missing
-mtcars[mtcars$cyl <= 5, ]
-
-# correct subsetting syntax
-mtcars[mtcars$cyl == 4 | mtcars$cyl == 6, ]
-mtcars[mtcars$cyl %in% c(4, 6), ]
-

Q2. Why does the following code yield five missing values?

-
-x <- 1:5
-x[NA]
-#> [1] NA NA NA NA NA
-

A2. This is because of two reasons:

-
    -
  • The default type of NA in R is of logical type.
  • -
-
-typeof(NA)
-#> [1] "logical"
-
    -
  • R recycles indexes to match the length of the vector.
  • -
-
-x <- 1:5
-x[c(TRUE, FALSE)] # recycled to c(TRUE, FALSE, TRUE, FALSE, TRUE)
-#> [1] 1 3 5
-

Q3. What does upper.tri() return? How does subsetting a matrix with it work? Do we need any additional subsetting rules to describe its behaviour?

-
-x <- outer(1:5, 1:5, FUN = "*")
-x[upper.tri(x)]
-

A3. The documentation for upper.tri() states-

-
-

Returns a matrix of logicals the same size of a given matrix with entries TRUE in the upper triangle

-
-
-(x <- outer(1:5, 1:5, FUN = "*"))
-#>      [,1] [,2] [,3] [,4] [,5]
-#> [1,]    1    2    3    4    5
-#> [2,]    2    4    6    8   10
-#> [3,]    3    6    9   12   15
-#> [4,]    4    8   12   16   20
-#> [5,]    5   10   15   20   25
-
-upper.tri(x)
-#>       [,1]  [,2]  [,3]  [,4]  [,5]
-#> [1,] FALSE  TRUE  TRUE  TRUE  TRUE
-#> [2,] FALSE FALSE  TRUE  TRUE  TRUE
-#> [3,] FALSE FALSE FALSE  TRUE  TRUE
-#> [4,] FALSE FALSE FALSE FALSE  TRUE
-#> [5,] FALSE FALSE FALSE FALSE FALSE
-

When used with a matrix for subsetting, elements corresponding to TRUE in the subsetting matrix are selected. But, instead of a matrix, this returns a vector:

-
-x[upper.tri(x)]
-#>  [1]  2  3  6  4  8 12  5 10 15 20
-

Q4. Why does mtcars[1:20] return an error? How does it differ from the similar mtcars[1:20, ]?

-

A4. When indexed like a list, data frame columns at given indices will be selected.

-
-head(mtcars[1:2])
-#>                    mpg cyl
-#> Mazda RX4         21.0   6
-#> Mazda RX4 Wag     21.0   6
-#> Datsun 710        22.8   4
-#> Hornet 4 Drive    21.4   6
-#> Hornet Sportabout 18.7   8
-#> Valiant           18.1   6
-

mtcars[1:20] doesn’t work because there are only 11 columns in mtcars dataset.

-

On the other hand, mtcars[1:20, ] indexes a dataframe like a matrix, and because there are indeed 20 rows in mtcars, all columns with these rows are selected.

-
-nrow(mtcars[1:20, ])
-#> [1] 20
-

Q5. Implement your own function that extracts the diagonal entries from a matrix (it should behave like diag(x) where x is a matrix).

-

A5. We can combine the existing functions to our advantage:

-
-x[!upper.tri(x) & !lower.tri(x)]
-#> [1]  1  4  9 16 25
-
-diag(x)
-#> [1]  1  4  9 16 25
-

Q6. What does df[is.na(df)] <- 0 do? How does it work?

-

A6. This expression replaces every instance of NA in df with 0.

-

is.na(df) produces a matrix of logical values, which provides a way of subsetting.

-
-(df <- tibble(x = c(1, 2, NA), y = c(NA, 5, NA)))
-#> # A tibble: 3 Γ— 2
-#>       x     y
-#>   <dbl> <dbl>
-#> 1     1    NA
-#> 2     2     5
-#> 3    NA    NA
-
-is.na(df)
-#>          x     y
-#> [1,] FALSE  TRUE
-#> [2,] FALSE FALSE
-#> [3,]  TRUE  TRUE
-
-class(is.na(df))
-#> [1] "matrix" "array"
-
-
-

-4.2 Selecting a single element (Exercises 4.3.5) -

-

Q1. Brainstorm as many ways as possible to extract the third value from the cyl variable in the mtcars dataset.

-

A1. Possible ways to to extract the third value from the cyl variable in the mtcars dataset:

-
-mtcars[["cyl"]][[3]]
-#> [1] 4
-mtcars[[c(2, 3)]]
-#> [1] 4
-mtcars[3, ][["cyl"]]
-#> [1] 4
-mtcars[3, ]$cyl
-#> [1] 4
-mtcars[3, "cyl"]
-#> [1] 4
-mtcars[, "cyl"][[3]]
-#> [1] 4
-mtcars[3, 2]
-#> [1] 4
-mtcars$cyl[[3]]
-#> [1] 4
-

Q2. Given a linear model, e.g., mod <- lm(mpg ~ wt, data = mtcars), extract the residual degrees of freedom. Then extract the R squared from the model summary (summary(mod))

-

A2. Given that objects of class lm are lists, we can use subsetting operators to extract elements we want.

-
-mod <- lm(mpg ~ wt, data = mtcars)
-class(mod)
-#> [1] "lm"
-typeof(mod)
-#> [1] "list"
-
    -
  • extracting the residual degrees of freedom
  • -
-
-mod$df.residual 
-#> [1] 30
-mod[["df.residual"]]
-#> [1] 30
-
    -
  • extracting the R squared from the model summary
  • -
-
-summary(mod)$r.squared
-#> [1] 0.7528328
-summary(mod)[["r.squared"]]
-#> [1] 0.7528328
-
-
-

-4.3 Applications (Exercises 4.5.9) -

-

Q1. How would you randomly permute the columns of a data frame? (This is an important technique in random forests.) Can you simultaneously permute the rows and columns in one step?

-

A1. Let’s create a small data frame to work with.

-
-df <- head(mtcars)
-
-# original
-df
-#>                    mpg cyl disp  hp drat    wt  qsec vs am
-#> Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1
-#> Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1
-#> Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1
-#> Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0
-#> Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0
-#> Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0
-#>                   gear carb
-#> Mazda RX4            4    4
-#> Mazda RX4 Wag        4    4
-#> Datsun 710           4    1
-#> Hornet 4 Drive       3    1
-#> Hornet Sportabout    3    2
-#> Valiant              3    1
-

To randomly permute the columns of a data frame, we can combine [ and sample() as follows:

-
    -
  • randomly permute columns
  • -
-
-df[sample.int(ncol(df))]
-#>                   drat    wt carb am  qsec vs  hp  mpg disp
-#> Mazda RX4         3.90 2.620    4  1 16.46  0 110 21.0  160
-#> Mazda RX4 Wag     3.90 2.875    4  1 17.02  0 110 21.0  160
-#> Datsun 710        3.85 2.320    1  1 18.61  1  93 22.8  108
-#> Hornet 4 Drive    3.08 3.215    1  0 19.44  1 110 21.4  258
-#> Hornet Sportabout 3.15 3.440    2  0 17.02  0 175 18.7  360
-#> Valiant           2.76 3.460    1  0 20.22  1 105 18.1  225
-#>                   cyl gear
-#> Mazda RX4           6    4
-#> Mazda RX4 Wag       6    4
-#> Datsun 710          4    4
-#> Hornet 4 Drive      6    3
-#> Hornet Sportabout   8    3
-#> Valiant             6    3
-
    -
  • randomly permute rows
  • -
-
-df[sample.int(nrow(df)), ]
-#>                    mpg cyl disp  hp drat    wt  qsec vs am
-#> Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1
-#> Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1
-#> Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1
-#> Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0
-#> Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0
-#> Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0
-#>                   gear carb
-#> Datsun 710           4    1
-#> Mazda RX4 Wag        4    4
-#> Mazda RX4            4    4
-#> Hornet Sportabout    3    2
-#> Hornet 4 Drive       3    1
-#> Valiant              3    1
-
    -
  • randomly permute columns and rows
  • -
-
-df[sample.int(nrow(df)), sample.int(ncol(df))]
-#>                    qsec vs gear am    wt drat carb disp  hp
-#> Mazda RX4         16.46  0    4  1 2.620 3.90    4  160 110
-#> Hornet 4 Drive    19.44  1    3  0 3.215 3.08    1  258 110
-#> Datsun 710        18.61  1    4  1 2.320 3.85    1  108  93
-#> Mazda RX4 Wag     17.02  0    4  1 2.875 3.90    4  160 110
-#> Valiant           20.22  1    3  0 3.460 2.76    1  225 105
-#> Hornet Sportabout 17.02  0    3  0 3.440 3.15    2  360 175
-#>                    mpg cyl
-#> Mazda RX4         21.0   6
-#> Hornet 4 Drive    21.4   6
-#> Datsun 710        22.8   4
-#> Mazda RX4 Wag     21.0   6
-#> Valiant           18.1   6
-#> Hornet Sportabout 18.7   8
-

Q2. How would you select a random sample of m rows from a data frame? What if the sample had to be contiguous (i.e., with an initial row, a final row, and every row in between)?

-

A2. Let’s create a small data frame to work with.

-
-df <- head(mtcars)
-
-# original
-df
-#>                    mpg cyl disp  hp drat    wt  qsec vs am
-#> Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1
-#> Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1
-#> Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1
-#> Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0
-#> Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0
-#> Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0
-#>                   gear carb
-#> Mazda RX4            4    4
-#> Mazda RX4 Wag        4    4
-#> Datsun 710           4    1
-#> Hornet 4 Drive       3    1
-#> Hornet Sportabout    3    2
-#> Valiant              3    1
-
-# number of rows to sample
-m <- 2L
-

To select a random sample of m rows from a data frame, we can combine [ and sample() as follows:

-
    -
  • random and non-contiguous sample of m rows from a data frame
  • -
-
-df[sample(nrow(df), m), ]
-#>                mpg cyl disp  hp drat    wt  qsec vs am gear
-#> Valiant       18.1   6  225 105 2.76 3.460 20.22  1  0    3
-#> Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4
-#>               carb
-#> Valiant          1
-#> Mazda RX4 Wag    4
-
    -
  • random and contiguous sample of m rows from a data frame
  • -
-
-# select a random starting position from available number of rows
-start_row <- sample(nrow(df) - m + 1, size = 1)
-
-# adjust ending position while avoiding off-by-one error
-end_row <- start_row + m - 1
-
-df[start_row:end_row, ]
-#>               mpg cyl disp  hp drat    wt  qsec vs am gear
-#> Mazda RX4      21   6  160 110  3.9 2.620 16.46  0  1    4
-#> Mazda RX4 Wag  21   6  160 110  3.9 2.875 17.02  0  1    4
-#>               carb
-#> Mazda RX4        4
-#> Mazda RX4 Wag    4
-

Q3. How could you put the columns in a data frame in alphabetical order?

-

A3. we can sort columns in a data frame in the alphabetical order using [ with order():

-
-# columns in original order
-names(mtcars)
-#>  [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"  
-#>  [9] "am"   "gear" "carb"
-
-# columns in alphabetical order
-names(mtcars[order(names(mtcars))])
-#>  [1] "am"   "carb" "cyl"  "disp" "drat" "gear" "hp"   "mpg" 
-#>  [9] "qsec" "vs"   "wt"
-
-
-

-4.4 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fansi         1.0.3      2022-03-24 [1] CRAN (R 4.2.0)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>    glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    pillar        1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
-#>    pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang         1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>    tibble      * 3.1.8.9002 2022-10-16 [1] local
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>    utf8          1.2.2      2021-07-24 [1] CRAN (R 4.2.0)
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    vctrs         0.5.0      2022-10-22 [1] CRAN (R 4.2.1)
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/trade-offs.html b/_book/trade-offs.html deleted file mode 100644 index f5da0073..00000000 --- a/_book/trade-offs.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - -Chapter 16 Trade-offs | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/translation.html b/_book/translation.html deleted file mode 100644 index b820398f..00000000 --- a/_book/translation.html +++ /dev/null @@ -1,454 +0,0 @@ - - - - - - -Chapter 21 Translation | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-21 Translation -

-

Needed libraries:

- -
-

-21.1 HTML (Exercises 21.2.6) -

-
-

Q1. The escaping rules for <script> tags are different because they contain JavaScript, not HTML. Instead of escaping angle brackets or ampersands, you need to escape </script> so that the tag isn’t closed too early. For example, script("'</script>'"), shouldn’t generate this:

-
  <script>'</script>'</script>
-

But

-
  <script>'<\/script>'</script>
-

Adapt the escape() to follow these rules when a new argument script is set to TRUE.

-

A1. Let’s first start with the boilerplate code included in the book:

-
-escape <- function(x, ...) UseMethod("escape")
-
-escape.character <- function(x, script = FALSE) {
-  if (script) {
-    x <- gsub("</script>", "<\\/script>", x, fixed = TRUE)
-  } else {
-    x <- gsub("&", "&amp;", x)
-    x <- gsub("<", "&lt;", x)
-    x <- gsub(">", "&gt;", x)
-  }
-
-  html(x)
-}
-
-escape.advr_html <- function(x, ...) x
-

We will also need to tweak the boilerplate to pass this additional parameter to escape():

-
-html <- function(x) structure(x, class = "advr_html")
-
-print.advr_html <- function(x, ...) {
-  out <- paste0("<HTML> ", x)
-  cat(paste(strwrap(out), collapse = "\n"), "\n", sep = "")
-}
-
-dots_partition <- function(...) {
-  dots <- list2(...)
-
-  if (is.null(names(dots))) {
-    is_named <- rep(FALSE, length(dots))
-  } else {
-    is_named <- names(dots) != ""
-  }
-
-  list(
-    named = dots[is_named],
-    unnamed = dots[!is_named]
-  )
-}
-
-tag <- function(tag, script = FALSE) {
-  force(script)
-  new_function(
-    exprs(... = ),
-    expr({
-      dots <- dots_partition(...)
-      attribs <- html_attributes(dots$named)
-      children <- map_chr(.x = dots$unnamed, .f = ~ escape(.x, !!script))
-
-      html(paste0(
-        !!paste0("<", tag), attribs, ">",
-        paste(children, collapse = ""),
-        !!paste0("</", tag, ">")
-      ))
-    }),
-    caller_env()
-  )
-}
-
-void_tag <- function(tag) {
-  new_function(
-    exprs(... = ),
-    expr({
-      dots <- dots_partition(...)
-      if (length(dots$unnamed) > 0) {
-        abort(!!paste0("<", tag, "> must not have unnamed arguments"))
-      }
-      attribs <- html_attributes(dots$named)
-
-      html(paste0(!!paste0("<", tag), attribs, " />"))
-    }),
-    caller_env()
-  )
-}
-
-p <- tag("p")
-script <- tag("script", script = TRUE)
-
-script("'</script>'")
-#> <HTML> <script>'<\/script>'</script>
-
-

Q2. The use of ... for all functions has some big downsides. There’s no input validation and there will be little information in the documentation or autocomplete about how they are used in the function. Create a new function that, when given a named list of tags and their attribute names (like below), creates tag functions with named arguments.

-
-list(
-  a = c("href"),
-  img = c("src", "width", "height")
-)
-

All tags should get class and id attributes.

-
-

Q3. Reason about the following code that calls with_html() referencing objects from the environment. Will it work or fail? Why? Run the code to verify your predictions.

-
-greeting <- "Hello!"
-with_html(p(greeting))
-p <- function() "p"
-address <- "123 anywhere street"
-with_html(p(address))
-

A3. To work with this, we first need to copy-paste relevant code from the book:

-
-tags <- c(
-  "a", "abbr", "address", "article", "aside", "audio",
-  "b", "bdi", "bdo", "blockquote", "body", "button", "canvas",
-  "caption", "cite", "code", "colgroup", "data", "datalist",
-  "dd", "del", "details", "dfn", "div", "dl", "dt", "em",
-  "eventsource", "fieldset", "figcaption", "figure", "footer",
-  "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header",
-  "hgroup", "html", "i", "iframe", "ins", "kbd", "label",
-  "legend", "li", "mark", "map", "menu", "meter", "nav",
-  "noscript", "object", "ol", "optgroup", "option", "output",
-  "p", "pre", "progress", "q", "ruby", "rp", "rt", "s", "samp",
-  "script", "section", "select", "small", "span", "strong",
-  "style", "sub", "summary", "sup", "table", "tbody", "td",
-  "textarea", "tfoot", "th", "thead", "time", "title", "tr",
-  "u", "ul", "var", "video"
-)
-
-void_tags <- c(
-  "area", "base", "br", "col", "command", "embed",
-  "hr", "img", "input", "keygen", "link", "meta", "param",
-  "source", "track", "wbr"
-)
-
-html_tags <- c(
-  tags %>% set_names() %>% map(tag),
-  void_tags %>% set_names() %>% map(void_tag)
-)
-
-with_html <- function(code) {
-  code <- enquo(code)
-  eval_tidy(code, html_tags)
-}
-

Note that with_html() uses eval_tidy(), and therefore code argument is evaluated first in the html_tags named list, which acts as a data mask, and if no object is found in the data mask, searches in the caller environment.

-

For this reason, the first example code will work:

-
-greeting <- "Hello!"
-with_html(p(greeting))
-#> <HTML> <p>Hello!</p>
-

The following code, however, is not going to work because there is already address element in the data mask, and so p() will take a function address() as an input, and escape() doesn’t know how to deal with objects of function type:

-
-"address" %in% names(html_tags)
-#> [1] TRUE
-
-p <- function() "p"
-address <- "123 anywhere street"
-with_html(p(address))
-#> Error in UseMethod("escape"): no applicable method for 'escape' applied to an object of class "function"
-
-

Q4. Currently the HTML doesn’t look terribly pretty, and it’s hard to see the structure. How could you adapt tag() to do indenting and formatting? (You may need to do some research into block and inline tags.)

-

A4. Let’s first have a look at what it currently looks like:

-
-with_html(
-  body(
-    h1("A heading", id = "first"),
-    p("Some text &", b("some bold text.")),
-    img(src = "myimg.png", width = 100, height = 100)
-  )
-)
-#> <HTML> <body><h1 id='first'>A heading</h1><p>Some
-#> text &amp;<b>some bold text.</b></p><img
-#> src='myimg.png' width='100' height='100' /></body>
-

We can improve this to follow the Google HTML/CSS Style Guide.

-

For this, we need to create a new function to indent the code conditionally:

-
-print.advr_html <- function(x, ...) {
-  cat(paste("<HTML>", x, sep = "\n"))
-}
-
-indent <- function(x) {
-  paste0("  ", gsub("\n", "\n  ", x))
-}
-
-format_code <- function(children, indent = FALSE) {
-  if (indent) {
-    paste0("\n", paste0(indent(children), collapse = "\n"), "\n")
-  } else {
-    paste(children, collapse = "")
-  }
-}
-

We can then update the body() function to use this new helper:

-
-html_tags$body <- function(...) {
-  dots <- dots_partition(...)
-  attribs <- html_attributes(dots$named)
-  children <- map_chr(dots$unnamed, escape)
-
-  html(paste0(
-    "<body", attribs, ">",
-    format_code(children, indent = TRUE),
-    "</body>"
-  ))
-}
-

The new formatting looks much better:

-
-with_html(
-  body(
-    h1("A heading", id = "first"),
-    p("Some text &", b("some bold text.")),
-    img(src = "myimg.png", width = 100, height = 100)
-  )
-)
-#> <HTML>
-#> <body>
-#>   <h1 id='first'>A heading</h1>
-#>   <p>Some text &amp;<b>some bold text.</b></p>
-#>   <img src='myimg.png' width='100' height='100' />
-#> </body>
-
-
-
-

-21.2 LaTeX (Exercises 21.3.8) -

-

I didn’t manage to solve these exercises, and so I’d recommend checking out the solutions in the official solutions manual.

-
-

Q1. Add escaping. The special symbols that should be escaped by adding a backslash in front of them are \, $, and %. Just as with HTML, you’ll need to make sure you don’t end up double-escaping. So you’ll need to create a small S3 class and then use that in function operators. That will also allow you to embed arbitrary LaTeX if needed.

-
-

Q2. Complete the DSL to support all the functions that plotmath supports.

-
-
-
-

-21.3 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    purrr       * 0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang       * 1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
-
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_book/vectors.html b/_book/vectors.html deleted file mode 100644 index 7062afbe..00000000 --- a/_book/vectors.html +++ /dev/null @@ -1,922 +0,0 @@ - - - - - - -Chapter 3 Vectors | Advanced R Exercises - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

-3 Vectors -

-
-

-3.1 Atomic vectors (Exercises 3.2.5) -

-

Q1. How do you create raw and complex scalars? (See ?raw and ?complex.)

-

A1. In R, scalars are nothing but vectors of length 1, and can be created using the same constructor.

-
    -
  • Raw vectors
  • -
-

The raw type holds raw bytes, and can be created using charToRaw(). For example,

-
-x <- "A string"
-
-(y <- charToRaw(x))
-#> [1] 41 20 73 74 72 69 6e 67
-
-typeof(y)
-#> [1] "raw"
-

An alternative is to use as.raw():

-
-as.raw("–") # en-dash
-#> Warning: NAs introduced by coercion
-#> Warning: out-of-range values treated as 0 in coercion to raw
-#> [1] 00
-as.raw("β€”") # em-dash
-#> Warning: NAs introduced by coercion
-
-#> Warning: out-of-range values treated as 0 in coercion to raw
-#> [1] 00
-
    -
  • Complex vectors
  • -
-

Complex vectors are used to represent (surprise!) complex numbers.

-

Example of a complex scalar:

-
-(x <- complex(length.out = 1, real = 1, imaginary = 8))
-#> [1] 1+8i
-
-typeof(x)
-#> [1] "complex"
-

Q2. Test your knowledge of the vector coercion rules by predicting the output of the following uses of c():

-
-c(1, FALSE)
-c("a", 1)
-c(TRUE, 1L)
-

A2. The vector coercion rules dictate that the data type with smaller size will be converted to data type with bigger size.

-
-c(1, FALSE)
-#> [1] 1 0
-
-c("a", 1)
-#> [1] "a" "1"
-
-c(TRUE, 1L)
-#> [1] 1 1
-

Q3. Why is 1 == "1" true? Why is -1 < FALSE true? Why is "one" < 2 false?

-

A3. The coercion rules for vectors reveal why some of these comparisons return the results that they do.

-
-1 == "1"
-#> [1] TRUE
-
-c(1, "1")
-#> [1] "1" "1"
-
--1 < FALSE
-#> [1] TRUE
-
-c(-1, FALSE)
-#> [1] -1  0
-
-"one" < 2
-#> [1] FALSE
-
-c("one", 2)
-#> [1] "one" "2"
-
-sort(c("one", 2))
-#> [1] "2"   "one"
-

Q4. Why is the default missing value, NA, a logical vector? What’s special about logical vectors? (Hint: think about c(FALSE, NA_character_).)

-

A4. The "logical" type is the lowest in the coercion hierarchy.

-

So NA defaulting to any other type (e.g.Β "numeric") would mean that any time there is a missing element in a vector, rest of the elements would be converted to a type higher in hierarchy, which would be problematic for types lower in hierarchy.

-
-typeof(NA)
-#> [1] "logical"
-
-c(FALSE, NA_character_)
-#> [1] "FALSE" NA
-

Q5. Precisely what do is.atomic(), is.numeric(), and is.vector() test for?

-

A5. Let’s discuss them one-by-one.

- -

This function checks if the object is a vector of atomic type (or NULL).

-

Quoting docs:

-
-

is.atomic is true for the atomic types (β€œlogical”, β€œinteger”, β€œnumeric”, β€œcomplex”, β€œcharacter” and β€œraw”) and NULL.

-
-
-is.atomic(NULL)
-#> [1] TRUE
-
-is.atomic(list(NULL))
-#> [1] FALSE
- -

Its documentation says:

-
-

is.numeric should only return true if the base type of the class is double or integer and values can reasonably be regarded as numeric

-
-

Therefore, this function only checks for double and integer base types and not other types based on top of these types (factor, Date, POSIXt, or difftime).

-
-is.numeric(1L)
-#> [1] TRUE
-
-is.numeric(factor(1L))
-#> [1] FALSE
- -

As per its documentation:

-
-

is.vector returns TRUE if x is a vector of the specified mode having no attributes other than names. It returns FALSE otherwise.

-
-

Thus, the function can be incorrectif the object has attributes other than names.

-
-x <- c("x" = 1, "y" = 2)
-
-is.vector(x)
-#> [1] TRUE
-
-attr(x, "m") <- "abcdef"
-
-is.vector(x)
-#> [1] FALSE
-

A better way to check for a vector:

-
-is.null(dim(x))
-#> [1] TRUE
-
-
-

-3.2 Attributes (Exercises 3.3.4) -

-

Q1. How is setNames() implemented? How is unname() implemented? Read the source code.

-

A1. Let’s have a look at implementations for these functions.

- -
-setNames
-#> function (object = nm, nm) 
-#> {
-#>     names(object) <- nm
-#>     object
-#> }
-#> <bytecode: 0x12034d870>
-#> <environment: namespace:stats>
-

Given this function signature, we can see why, when no first argument is given, the result is still a named vector.

-
-setNames(, c("a", "b"))
-#>   a   b 
-#> "a" "b"
-
-setNames(c(1, 2), c("a", "b"))
-#> a b 
-#> 1 2
- -
-unname
-#> function (obj, force = FALSE) 
-#> {
-#>     if (!is.null(names(obj))) 
-#>         names(obj) <- NULL
-#>     if (!is.null(dimnames(obj)) && (force || !is.data.frame(obj))) 
-#>         dimnames(obj) <- NULL
-#>     obj
-#> }
-#> <bytecode: 0x1058aad50>
-#> <environment: namespace:base>
-

unname() removes existing names (or dimnames) by setting them to NULL.

-
-unname(setNames(, c("a", "b")))
-#> [1] "a" "b"
-

Q2. What does dim() return when applied to a 1-dimensional vector? When might you use NROW() or NCOL()?

-

A2. Dimensions for a 1-dimensional vector are NULL. For example,

-
-dim(c(1, 2))
-#> NULL
-

NROW() and NCOL() are helpful for getting dimensions for 1D vectors by treating them as if they were matrices or dataframes.

-
-# example-1
-x <- character(0)
-
-dim(x)
-#> NULL
-
-nrow(x)
-#> NULL
-NROW(x)
-#> [1] 0
-
-ncol(x)
-#> NULL
-NCOL(x)
-#> [1] 1
-
-# example-2
-y <- 1:4
-
-dim(y)
-#> NULL
-
-nrow(y)
-#> NULL
-NROW(y)
-#> [1] 4
-
-ncol(y)
-#> NULL
-NCOL(y)
-#> [1] 1
-

Q3. How would you describe the following three objects? What makes them different from 1:5?

-
-x1 <- array(1:5, c(1, 1, 5))
-x2 <- array(1:5, c(1, 5, 1))
-x3 <- array(1:5, c(5, 1, 1))
-

A3. x1, x2, and x3 are one-dimensional arrays, but with different β€œorientations”, if we were to mentally visualize them.

-

x1 has 5 entries in the third dimension, x2 in the second dimension, while x1 in the first dimension.

-

Q4. An early draft used this code to illustrate structure():

-
-structure(1:5, comment = "my attribute")
-#> [1] 1 2 3 4 5
-

But when you print that object you don’t see the comment attribute. Why? Is the attribute missing, or is there something else special about it? (Hint: try using help.)

-

A4. From ?attributes (emphasis mine):

-
-

Note that some attributes (namely class, comment, dim, dimnames, names, row.names and tsp) are treated specially and have restrictions on the values which can be set.

-
-
-structure(1:5, x = "my attribute")
-#> [1] 1 2 3 4 5
-#> attr(,"x")
-#> [1] "my attribute"
-
-structure(1:5, comment = "my attribute")
-#> [1] 1 2 3 4 5
-
-
-

-3.3 S3 atomic vectors (Exercises 3.4.5) -

-

Q1. What sort of object does table() return? What is its type? What attributes does it have? How does the dimensionality change as you tabulate more variables?

-

A1. table() returns an array of integer type and its dimensions scale with the number of variables present.

-
-(x <- table(mtcars$am))
-#> 
-#>  0  1 
-#> 19 13
-(y <- table(mtcars$am, mtcars$cyl))
-#>    
-#>      4  6  8
-#>   0  3  4 12
-#>   1  8  3  2
-(z <- table(mtcars$am, mtcars$cyl, mtcars$vs))
-#> , ,  = 0
-#> 
-#>    
-#>      4  6  8
-#>   0  0  0 12
-#>   1  1  3  2
-#> 
-#> , ,  = 1
-#> 
-#>    
-#>      4  6  8
-#>   0  3  4  0
-#>   1  7  0  0
-
-# type
-purrr::map(list(x, y, z), typeof)
-#> [[1]]
-#> [1] "integer"
-#> 
-#> [[2]]
-#> [1] "integer"
-#> 
-#> [[3]]
-#> [1] "integer"
-
-# attributes
-purrr::map(list(x, y, z), attributes)
-#> [[1]]
-#> [[1]]$dim
-#> [1] 2
-#> 
-#> [[1]]$dimnames
-#> [[1]]$dimnames[[1]]
-#> [1] "0" "1"
-#> 
-#> 
-#> [[1]]$class
-#> [1] "table"
-#> 
-#> 
-#> [[2]]
-#> [[2]]$dim
-#> [1] 2 3
-#> 
-#> [[2]]$dimnames
-#> [[2]]$dimnames[[1]]
-#> [1] "0" "1"
-#> 
-#> [[2]]$dimnames[[2]]
-#> [1] "4" "6" "8"
-#> 
-#> 
-#> [[2]]$class
-#> [1] "table"
-#> 
-#> 
-#> [[3]]
-#> [[3]]$dim
-#> [1] 2 3 2
-#> 
-#> [[3]]$dimnames
-#> [[3]]$dimnames[[1]]
-#> [1] "0" "1"
-#> 
-#> [[3]]$dimnames[[2]]
-#> [1] "4" "6" "8"
-#> 
-#> [[3]]$dimnames[[3]]
-#> [1] "0" "1"
-#> 
-#> 
-#> [[3]]$class
-#> [1] "table"
-

Q2. What happens to a factor when you modify its levels?

-
-f1 <- factor(letters)
-levels(f1) <- rev(levels(f1))
-

A2. Its levels change but the underlying integer values remain the same.

-
-f1 <- factor(letters)
-f1
-#>  [1] a b c d e f g h i j k l m n o p q r s t u v w x y z
-#> 26 Levels: a b c d e f g h i j k l m n o p q r s t u ... z
-as.integer(f1)
-#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
-#> [19] 19 20 21 22 23 24 25 26
-
-levels(f1) <- rev(levels(f1))
-f1
-#>  [1] z y x w v u t s r q p o n m l k j i h g f e d c b a
-#> 26 Levels: z y x w v u t s r q p o n m l k j i h g f ... a
-as.integer(f1)
-#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
-#> [19] 19 20 21 22 23 24 25 26
-

Q3. What does this code do? How do f2 and f3 differ from f1?

-
-f2 <- rev(factor(letters))
-f3 <- factor(letters, levels = rev(letters))
-

A3. In this code:

-
    -
  • -f2: Only the underlying integers are reversed, but levels remain unchanged.
  • -
-
-f2 <- rev(factor(letters))
-f2
-#>  [1] z y x w v u t s r q p o n m l k j i h g f e d c b a
-#> 26 Levels: a b c d e f g h i j k l m n o p q r s t u ... z
-as.integer(f2)
-#>  [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9
-#> [19]  8  7  6  5  4  3  2  1
-
    -
  • -f3: Both the levels and the underlying integers are reversed.
  • -
-
-f3 <- factor(letters, levels = rev(letters))
-f3
-#>  [1] a b c d e f g h i j k l m n o p q r s t u v w x y z
-#> 26 Levels: z y x w v u t s r q p o n m l k j i h g f ... a
-as.integer(f3)
-#>  [1] 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9
-#> [19]  8  7  6  5  4  3  2  1
-
-
-

-3.4 Lists (Exercises 3.5.4) -

-

Q1. List all the ways that a list differs from an atomic vector.

-

A1. Here is a table of comparison:

-
----- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
featureatomic vectorlist (aka generic vector)
element typeuniquemixed1 -
recursive?noyes2 -
return for out-of-bounds indexNANULL
memory addresssingle memory reference3 -reference per list element4 -
-

Q2. Why do you need to use unlist() to convert a list to an atomic vector? Why doesn’t as.vector() work?

-

A2. A list already is a (generic) vector, so as.vector() is not going to change anything, and there is no as.atomic.vector. Thus, we need to use unlist().

-
-x <- list(a = 1, b = 2)
-
-is.vector(x)
-#> [1] TRUE
-is.atomic(x)
-#> [1] FALSE
-
-# still a list
-as.vector(x)
-#> $a
-#> [1] 1
-#> 
-#> $b
-#> [1] 2
-
-# now a vector
-unlist(x)
-#> a b 
-#> 1 2
-

Q3. Compare and contrast c() and unlist() when combining a date and date-time into a single vector.

-

A3. Let’s first create a date and datetime object

-
-date <- as.Date("1947-08-15")
-datetime <- as.POSIXct("1950-01-26 00:01", tz = "UTC")
-

And check their attributes and underlying double representation:

-
-attributes(date)
-#> $class
-#> [1] "Date"
-attributes(datetime)
-#> $class
-#> [1] "POSIXct" "POSIXt" 
-#> 
-#> $tzone
-#> [1] "UTC"
-
-as.double(date) # number of days since the Unix epoch 1970-01-01
-#> [1] -8175
-as.double(datetime) # number of seconds since then
-#> [1] -628991940
-
    -
  • Behavior with c() -
  • -
-

Since S3 method for c() dispatches on the first argument, the resulting class of the vector is going to be the same as the first argument. Because of this, some attributes will be lost.

-
-c(date, datetime)
-#> [1] "1947-08-15" "1950-01-26"
-
-attributes(c(date, datetime))
-#> $class
-#> [1] "Date"
-
-c(datetime, date)
-#> [1] "1950-01-26 01:01:00 CET"  "1947-08-15 02:00:00 CEST"
-
-attributes(c(datetime, date))
-#> $class
-#> [1] "POSIXct" "POSIXt"
- -

It removes all attributes and we are left only with the underlying double representations of these objects.

-
-unlist(list(date, datetime))
-#> [1]      -8175 -628991940
-
-unlist(list(datetime, date))
-#> [1] -628991940      -8175
-
-
-

-3.5 Data frames and tibbles (Exercises 3.6.8) -

-

Q1. Can you have a data frame with zero rows? What about zero columns?

-

A1. Data frame with 0 rows is possible. This is basically a list with a vector of length 0.

-
-data.frame(x = numeric(0))
-#> [1] x
-#> <0 rows> (or 0-length row.names)
-

Data frame with 0 columns is also possible. This will be an empty list.

-
-data.frame(row.names = 1)
-#> data frame with 0 columns and 1 row
-

And, finally, data frame with 0 rows and columns is also possible:

-
-data.frame()
-#> data frame with 0 columns and 0 rows
-
-dim(data.frame())
-#> [1] 0 0
-

Although, it might not be common to create such data frames, they can be results of subsetting. For example,

-
-BOD[0, ]
-#> [1] Time   demand
-#> <0 rows> (or 0-length row.names)
-
-BOD[, 0]
-#> data frame with 0 columns and 6 rows
-
-BOD[0, 0]
-#> data frame with 0 columns and 0 rows
-

Q2. What happens if you attempt to set rownames that are not unique?

-

A2. If you attempt to set data frame rownames that are not unique, it will not work.

-
-data.frame(row.names = c(1, 1))
-#> Error in data.frame(row.names = c(1, 1)): duplicate row.names: 1
-

Q3. If df is a data frame, what can you say about t(df), and t(t(df))? Perform some experiments, making sure to try different column types.

-

A3. Transposing a data frame:

-
    -
  • transforms it into a matrix
  • -
  • coerces all its elements to be of the same type
  • -
-
-# original
-(df <- head(iris))
-#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
-#> 1          5.1         3.5          1.4         0.2  setosa
-#> 2          4.9         3.0          1.4         0.2  setosa
-#> 3          4.7         3.2          1.3         0.2  setosa
-#> 4          4.6         3.1          1.5         0.2  setosa
-#> 5          5.0         3.6          1.4         0.2  setosa
-#> 6          5.4         3.9          1.7         0.4  setosa
-
-# transpose
-t(df)
-#>              1        2        3        4        5       
-#> Sepal.Length "5.1"    "4.9"    "4.7"    "4.6"    "5.0"   
-#> Sepal.Width  "3.5"    "3.0"    "3.2"    "3.1"    "3.6"   
-#> Petal.Length "1.4"    "1.4"    "1.3"    "1.5"    "1.4"   
-#> Petal.Width  "0.2"    "0.2"    "0.2"    "0.2"    "0.2"   
-#> Species      "setosa" "setosa" "setosa" "setosa" "setosa"
-#>              6       
-#> Sepal.Length "5.4"   
-#> Sepal.Width  "3.9"   
-#> Petal.Length "1.7"   
-#> Petal.Width  "0.4"   
-#> Species      "setosa"
-
-# transpose of a transpose
-t(t(df))
-#>   Sepal.Length Sepal.Width Petal.Length Petal.Width
-#> 1 "5.1"        "3.5"       "1.4"        "0.2"      
-#> 2 "4.9"        "3.0"       "1.4"        "0.2"      
-#> 3 "4.7"        "3.2"       "1.3"        "0.2"      
-#> 4 "4.6"        "3.1"       "1.5"        "0.2"      
-#> 5 "5.0"        "3.6"       "1.4"        "0.2"      
-#> 6 "5.4"        "3.9"       "1.7"        "0.4"      
-#>   Species 
-#> 1 "setosa"
-#> 2 "setosa"
-#> 3 "setosa"
-#> 4 "setosa"
-#> 5 "setosa"
-#> 6 "setosa"
-
-# is it a dataframe?
-is.data.frame(df)
-#> [1] TRUE
-is.data.frame(t(df))
-#> [1] FALSE
-is.data.frame(t(t(df)))
-#> [1] FALSE
-
-# check type
-typeof(df)
-#> [1] "list"
-typeof(t(df))
-#> [1] "character"
-typeof(t(t(df)))
-#> [1] "character"
-
-# check dimensions
-dim(df)
-#> [1] 6 5
-dim(t(df))
-#> [1] 5 6
-dim(t(t(df)))
-#> [1] 6 5
-

Q4. What does as.matrix() do when applied to a data frame with columns of different types? How does it differ from data.matrix()?

-

A4. The return type of as.matrix() depends on the data frame column types.

-

As docs for as.matrix() mention:

-
-

The method for data frames will return a character matrix if there is only atomic columns and any non-(numeric/logical/complex) column, applying as.vector to factors and format to other non-character columns. Otherwise the usual coercion hierarchy (logical < integer < double < complex) will be used, e.g.Β all-logical data frames will be coerced to a logical matrix, mixed logical-integer will give an integer matrix, etc.

-
-

Let’s experiment:

-
-# example with mixed types (coerced to character)
-(df <- head(iris))
-#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
-#> 1          5.1         3.5          1.4         0.2  setosa
-#> 2          4.9         3.0          1.4         0.2  setosa
-#> 3          4.7         3.2          1.3         0.2  setosa
-#> 4          4.6         3.1          1.5         0.2  setosa
-#> 5          5.0         3.6          1.4         0.2  setosa
-#> 6          5.4         3.9          1.7         0.4  setosa
-
-as.matrix(df)
-#>   Sepal.Length Sepal.Width Petal.Length Petal.Width
-#> 1 "5.1"        "3.5"       "1.4"        "0.2"      
-#> 2 "4.9"        "3.0"       "1.4"        "0.2"      
-#> 3 "4.7"        "3.2"       "1.3"        "0.2"      
-#> 4 "4.6"        "3.1"       "1.5"        "0.2"      
-#> 5 "5.0"        "3.6"       "1.4"        "0.2"      
-#> 6 "5.4"        "3.9"       "1.7"        "0.4"      
-#>   Species 
-#> 1 "setosa"
-#> 2 "setosa"
-#> 3 "setosa"
-#> 4 "setosa"
-#> 5 "setosa"
-#> 6 "setosa"
-
-str(as.matrix(df))
-#>  chr [1:6, 1:5] "5.1" "4.9" "4.7" "4.6" "5.0" "5.4" ...
-#>  - attr(*, "dimnames")=List of 2
-#>   ..$ : chr [1:6] "1" "2" "3" "4" ...
-#>   ..$ : chr [1:5] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" ...
-
-# another example (no such coercion)
-BOD
-#>   Time demand
-#> 1    1    8.3
-#> 2    2   10.3
-#> 3    3   19.0
-#> 4    4   16.0
-#> 5    5   15.6
-#> 6    7   19.8
-
-as.matrix(BOD)
-#>      Time demand
-#> [1,]    1    8.3
-#> [2,]    2   10.3
-#> [3,]    3   19.0
-#> [4,]    4   16.0
-#> [5,]    5   15.6
-#> [6,]    7   19.8
-

On the other hand, data.matrix() always returns a numeric matrix.

-

From documentation of data.matrix():

-
-

Return the matrix obtained by converting all the variables in a data frame to numeric mode and then binding them together as the columns of a matrix. Factors and ordered factors are replaced by their internal codes. […] Character columns are first converted to factors and then to integers.

-
-

Let’s experiment:

-
-data.matrix(df)
-#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
-#> 1          5.1         3.5          1.4         0.2       1
-#> 2          4.9         3.0          1.4         0.2       1
-#> 3          4.7         3.2          1.3         0.2       1
-#> 4          4.6         3.1          1.5         0.2       1
-#> 5          5.0         3.6          1.4         0.2       1
-#> 6          5.4         3.9          1.7         0.4       1
-
-str(data.matrix(df))
-#>  num [1:6, 1:5] 5.1 4.9 4.7 4.6 5 5.4 3.5 3 3.2 3.1 ...
-#>  - attr(*, "dimnames")=List of 2
-#>   ..$ : chr [1:6] "1" "2" "3" "4" ...
-#>   ..$ : chr [1:5] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" ...
-
-
-

-3.6 Session information -

-
-sessioninfo::session_info(include_base = TRUE)
-#> ─ Session info ───────────────────────────────────────────
-#>  setting  value
-#>  version  R version 4.2.2 (2022-10-31)
-#>  os       macOS Ventura 13.0
-#>  system   aarch64, darwin20
-#>  ui       X11
-#>  language (EN)
-#>  collate  en_US.UTF-8
-#>  ctype    en_US.UTF-8
-#>  tz       Europe/Berlin
-#>  date     2022-11-12
-#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
-#> 
-#> ─ Packages ───────────────────────────────────────────────
-#>  ! package     * version    date (UTC) lib source
-#>    base        * 4.2.2      2022-10-31 [?] local
-#>    bookdown      0.30       2022-11-09 [1] CRAN (R 4.2.2)
-#>    bslib         0.4.1      2022-11-02 [1] CRAN (R 4.2.2)
-#>    cachem        1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
-#>    cli           3.4.1      2022-09-23 [1] CRAN (R 4.2.0)
-#>  P compiler      4.2.2      2022-10-31 [1] local
-#>  P datasets    * 4.2.2      2022-10-31 [1] local
-#>    digest        0.6.30     2022-10-18 [1] CRAN (R 4.2.1)
-#>    downlit       0.4.2      2022-07-05 [1] CRAN (R 4.2.1)
-#>    evaluate      0.18       2022-11-07 [1] CRAN (R 4.2.2)
-#>    fastmap       1.1.0      2021-01-25 [1] CRAN (R 4.2.0)
-#>    fs            1.5.2      2021-12-08 [1] CRAN (R 4.2.0)
-#>  P graphics    * 4.2.2      2022-10-31 [1] local
-#>  P grDevices   * 4.2.2      2022-10-31 [1] local
-#>    htmltools     0.5.3      2022-07-18 [1] CRAN (R 4.2.1)
-#>    jquerylib     0.1.4      2021-04-26 [1] CRAN (R 4.2.0)
-#>    jsonlite      1.8.3      2022-10-21 [1] CRAN (R 4.2.1)
-#>    knitr         1.40       2022-08-24 [1] CRAN (R 4.2.1)
-#>    magrittr    * 2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
-#>    memoise       2.0.1      2021-11-26 [1] CRAN (R 4.2.0)
-#>  P methods     * 4.2.2      2022-10-31 [1] local
-#>    purrr         0.3.5      2022-10-06 [1] CRAN (R 4.2.1)
-#>    R6            2.5.1.9000 2022-10-27 [1] local
-#>    rlang         1.0.6      2022-09-24 [1] CRAN (R 4.2.1)
-#>    rmarkdown     2.18       2022-11-09 [1] CRAN (R 4.2.2)
-#>    rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.1)
-#>    sass          0.4.2      2022-07-16 [1] CRAN (R 4.2.1)
-#>    sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
-#>  P stats       * 4.2.2      2022-10-31 [1] local
-#>    stringi       1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
-#>    stringr       1.4.1      2022-08-20 [1] CRAN (R 4.2.1)
-#>  P tools         4.2.2      2022-10-31 [1] local
-#>  P utils       * 4.2.2      2022-10-31 [1] local
-#>    withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
-#>    xfun          0.34       2022-10-18 [1] CRAN (R 4.2.1)
-#>    xml2          1.3.3.9000 2022-10-10 [1] local
-#>    yaml          2.3.6      2022-10-18 [1] CRAN (R 4.2.1)
-#> 
-#>  [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
-#> 
-#>  P ── Loaded and on-disk path mismatch.
-#> 
-#> ──────────────────────────────────────────────────────────
- -
-
- -
- -
-
- -
- -
-

"Advanced R Exercises" was written by Indrajeet Patil. It was last built on 2022-11-12.

-
- -
-

This book was built by the bookdown R package.

-
- -
-
- - diff --git a/_bookdown_files/Function-factories_files/figure-html/Function-factories-18-1.png b/_bookdown_files/Function-factories_files/figure-html/Function-factories-18-1.png deleted file mode 100644 index 1e696e5e..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/Function-factories-18-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/Function-factories-19-1.png b/_bookdown_files/Function-factories_files/figure-html/Function-factories-19-1.png deleted file mode 100644 index 9cb4b9e3..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/Function-factories-19-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/Function-factories-28-1.png b/_bookdown_files/Function-factories_files/figure-html/Function-factories-28-1.png deleted file mode 100644 index 04c05abc..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/Function-factories-28-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/Function-factories-4-1.png b/_bookdown_files/Function-factories_files/figure-html/Function-factories-4-1.png deleted file mode 100644 index 0c37642d..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/Function-factories-4-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-17-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-17-1.png deleted file mode 100644 index 77b71e1e..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-17-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-18-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-18-1.png deleted file mode 100644 index 77b71e1e..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-18-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-19-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-19-1.png deleted file mode 100644 index 81ca78e9..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-19-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-2-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-2-1.png deleted file mode 100644 index 0c37642d..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-2-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-20-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-20-1.png deleted file mode 100644 index 9f8d6b82..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-20-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-21-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-21-1.png deleted file mode 100644 index 091c33b9..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-21-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-22-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-22-1.png deleted file mode 100644 index 00575ba1..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-22-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-23-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-23-1.png deleted file mode 100644 index d1d14e4f..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-23-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-24-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-24-1.png deleted file mode 100644 index 50edc3aa..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-24-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-25-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-25-1.png deleted file mode 100644 index 294878e1..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-25-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-26-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-26-1.png deleted file mode 100644 index b61b1998..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-26-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-27-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-27-1.png deleted file mode 100644 index e680eb03..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-27-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-28-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-28-1.png deleted file mode 100644 index ec17a2fd..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-28-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-29-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-29-1.png deleted file mode 100644 index bff16906..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-29-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-30-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-30-1.png deleted file mode 100644 index 1f8f72b8..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-30-1.png and /dev/null differ diff --git a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-4-1.png b/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-4-1.png deleted file mode 100644 index b92aa0c5..00000000 Binary files a/_bookdown_files/Function-factories_files/figure-html/unnamed-chunk-4-1.png and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-epub3/unnamed-chunk-10-1.png b/_bookdown_files/Functionals_files/figure-epub3/unnamed-chunk-10-1.png deleted file mode 100644 index dac71971..00000000 Binary files a/_bookdown_files/Functionals_files/figure-epub3/unnamed-chunk-10-1.png and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-epub3/unnamed-chunk-7-1.png b/_bookdown_files/Functionals_files/figure-epub3/unnamed-chunk-7-1.png deleted file mode 100644 index 2aaf01a1..00000000 Binary files a/_bookdown_files/Functionals_files/figure-epub3/unnamed-chunk-7-1.png and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-html/Functionals-11-1.png b/_bookdown_files/Functionals_files/figure-html/Functionals-11-1.png deleted file mode 100644 index 8d7f70fd..00000000 Binary files a/_bookdown_files/Functionals_files/figure-html/Functionals-11-1.png and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-html/Functionals-11-2.png b/_bookdown_files/Functionals_files/figure-html/Functionals-11-2.png deleted file mode 100644 index f8b39a86..00000000 Binary files a/_bookdown_files/Functionals_files/figure-html/Functionals-11-2.png and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-10-1.png b/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-10-1.png deleted file mode 100644 index f383f825..00000000 Binary files a/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-10-1.png and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-11-1.png b/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-11-1.png deleted file mode 100644 index f383f825..00000000 Binary files a/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-11-1.png and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-11-2.png b/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-11-2.png deleted file mode 100644 index e5b695ba..00000000 Binary files a/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-11-2.png and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-7-1.png b/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-7-1.png deleted file mode 100644 index 810eb40d..00000000 Binary files a/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-7-1.png and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-8-1.png b/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-8-1.png deleted file mode 100644 index 3fd0ec1c..00000000 Binary files a/_bookdown_files/Functionals_files/figure-html/unnamed-chunk-8-1.png and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-latex/unnamed-chunk-10-1.pdf b/_bookdown_files/Functionals_files/figure-latex/unnamed-chunk-10-1.pdf deleted file mode 100644 index 240c3627..00000000 Binary files a/_bookdown_files/Functionals_files/figure-latex/unnamed-chunk-10-1.pdf and /dev/null differ diff --git a/_bookdown_files/Functionals_files/figure-latex/unnamed-chunk-7-1.pdf b/_bookdown_files/Functionals_files/figure-latex/unnamed-chunk-7-1.pdf deleted file mode 100644 index 7389f90f..00000000 Binary files a/_bookdown_files/Functionals_files/figure-latex/unnamed-chunk-7-1.pdf and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/Functions-36-1.png b/_bookdown_files/Functions_files/figure-html/Functions-36-1.png deleted file mode 100644 index 2c2a55ff..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/Functions-36-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/Functions-43-1.png b/_bookdown_files/Functions_files/figure-html/Functions-43-1.png deleted file mode 100644 index 0950e226..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/Functions-43-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-30-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-30-1.png deleted file mode 100644 index 2c2a55ff..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-30-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-31-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-31-1.png deleted file mode 100644 index e3bf7c11..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-31-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-33-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-33-1.png deleted file mode 100644 index e3bf7c11..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-33-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-34-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-34-1.png deleted file mode 100644 index e3bf7c11..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-34-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-35-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-35-1.png deleted file mode 100644 index e3bf7c11..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-35-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-36-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-36-1.png deleted file mode 100644 index e3bf7c11..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-36-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-37-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-37-1.png deleted file mode 100644 index 0950e226..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-37-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-38-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-38-1.png deleted file mode 100644 index b509e071..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-38-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-40-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-40-1.png deleted file mode 100644 index b509e071..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-40-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-41-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-41-1.png deleted file mode 100644 index b509e071..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-41-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-42-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-42-1.png deleted file mode 100644 index b509e071..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-42-1.png and /dev/null differ diff --git a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-43-1.png b/_bookdown_files/Functions_files/figure-html/unnamed-chunk-43-1.png deleted file mode 100644 index b509e071..00000000 Binary files a/_bookdown_files/Functions_files/figure-html/unnamed-chunk-43-1.png and /dev/null differ diff --git a/_bookdown_files/Names-values_files/figure-epub3/unnamed-chunk-29-1.png b/_bookdown_files/Names-values_files/figure-epub3/unnamed-chunk-29-1.png deleted file mode 100644 index b537ce38..00000000 Binary files a/_bookdown_files/Names-values_files/figure-epub3/unnamed-chunk-29-1.png and /dev/null differ diff --git a/_bookdown_files/Names-values_files/figure-html/Names-values-31-1.png b/_bookdown_files/Names-values_files/figure-html/Names-values-31-1.png deleted file mode 100644 index 6210e6f4..00000000 Binary files a/_bookdown_files/Names-values_files/figure-html/Names-values-31-1.png and /dev/null differ diff --git a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-17-1.png b/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-17-1.png deleted file mode 100644 index 5a48cdd1..00000000 Binary files a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-17-1.png and /dev/null differ diff --git a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-18-1.png b/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-18-1.png deleted file mode 100644 index e9e3d64f..00000000 Binary files a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-18-1.png and /dev/null differ diff --git a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-28-1.png b/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-28-1.png deleted file mode 100644 index afc7a713..00000000 Binary files a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-28-1.png and /dev/null differ diff --git a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-29-1.png b/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-29-1.png deleted file mode 100644 index c8d181e0..00000000 Binary files a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-29-1.png and /dev/null differ diff --git a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-30-1.png b/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-30-1.png deleted file mode 100644 index 16c40fb9..00000000 Binary files a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-30-1.png and /dev/null differ diff --git a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-31-1.png b/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-31-1.png deleted file mode 100644 index a5241b0c..00000000 Binary files a/_bookdown_files/Names-values_files/figure-html/unnamed-chunk-31-1.png and /dev/null differ diff --git a/_bookdown_files/Names-values_files/figure-latex/unnamed-chunk-29-1.pdf b/_bookdown_files/Names-values_files/figure-latex/unnamed-chunk-29-1.pdf deleted file mode 100644 index 1beb4169..00000000 Binary files a/_bookdown_files/Names-values_files/figure-latex/unnamed-chunk-29-1.pdf and /dev/null differ diff --git a/_bookdown_files/Perf-improve_files/figure-html/Perf-improve-28-1.png b/_bookdown_files/Perf-improve_files/figure-html/Perf-improve-28-1.png deleted file mode 100644 index 639b4862..00000000 Binary files a/_bookdown_files/Perf-improve_files/figure-html/Perf-improve-28-1.png and /dev/null differ diff --git a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-17-1.png b/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-17-1.png deleted file mode 100644 index c3ac18a4..00000000 Binary files a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-17-1.png and /dev/null differ diff --git a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-21-1.png b/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-21-1.png deleted file mode 100644 index 011acb83..00000000 Binary files a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-21-1.png and /dev/null differ diff --git a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-22-1.png b/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-22-1.png deleted file mode 100644 index bd18c8dc..00000000 Binary files a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-22-1.png and /dev/null differ diff --git a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-23-1.png b/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-23-1.png deleted file mode 100644 index 488a84ac..00000000 Binary files a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-23-1.png and /dev/null differ diff --git a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-24-1.png b/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-24-1.png deleted file mode 100644 index 2197f17c..00000000 Binary files a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-24-1.png and /dev/null differ diff --git a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-25-1.png b/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-25-1.png deleted file mode 100644 index 08a5f687..00000000 Binary files a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-25-1.png and /dev/null differ diff --git a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-26-1.png b/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-26-1.png deleted file mode 100644 index 2574c206..00000000 Binary files a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-26-1.png and /dev/null differ diff --git a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-28-1.png b/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-28-1.png deleted file mode 100644 index 70acd6c5..00000000 Binary files a/_bookdown_files/Perf-improve_files/figure-html/unnamed-chunk-28-1.png and /dev/null differ diff --git a/_bookdown_files/Quotation_files/figure-html/Quotation-12-1.png b/_bookdown_files/Quotation_files/figure-html/Quotation-12-1.png deleted file mode 100644 index e0176d05..00000000 Binary files a/_bookdown_files/Quotation_files/figure-html/Quotation-12-1.png and /dev/null differ diff --git a/_bookdown_files/Quotation_files/figure-html/unnamed-chunk-12-1.png b/_bookdown_files/Quotation_files/figure-html/unnamed-chunk-12-1.png deleted file mode 100644 index fd066b36..00000000 Binary files a/_bookdown_files/Quotation_files/figure-html/unnamed-chunk-12-1.png and /dev/null differ diff --git a/_main.rds b/_main.rds deleted file mode 100644 index 0c53b40c..00000000 Binary files a/_main.rds and /dev/null differ diff --git a/emoji.R b/emoji.R index fda9b32c..d82431dc 100644 --- a/emoji.R +++ b/emoji.R @@ -1,4 +1,3 @@ - emoji <- function(x) { x <- emo::ji(x) diff --git a/render29e41578ebf.rds b/render29e41578ebf.rds deleted file mode 100644 index 22165256..00000000 Binary files a/render29e41578ebf.rds and /dev/null differ diff --git a/render3fa870b56538.rds b/render3fa870b56538.rds deleted file mode 100644 index 22165256..00000000 Binary files a/render3fa870b56538.rds and /dev/null differ diff --git a/render50ec444e1bd5.rds b/render50ec444e1bd5.rds deleted file mode 100644 index 7996df12..00000000 Binary files a/render50ec444e1bd5.rds and /dev/null differ diff --git a/render7fc42b73ed1.rds b/render7fc42b73ed1.rds deleted file mode 100644 index 22165256..00000000 Binary files a/render7fc42b73ed1.rds and /dev/null differ diff --git a/renderdeec651246f9.rds b/renderdeec651246f9.rds deleted file mode 100644 index d7d348ea..00000000 Binary files a/renderdeec651246f9.rds and /dev/null differ