exclude: true

```{r setup, message=FALSE, warning=FALSE, include=FALSE}
options(
  htmltools.dir.version = FALSE, # for blogdown
  width=80
)

library(magrittr)
```

---
class: middle
count: false

# Subsetting and assignment

---

## Subsetting and assignment

Subsets can also be used with assignment to update specific values within an object.

```{r}
x = c(1, 4, 7)
```

--

```{r}
x[2] = 2
x
```
--
```{r}
x %% 2 != 0
```
--
```{r}
x[x %% 2 != 0] = x[x %% 2 != 0] + 1
x
```
--
```{r}
x[c(1,1)] = c(2,3)
x
```

---

```{r}
x = 1:6
x[c(2,NA)] = 1
x
```

--

```{r}
x = 1:6
x[c(-1,-2)] = 3
x
```

--

```{r}
x = 1:6
x[c(TRUE,NA)] = 1
x
```

--

```{r}
x = 1:6
x[] = 1:3
x
```


---

## Subsets of Subsets

```{r}
df = data.frame(a = c(5,1,NA,3))
```

```{r}
df$a[df$a == 5] = 0
df
```

--

```{r}
df[1][df[1] == 3] = 0
df
```


---
class: middle
count: false

.center[
```{r echo=FALSE, out.width="40%"}
knitr::include_graphics('imgs/hex-tidyverse.png')
```
]

---

## Tidy data

.pull-left-wide[
```{r echo=FALSE, out.width="90%", fig.align="center"}
knitr::include_graphics('imgs/tidy.png')
```
]

.pull-right-narrow[
<br/>
1. Every column is a variable.

2. Every row is an observation.

3. Every cell is a single value.
]


.footnote[ From R4DS - [tidy data](r4ds.had.co.nz/tidy-data.html) ]

---

## Tidy vs Untidy

> Happy families are all alike; every unhappy family is unhappy in its own way 
>
> — Leo Tolstoy

--

.pad-top[]

```{r}
tidyr::billboard[,1:7]
```

Is the above data set tidy?

---

## More tidy vs untidy

Is the following data tidy?

```{r include=FALSE}
sw_people = purrr::map(
  repurrrsive::sw_people,
  ~ .[1:8]
)
```

.pull-left[
```{r}
str(sw_people[1:3])
```
]

.pull-right[
```{r}
str(sw_people[4:6])
```
]

---
class: middle
count: false

.center[
```{r echo=FALSE, out.width="40%"}
knitr::include_graphics('imgs/hex-tibble.png')
```
]

---

## Modern data frames

Hadley Wickham / RStudio have a package that modifies data frames to be more modern, specifically they are designed to be surly and lazy + some fancy printing.

```{r}
library(tibble)
```

```{r include = FALSE}
options(width = 50)
```

.pull-left[
```{r}
iris
```
]

.pull-right[
```{r}
(tbl_iris = as_tibble(iris))
```
]

```{r include = FALSE}
options(width = 80)
```

---

## Tibbles are lazy

By default, tibbles will always stay as tibbles when subsetting (except when using `$` or `[[`).

```{r}
tbl_iris[1,]
```

--

.pull-left[
```{r}
tbl_iris[,1]
```
]

.pull-right[
```{r}
tbl_iris[[1]]
```
]


---

## More laziness - partial matching

Tibbles do not use partial matching when the `$` operator is used.

```{r}
head( iris$Sp )
head( iris$Species )
```

--

```{r}
head( tbl_iris$Sp )
head( tbl_iris$Species )
```


---

## More laziness - stringsAsFactors

Tibbles also have always had `stringsAsFactors = FALSE` as default behavior.

.pull-left[
```{r}
(t = tibble(
  x = 1:3, 
  y = c("A","B","C"),
  z = factor(c("X","Y","Z"))
))
```
]

.pull-right[
```{r}
(d = data.frame(
  x = 1:3, 
  y = c("A","B","C"),
  z = factor(c("X","Y","Z")),
  stringsAsFactors = TRUE
))
```
]

<div/>

--

.pull-left[
```{r}
str(t)
```
]

.pull-right[
```{r}
str(d)
```
]

---

## Tibbles and length coercion

```{r error=TRUE}
tibble(x = 1:4, y = 1)
```

--

```{r error=TRUE}
tibble(x = 1:4, y = 1:2)
```

--

```{r error=TRUE}
tibble(x = 1:4, y = 1:3)
```


---

## Tibbles and S3

.pull-left[
```{r}
t = tibble(
  x = 1:3, 
  y = c("A","B","C")
)

class(t)
```
]

.pull-right[
```{r}
d = data.frame(
  x = 1:3, 
  y = c("A","B","C")
)

class(d)
```
]


---
class: middle
count: false

```{r echo=FALSE, out.width="35%", fig.align="center"}
knitr::include_graphics('imgs/hex-magrittr.png')
```

.center[ .larger[
magrittr
] ]

---

## Pipes in R

You can think about the following sequence of actions - find key, unlock car, start car, drive to school, park.

<br/>

Expressed as a set of nested functions in R pseudocode this would look like:

```{r eval=FALSE}
park(drive(start_car(find("keys")), to="campus"))
```

<br/>

Writing it out using pipes give it a more natural (and easier to read) structure:

```{r eval=FALSE}
find("keys") %>%
    start_car() %>%
    drive(to="campus") %>%
    park()
```

---

## Approaches

All of the following are fine, it comes down to personal preference:

<br/>
Nested:
```{r, eval=FALSE}
h( g( f(x), y=1), z=1 )
```

<br/>
Piped:
```{r, eval=FALSE}
f(x) %>% g(y=1) %>% h(z=1)
```

<br/>
Intermediate:
```{r, eval=FALSE}
res = f(x)
res = g(res, y=1)
res = h(res, z=1)
```

---

## What about other arguments?

Sometimes we want to send our results to an function argument other than first one or we want to use the previous result for multiple arguments. In these cases we can refer to the previous result using `.`.

--

```{r}
data.frame(a = 1:3, b = 3:1) %>% lm(a~b, data=.)
```

--

```{r}
data.frame(a = 1:3, b = 3:1) %>% .[[1]]
```

--

```{r}
data.frame(a = 1:3, b = 3:1) %>% .[[length(.)]]
```


---
class: middle

.center[
```{r echo=FALSE, out.width="50%"}
knitr::include_graphics('imgs/hex-dplyr.png')
```
]

---

## A Grammar of Data Manipulation

dplyr is based on the concepts of functions as verbs that manipulate data frames.

Single data frame functions / verbs:

* `filter()` / `slice()`: pick rows based on criteria
* `select()` / `rename()`: select columns by name
* `pull()`: grab a column as a vector
* `arrange()`: reorder rows
* `mutate()` / `transmute()`: create or modify columns
* `distinct()`: filter for unique rows
* `summarise()` / `count()`: reduce variables to values
* `group_by()` / `ungroup()`: modify other verbs to act on subsets
* `relocate()`: change column order
* ... (many more)

---

## dplyr rules

1. First argument is *always* a data frame

2. Subsequent arguments say what to do with that data frame

3. *Always* return a data frame

4. Don't modify in place

5. Lazy evaluation magic


---

## Example Data

We will demonstrate dplyr's functionality using the nycflights13 data.

```{r message=FALSE}
library(dplyr)
library(nycflights13)

flights
```

---

## filter() - March flights

```{r}
flights %>% filter(month == 3)
```

---

## filter() - Flights in the first 7 days of March

```{r}
flights %>% filter(month == 3, day <= 7)
```

---

## filter() - Flights to LAX *or* JFK in March

```{r}
flights %>% filter(dest == "LAX" | dest == "JFK", month==3)
```

---

## slice() - First 10 flights

```{r}
flights %>% slice(1:10)
```

---

## slice() - Last 5 flights

```{r}
flights %>% slice((n()-4):n())
```

---

```{r}
flights %>% slice_tail(n = 5)
```

---

## select() - Individual Columns

```{r}
flights %>% select(year, month, day)
```

---

## select() - Exclude Columns

```{r}
flights %>% select(-year, -month, -day)
```

---

## select() - Ranges

```{r}
flights %>% select(year:day)
```

---

## select() - Exclusion Ranges

```{r}
flights %>% select(-(year:day))
```

---

## select() - Matching

```{r}
flights %>% select(contains("dep"), 
                   contains("arr"))
```

---

```{r}
flights %>% select(starts_with("dep"), 
                   starts_with("arr"))
```

.footnote[
Other helpers provide by [tidyselect](https://tidyselect.r-lib.org/): `starts_with`, `ends_with`, `everything`, `matches`, `num_range`, `one_of`, `everything`, `last_col`.
]
---

## select_if() - Get non-numeric columns

```{r}
flights %>% select_if(function(x) !is.numeric(x))
```

---

## relocate - to the front

```{r}
flights %>% relocate(carrier, origin, dest)
```

---

## relocate - to the end

```{r}
flights %>%
  relocate(year, month, day, .after = last_col())
```


---

## rename() - Change column names

```{r}
flights %>% rename(tail_number = tailnum)
```
 
---

## select() vs. rename()

.xsmall[
```{r}
flights %>% select(tail_number = tailnum)

flights %>% rename(tail_number = tailnum)
```
]

---

## pull()

```{r}
names(flights)
```

--

```{r}
flights %>% pull("year") %>% head()
```

--

```{r}
flights %>% pull(1) %>% head()
```

--

```{r}
flights %>% pull(-1) %>% head()
```

---

## arrange() - Sort data

```{r}
flights %>% filter(month==3,day==2) %>% arrange(origin, dest)
```

---

## arrange() & desc() - Descending order

```{r}
flights %>% 
  filter(month==3, day==2) %>% 
  arrange(desc(origin), dest) %>% 
  select(origin, dest, tailnum)
```

---

## mutate() - Modify columns

```{r message=FALSE}
flights %>% 
  select(year:day) %>% 
  mutate(date = paste(year, month, day, sep="/"))
```

---

## distinct() - Find unique rows

```{r}
flights %>% 
  select(origin, dest) %>% 
  distinct() %>% 
  arrange(origin,dest)
```

---

## summarise()

```{r}
flights %>% 
  summarize(n(), min(dep_delay), max(dep_delay))
```

--

.pad-top[]

```{r}
flights %>% 
  summarize(
    n = n(), 
    min_dep_delay = min(dep_delay, na.rm = TRUE), 
    max_dep_delay = max(dep_delay, na.rm = TRUE)
  )
```

---

## group_by()

```{r}
flights %>% group_by(origin)
```

---

## summarise() with group_by()

```{r}
flights %>% 
  group_by(origin) %>%
  summarize(
    n = n(), 
    min_dep_delay = min(dep_delay, na.rm = TRUE), 
    max_dep_delay = max(dep_delay, na.rm = TRUE)
  )
```

---

## summarise() with group_by() - quietly

.pull-left[
```{r}
flights %>% 
  group_by(origin) %>%
  summarize(
    n = n(), 
    min_dep_delay = min(dep_delay, na.rm = TRUE), 
    max_dep_delay = max(dep_delay, na.rm = TRUE),
    .groups = "drop_last"
  )
```
]

.pull-right[
```{r}
flights %>% 
  group_by(origin) %>%
  summarize(
    n = n(), 
    min_dep_delay = min(dep_delay, na.rm = TRUE), 
    max_dep_delay = max(dep_delay, na.rm = TRUE),
    .groups = "keep"
  )
```
]


---

```{r}
flights %>% 
  group_by(origin, carrier) %>%
  summarize(
    n = n(), 
    min_dep_delay = min(dep_delay, na.rm = TRUE), 
    max_dep_delay = max(dep_delay, na.rm = TRUE)
  ) %>%
  filter(n > 10000)
```

---

## count()

.pull-left[
```{r}
flights %>% 
  group_by(origin, carrier) %>%
  summarize(n = n(), .groups = "drop")
```
]

.pull-right[
```{r}
flights %>% 
  count(origin, carrier)
```
]

---

## mutate() with group_by()

```{r}
flights %>% group_by(origin) %>%
  mutate(
    n = n(), 
  ) %>%
  select(origin, n)
```

---

## Demos

.wide-list[ .large[
1. How many flights to Los Angeles (LAX) did each of the legacy carriers (AA, UA, DL or US) have in May from JFK, and what was their average duration?

2. What was the shortest flight out of each airport in terms of distance? In terms of duration?
] ]

---

## Exercise 1

.wide-list[ .large[
1. Which plane (check the tail number) flew out of each New York airport the most?

2. Which date should you fly on if you want to have the lowest possible average departure delay? What about arrival delay?
] ]
