---
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)
```