Use list diagrams throughout the chapter

This commit is contained in:
hadley 2015-12-01 11:47:20 +13:00
parent 28ba2c37f3
commit f9d61f5171
8 changed files with 62 additions and 31 deletions

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

BIN
diagrams/lists-flatten.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
diagrams/lists.graffle Normal file

Binary file not shown.

View File

@ -48,17 +48,18 @@ To create a list, you use the `list()` function:
```{r}
x <- list(1, 2, 3)
str(x)
x_named <- list(a = 1, b = 2, c = 3)
str(x)
```
Unlike the atomic vectors, `lists()` can contain a mix of objects:
Unlike atomic vectors, `lists()` can contain a mix of objects:
```{r}
y <- list("a", 1L, 1.5, TRUE)
str(y)
```
`str()` is very helpful when looking at lists because it focusses on the structure, not the contents.
Lists can even contain other lists!
```{r}
@ -66,32 +67,62 @@ z <- list(list(1, 2), list(3, 4))
str(z)
```
There are three ways to subset a list:
`str()` is very helpful when looking at lists because it focusses on the structure, not the contents.
## Visualising lists
It's helpful to have a visual representation of lists, so I'll use a nested set representation where each level of the hierarchy is nested in the previous. I'll always use rounded rectangles to represent lists, and regular rectangles to represent vectors. Note that single numbers (e.g. 1, 2), also called scalars, are not top-level objects in R and must always live inside a vector.
```{r}
x1 <- list(c(1, 2), c(3, 4))
x2 <- list(list(1, 2), list(3, 4))
x3 <- list(1, list(2, list(3)))
```
To make it easier to see the levels in the list, I colour each level a little darker than the previous. The orientiation of the elements (i.e. rows or columns) isn't important to the structure of the list (just the order of the elements), so I pick a row or column orientation to either save space or illustrate and important property of the operation.
`r bookdown::embed_png("diagrams/lists-structure.png", dpi = 220)`
(Unfortunately there's no way to draw these diagrams automatically - I did them by hand, carefully picking the arrangement that I think best illustrates the point I'm trying to make)
### Subsetting
There are three ways to subset a list, which I'll illustrate with this list:
```{r}
a <- list(a = 1:3, b = "a string", c = pi, d = list(-1, -5))
```
* `[` extracts a sub-list. The result will always be a list.
```{r}
str(y[1:3])
str(y[1])
str(a[1:2])
str(a[4])
```
* `[[` extracts a single component from a list.
* `[[` extracts a single component from a list. It removes a level of
hierarchy from the list.
```{r}
str(y[[1]])
str(y[[3]])
str(y[[4]])
```
* `$` is a shorthand for extracting named elements of a list. It works
very similarly to `[[` except that you don't need to use quotes.
```{r}
a <- list(x = 1:2, y = 3:4)
a$x
a[["y"]]
a$a
a[["b"]]
```
It's easy to get confused between `[` and `[[`, but understanding the difference is critical when working with lists. A few months ago I stayed at a hotel with a pretty interesting pepper shaker that I hope will help remember:
Or visually:
`r bookdown::embed_png("diagrams/lists-subsetting.png", dpi = 220)`
### Lists of condiments
It's easy to get confused between `[` and `[[`, but understanding the difference is critical when working with lists. A few months ago I stayed at a hotel with a pretty interesting pepper shaker that I hope will help remember these differences:
```{r, echo = FALSE}
embed_jpg("images/pepper.jpg", 300)
@ -117,6 +148,14 @@ If you wanted to get the content of the pepper package, you'd need `x[[1]][[1]]`
embed_jpg("images/pepper-3.jpg", 300)
```
### Exercises
1. Draw the following lists as nested sets.
1. Generate the lists corresponding to these nested set diagrams.
1. What happens if you subset a data frame as if you're subsetting a list?
## A common pattern of for loops
Lets start by creating a stereotypical list: an eight element list where each element contains a random vector of random length. (You'll learn `rerun()` later.)
@ -456,7 +495,7 @@ x %>% flatten() %>% flatten_dbl()
Graphically, that sequence of operations looks like:
`r bookdown::embed_png("diagrams/flatten.png", dpi = 220)`
`r bookdown::embed_png("diagrams/lists-flatten.png", dpi = 220)`
Whenever I get confused about a sequence of flattening operations, I'll often draw a diagram like this to help me understand what's going on.
@ -464,32 +503,24 @@ Base R has `unlist()`, but I recommend avoiding it for the same reason I recomme
### Switching levels in the hierarchy
Other times the hierarchy feels "inside out". For example, when using `safely()`, you get a list like this:
Other times the hierarchy feels "inside out". You can use `transpose()` to flip the first and second levels of a list:
```{r}
out <- list(
list(error = NULL, result = 1),
list(error = "ERROR", result = NULL),
list(error = NULL, result = 3)
x <- list(
x = list(a = 1, b = 3, c = 5),
y = list(a = 2, b = 4, c = 6)
)
str(out)
x %>% str()
x %>% transpose() %>% str()
```
This is a suboptimal arrangement because ideally you'd have a list of errors and a list of results. Earlier we handled this challenge by using `map()` to extract the named components into their own lists. Another approach is to use `transpose()`, which flips the first and second levels in the hierarchy:
Graphically, this looks like:
```{r}
out %>% transpose() %>% str()
```
`r bookdown::embed_png("diagrams/lists-transpose.png", dpi = 220)`
It's called transpose by analogy to matrices. When you subset a transposed matrix, you transpose the indices. When you subset a transposed list, you transpose the indices:
You'll see an example of this in the next section, as `transpose()` is particularly useful in conjunction with adverbs like `safely()` and `quietly()`.
```{r}
x <- list(list(a = 1, b = 3), list(a = 2, b = 4))
xt <- transpose(x)
x[[1]][[2]]
xt[[2]][[1]]
```
It's called transpose by analogy to matrices. When you subset a transposed matrix, you switch indices: `x[i, j]` is the same as `t(x)[j, i]`. It's the same idea when transposing a list, but the subsetting looks a little different: `x[[i]][[j]]` is equivalent to `transpose(x)[[j]][[i]]`. Similarly, a transpose is its own inverse so `transpose(transpose(x))` is equal to `x`.
### Exercises