--- title: "Miscellaneous Tips and Tricks in .mono[R]" subtitle: "EC 425/525, Lab 7" author: "Edward Rubin" date: "`r format(Sys.time(), '%d %B %Y')`" output: xaringan::moon_reader: css: ['default', 'metropolis', 'metropolis-fonts', 'my-css.css'] # self_contained: true nature: highlightStyle: github highlightLines: true countIncrementalSlides: false --- class: inverse, middle ```{R, setup, include = F} # devtools::install_github("dill/emoGG") library(pacman) p_load( broom, tidyverse, latex2exp, ggplot2, ggthemes, ggforce, viridis, extrafont, gridExtra, kableExtra, snakecase, janitor, data.table, dplyr, estimatr, lubridate, knitr, parallel, furrr, lfe, here, magrittr ) # Define pink color red_pink <- "#e64173" turquoise <- "#20B2AA" orange <- "#FFA500" red <- "#fb6107" blue <- "#3b3b9a" green <- "#8bb174" grey_light <- "grey70" grey_mid <- "grey50" grey_dark <- "grey20" purple <- "#6A5ACD" slate <- "#314f4f" # Dark slate grey: #314f4f # Knitr options opts_chunk$set( comment = "#>", fig.align = "center", fig.height = 7, fig.width = 10.5, warning = F, message = F ) opts_chunk$set(dev = "svg") options(device = function(file, width, height) { svg(tempfile(), width = width, height = height) }) options(knitr.table.format = "html") ``` # Prologue --- name: schedule # Schedule ## Last time Simulation in .mono[R] ## Today Helpful tips and tricks in .mono[R] --- layout: true # Tips and tricks --- class: inverse, middle --- name: applys ## The apply family In general, `for` loops are not the "preferred" route in .mono[R]. -- 1. Many functions are vectorized—you can apply a function over a vector. --
_E.g._, the square root of the numbers from 1 to 10: `sqrt(1:10)`. -- 1. That said, sometimes you just gotta loop. --
For these situations, `base` .mono[R] offers a family of `apply` functions. --- name: lapply ## The apply family The `apply` family *applies* a function over a vector, list, data frame, *etc.* -- For example, `lapply()` takes two arguments: `X` and `FUN`. -- - .purple[`X`] A vector/list of values. -- - .purple[`FUN`] The function you want to evaluate on each value of `X`. -- `lapply()` returns a list of the results. -- .ex[Example] `toupper()` capitalizes characters -- , _e.g._, `toupper("a")` yields `"A"`. -- `lapply(X = c("a", "pig"), FUN = toupper)` -- returns `list("A", "PIG")`. -- .note[Note] This is a silly example, as you can directly use `toupper()` on vectors. --- name: apply ## Plain apply The related `apply()` function *applies* a given function (`FUN`) along the margins (`MARGIN`) of a given array/matrix (`X`). -- Your options for `MARGIN` are `1` for rows and `2` for columns. -- .ex[Example] Let's find the maximum value in each row of a matrix. ```{R, ex-apply} # Create a matrix ex_matrix <- matrix(data = 1:16, nrow = 4, byrow = T) # Find the maximum value in each row. apply(X = ex_matrix, MARGIN = 1, FUN = max) ``` --- name: mapply ## Multiple apply Like `lapply()`, `mapply()` repeatedly evaluates a function (`FUN`) for each value in a vector of inputs. -- However, `mapply()` allows you to evaluate across .b[multiple] vectors. -- In addition `mapply()` allows you to dictate whether/how the results are simplified (_e.g._, `SIMPLIFY = T` for vector or matrix) or kept as a `list`. -- .ex[Example] Random normal draws with different means and variances. ```{R, ex-mapply} mapply(FUN = rnorm, n = 1, mean = c(0, 10, 20), sd = 1:3) ``` --- ## Custom apply All of our examples used already-defined functions for `FUN`, _e.g._, -- ```{R, more-apply, eval = F} lapply(X = c("a", "pig"), FUN = toupper) ``` -- Alternatively, you define your own function at `FUN`, _e.g._, -- ```{R, more-apply2, eval = T} lapply(X = 1:2, FUN = function(i) {i > 1}) ``` --- name: apply-more ## Other packages Other packages offer similar (and parallelized) functions. .left20[ .hi-pink[`base`]
`lapply()`
`apply()`
`mapply()` ] -- .left25[ .hi-orange[`purrr`/`furrr`]
`map()`
?
`map2()` ] -- .left30[ .hi-turquoise[`future.apply`]
`future_lapply()`
`future_apply()`
`future_mapply()` ] -- .left25[ .hi-purple[`parallel`]
`mclapply()`
`mcapply()`
`mcmapply()` ] --- name: for ## `for()` loops However, if you're really committed to running for loops, the syntax is ```{R, ex-for, eval = F} # Create an empty vector our_vector <- c() # Run the for loop for some numbers for (i in c(1, 1, 2, 3, 5, 8)) { # Print 'i' print(i) # Append 'i' to the end of our_vector our_vector <- c(our_vector, i) } ``` --- name: lists ## Lists and unlisting Lists (_e.g._, as outputted by `lapply()`) can be helpful—but they can also be fairly annoying. -- Enter `unlist()`. -- .col-left[ .b[List output] ```{R, ex-unlist} lapply( X = 1:2, FUN = as.character ) ``` ] -- .col-right[ .b[`unlist()`-ing to vector] ```{R, ex-unlist2} lapply( X = 1:2, FUN = as.character ) %>% unlist() ``` ] --- name: list-df ## From lists to data frames Sometimes you don't want to entirely `unlist()` a list. -- For example, you might have a list of data frames that you want to bind into a new data frame. -- In this case, you can use `bind_rows()` or `bind_cols()` from `dplyr`. -- Alternatively, you might be able to make use of `map_dfr()` or `map_dfc()`. --- name: list-index ## Indexing lists .note[Also] Don't forget that you can index lists using double-brackets. ```{R, list-index} # Capitalize the alphabet our_list <- lapply(X = letters, FUN = toupper) # The third letter our_list[[3]] ``` --- name: which ## Logical vectors and `which()` Finally, the simply function `which()` can be surprisingly helpful. -- `which()` tells you *which* of the entries in a logical vector are `TRUE` -- , _i.e._, *which* element—or elements—satisfies your logical condition(s). --- layout: false class: clear ```{R, which0} letters ``` -- ```{R, which1} letters > "m" ``` -- ```{R, which2} which(letters > "m") ``` -- ```{R, which3} letters[which(letters > "m")] ``` --- class: clear, middle Alternatively, we could have just used the logical vector. --- class: clear ```{R, logical0} letters ``` -- ```{R, logical1} letters > "m" ``` -- ```{R, logical2} letters[letters > "m"] ``` --- # Tips and tricks ## Logical vectors, continued This logic-based selection works on many classes of objects, but it may change the class/structure of the object. .col-left[ ```{R, logical-matrix0} # Create a matrix mat <- matrix(1:9, ncol = 3) # Print it out mat ``` ] -- .col-right[ ```{R, logical-matrix1} # Is the entry even? mat %% 2 == 0 ``` ] -- .col-right[ ```{R, logical-matrix2} # Print the even entries mat[mat %% 2 == 0] ``` ] --- layout: false # Table of contents .pull-left[ ### Tips and tricks .small[ 1. [The apply family](#applys) - [`lapply()`](#lapply) - [Plain `apply()`](#apply) - [`mapply()`](#mapply) 1. [`for()` loops](#for) 1. [Lists](#lists) - [`unlist()`-ing](#lists) - [Binding to data frame](#list-df) - [Indexing](#list-index) 1. [Logical vectors and `which()`](#which) ]] --- exclude: true ```{R, generate pdfs, include = F, eval = T} source("../../ScriptsR/unpause.R") unpause("07RMisc.Rmd", ".", T, T) ```