# Lists (aka “recursive vectors”)

## Introduction to Lists

• A list in R is a special type of vector.
• Previously we have seen atomic vectors in which each element is a scalar/singleton and all elements must be of the same mode.
• In a list, each component can be any object of any sort! So, a single component of a list can be an atomic vector, or another list, or a function, or an array, or a data frame, etc.
• No restriction that the different components of a list be of the same mode, or length, or class or anything!
• This makes the list class very powerful (!!) and makes lists a very important type of object in R.
• Most high level functions (like lm or hist, etc.) return their results in lists
• So, you really need to know how to:
1. Recognize lists when you see them
2. Access elements from lists
3. Operate upon the components of lists
• You will also want to know how to create and use lists yourself.

### Creating Lists

You use the list() function to create a list.

# make L an empty list
L <- list()
L
#> list()

# make a list of two components
M <- list( c(2,4,6), c("a","b") )
M
#> [[1]]
#> [1] 2 4 6
#>
#> [[2]]
#> [1] "a" "b"

# make a list of three components
N <- list( 12, c(3,4), c(T,T,T,F))
N
#> [[1]]
#> [1] 12
#>
#> [[2]]
#> [1] 3 4
#>
#> [[3]]
#> [1]  TRUE  TRUE  TRUE FALSE

### How R prints lists

• Notice that the i-th component is printed after a [[i]] in R’s output.
• Shortly we will see that this explicitly tells us how we can extract these values from the list.
• What if a list includes another list as a component?

# make a list that has another list as a component
Q <- list(c(1, 2, 3, 4, 5, 6),
c("a", "b", "c", "d"),
list("squish",
list("whizz-bang",
c(F, F, T)
)
)
)
Q
#> [[1]]
#> [1] 1 2 3 4 5 6
#>
#> [[2]]
#> [1] "a" "b" "c" "d"
#>
#> [[3]]
#> [[3]][[1]]
#> [1] "squish"
#>
#> [[3]][[2]]
#> [[3]][[2]][[1]]
#> [1] "whizz-bang"
#>
#> [[3]][[2]][[2]]
#> [1] FALSE FALSE  TRUE

### Names for list components

• Just as we saw atomic vectors with a names attribute, so too can lists have names.
• You can pass names in to the list() function:
• list() takes arguments that are have a value or tag=value format.
• The tags are recorded in the names attribute of the list.

# list of 2 named components and one unnamed one
a <- list( foo = c("q","w"), bar = "MINE!", 3+5*1i )

# note that the names of the components are stored in the
# list's names attribute. Accessible with the names function
names(a)  # the names are stored in this attribute
#> [1] "foo" "bar" ""

### Backtrack: you can do the same with c()

• In the last lecture, we may not have noted that names can be assigned to the elements of atomic vectors during assignment with c(), just like one can with list():

# example of assigning names and values with c()
weights <- c( onefish = 90,
twofish = 101,
redfish = 112,
bluefish = 107
)
weights
#>  onefish  twofish  redfish bluefish
#>       90      101      112      107

# check out the names:
names(weights)
#> [1] "onefish"  "twofish"  "redfish"  "bluefish"

### How R prints complex lists with names

Let’s recreate our Q list from above, but put a few names on it (and call it Z):

# make a list that has another list as a component
Z <- list(screaming = c(1, 2, 3, 4, 5, 6),
yellow = c("a", "b", "c", "d"),
zonkers = list("squish",
second = list(a.name = "whizz-bang",
c(F, F, T)
)
)
)
Z
#> $screaming #> [1] 1 2 3 4 5 6 #> #>$yellow
#> [1] "a" "b" "c" "d"
#>
#> $zonkers #>$zonkers[[1]]
#> [1] "squish"
#>
#> $zonkers$second
#> $zonkers$second$a.name #> [1] "whizz-bang" #> #>$zonkers$second[[2]] #> [1] FALSE FALSE TRUE ## Accessing Parts of Lists with [ ] ### The Boxcar analogy Someone had a nice way of thinking about lists: • You can think of a list as a train. • Each component of a list is a boxcar. • What that component holds is the contents of the boxcar. The weird thing about this is that inside a boxcar you can have a whole ’nuther train… • However, it provides a nice way of thinking about the indexing lists with the different list indexing operators. ### List indexing operators • There are [ ] and ’[[ ]]and$.
• We will talk about [ ] first

### L[ vec ] — The “standard” indexing operator

• This is what we have been using on atomic vectors.
• When applied to a list the [ ] indexing operator
• Returns a list, stil!
• Effectively just picks out boxcars from the original list (i.e. original train) and links them up (in the order prescribed by the indexing operator) into a new train.
• I like to call it the “single-chomp” indexing operator—it’s like a pair of jaws that just rips off a chunk of the list but doesn’t “chew” into the boxcars at all.

### A diagram might help

• Here is the list Z from above. It is a train with three boxcars:

• Never mind that the boxcar named “zonkers” has a lot of complicated stuff in it.

• Now, this is crucial: From the perspective of the [ ] indexing operator the train is just a bunch of boxcars.
• The [ ] is agnostic to the contents of the boxcars…
• When you index it with [ ], the [ ] does just what it would do with an atomic vector, it just happens that the elements of a list can be thought of as singleton lists, themselves (instead of logicals or numerics, etc.)

### The way [ ] sees a list:

So, to the [ ] (“single-chomp” indexing operator) the list Z really looks like this:

Hence if do this Z[c(3,1)] you get a list back that looks like:

And if you had X-ray eyes and were able to peek into each of the boxcars of the list Z[c(3,1)], the list would look like this:

### Examples of indexing with [vec] on lists in code

• Consider indexing a list with [vec]
• As before, vec is a vector that is positive numeric, negative numeric, logical, or character (names) (recall the four main ways of indexing a vector…)
• So, let’s see what it looks like when we index some of the lists we created up above:
# first play around with the list N
N
#> [[1]]
#> [1] 12
#>
#> [[2]]
#> [1] 3 4
#>
#> [[3]]
#> [1]  TRUE  TRUE  TRUE FALSE

N[c(3,1)]
#> [[1]]
#> [1]  TRUE  TRUE  TRUE FALSE
#>
#> [[2]]
#> [1] 12

N[-1]
#> [[1]]
#> [1] 3 4
#>
#> [[2]]
#> [1]  TRUE  TRUE  TRUE FALSE

# now index the list a
a
#> $foo #> [1] "q" "w" #> #>$bar
#> [1] "MINE!"
#>
#> [[3]]
#> [1] 3+5i

a[c(T,T,F)]
#> $foo #> [1] "q" "w" #> #>$bar
#> [1] "MINE!"

a[c("foo", "bar")]
#> $foo #> [1] "q" "w" #> #>$bar
#> [1] "MINE!"

a[c(3,2)]
#> [[1]]
#> [1] 3+5i
#>
#> $bar #> [1] "MINE!" ### Summary • When you index a variable L with [ ], then • If L is a list, the result is a list. • If L is an atomic vector, the result is an atomic vector. ## Accessing Single Components of Lists with [[ ]] • Indexing with [ ] is all fine and dandy if all we ever want to do is shuffle boxcars around. • But, typically, “the goods” are inside the boxcars! • How can we get at what is in the boxcars? • Enter the [[ ]] indexing operator. • If we do L[[i]], then, 1. We go to boxcar i 2. We open the door of boxcar i, pull out its contents, and return the contents (but not the boxcar “shell”) ### Call it a two-chomp extractor… • I think of [[ ]] as the “two-chomp” extractor. (which is what it looks like…if [ ] is the one-chomp extractor) • The first chomp grabs a boxcar • The second one chews on the boxcar to “crunch off” its outer shell. ### [[ i ]] only works with single indexes Here is how I think of it: • Because [[ ]] is “chewing” (to crunch into the boxcar) it can’t take too big of a bite • In fact, it can only chew on a single component of a list (single boxcar) at a time • So i in [[i]] can be only: • a single positive integer, OR • a single name of a component. • “Crunching off” its outer shell means it returns the contents of the component, and not a list that contains that component. • However, if the contents of the boxcar is another list (for example the “zonkers” component in Z), then, of course that list gets returned as a list. • i.e., it only chews through the outermost boxcar. N[2] # single chomp #> [[1]] #> [1] 3 4 N[[2]] # two chomps (one bite, one chew!) #> [1] 3 4 a["foo"] # single chomp #>$foo
#> [1] "q" "w"

a[["foo"]] # two chomps
#> [1] "q" "w"

#### These would not work

# indexer for [[ ]] must be of length 1
a[[ c("foo", "bar")]]

a[[c(1, 2)]]

# indexer for [[ ]] cannot be negative
a[[-1]]

# indexer for [[ ]] oughtn't be a logical vector
a[[ TRUE ]]  # this only works because TRUE
# gets coerced to 1

a[[c(TRUE, TRUE)]] # this returns "q".  Weird subtetly.
# not recommended!

a[[c(TRUE, FALSE)]]  # fails appropriately
• N.B. The [[ ]] can be used with atomic vectors, but seldom is, as it offers nothing over [ ] in the atomic vector case.

## The $Two-Chomp Extractor If you have names on your list, you can use the $ to extract the contents of single components like so:

a$foo #> [1] "q" "w" a$bar
#> [1] "MINE!"

Which is convenient if you are typing the name since you don’t have to use quotation marks. But it does not work with names that are stored in objects:

i <- "foo"

a$i # NULL! no component named i #> NULL a[[i]] # this returns the same as a$foo
#> [1] "q" "w"

### Class Activity:

Given our list Z:

Z
#> $screaming #> [1] 1 2 3 4 5 6 #> #>$yellow
#> [1] "a" "b" "c" "d"
#>
#> $zonkers #>$zonkers[[1]]
#> [1] "squish"
#>
#> $zonkers$second
#> $zonkers$second$a.name #> [1] "whizz-bang" #> #>$zonkers$second[[2]] #> [1] FALSE FALSE TRUE How can you extract the part whose value is: "whizz-bang". Do that with names, and with integer indices, and also do it with $ and with [[ ]] and with a combination of both.

Recall that R prints lists with headers that show how you could access each element.

### The $extractor might need quotes • If the names of the list don’t satisfy the requirements for names of symbols in the R language, then you need to quote them b <- list("my bonny" = 1:3, "lies-over" = 4:9, "the.ocean" = 10:14) # note when you print it you see the single backticks: b #>$my bonny
#> [1] 1 2 3
#>
#> $lies-over #> [1] 4 5 6 7 8 9 #> #>$the.ocean
#> [1] 10 11 12 13 14

# so, this will work:
b$lies-over #> [1] 4 5 6 7 8 9 # but it might be preferable to do b[["lies-over"]] #> [1] 4 5 6 7 8 9 # this would fail: b$lies-over

# Oddly, this works:
b$my #> [1] 1 2 3 # Why? ###$ and name matching

• If the name given to $is the unique prefix of name in the list, then it will automagically expand to that silly <- list( here.is.a.stupid.long.name = c(F, T, T), here.is.another.stupid.long.name = 4:8 ) silly$here.is.a.  # this works for here.is.a.stupid.long.name
#> [1] FALSE  TRUE  TRUE

silly$here.is.an # this works for here.is.another.stupid.long.name #> [1] 4 5 6 7 8 # these return NULL silly$here.is.a
#> NULL

silly$here #> NULL silly$something.completely.different
#> NULL

silly[["what.is.going.on.here?"]]
#> NULL

### You can add a component to a list using [[ ]] or $: length(a) # number of components #> [1] 3 a[["boing"]] <- 1:24 length(a) # it is now one component longer #> [1] 4 a$another <- 1:24 > 8 & 1:24 < 13

a[[10]] <- "this is way out there"

Adding an element to a list that is beyond its current length pads the rest with NULLs

### You can also replace a component using [[ ]] or $a #>$foo
#> [1] "q" "w"
#>
#> $bar #> [1] "MINE!" #> #> [[3]] #> [1] 3+5i #> #>$boing
#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#> [24] 24
#>
#> $another #> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE #> [12] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE #> [23] FALSE FALSE #> #> [[6]] #> NULL #> #> [[7]] #> NULL #> #> [[8]] #> NULL #> #> [[9]] #> NULL #> #> [[10]] #> [1] "this is way out there" a$bar <- "YOURS"

a
#> $foo #> [1] "q" "w" #> #>$bar
#> [1] "YOURS"
#>
#> [[3]]
#> [1] 3+5i
#>
#> $boing #> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #> [24] 24 #> #>$another
#>  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE
#> [12]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [23] FALSE FALSE
#>
#> [[6]]
#> NULL
#>
#> [[7]]
#> NULL
#>
#> [[8]]
#> NULL
#>
#> [[9]]
#> NULL
#>
#> [[10]]
#> [1] "this is way out there"

## Various List Essentials

### Catenating lists

• You can use the c() operator to catenate lists. If any argument is a list, then it creates a list
c(a,N) # stick those in there
#> $foo #> [1] "q" "w" #> #>$bar
#> [1] "YOURS"
#>
#> [[3]]
#> [1] 3+5i
#>
#> $boing #> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #> [24] 24 #> #>$another
#>  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE
#> [12]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [23] FALSE FALSE
#>
#> [[6]]
#> NULL
#>
#> [[7]]
#> NULL
#>
#> [[8]]
#> NULL
#>
#> [[9]]
#> NULL
#>
#> [[10]]
#> [1] "this is way out there"
#>
#> [[11]]
#> [1] 12
#>
#> [[12]]
#> [1] 3 4
#>
#> [[13]]
#> [1]  TRUE  TRUE  TRUE FALSE

c(a,c("oops", "this", "atomic", "vector", "gets", "listized!"))
#> $foo #> [1] "q" "w" #> #>$bar
#> [1] "YOURS"
#>
#> [[3]]
#> [1] 3+5i
#>
#> $boing #> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #> [24] 24 #> #>$another
#>  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE
#> [12]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [23] FALSE FALSE
#>
#> [[6]]
#> NULL
#>
#> [[7]]
#> NULL
#>
#> [[8]]
#> NULL
#>
#> [[9]]
#> NULL
#>
#> [[10]]
#> [1] "this is way out there"
#>
#> [[11]]
#> [1] "oops"
#>
#> [[12]]
#> [1] "this"
#>
#> [[13]]
#> [1] "atomic"
#>
#> [[14]]
#> [1] "vector"
#>
#> [[15]]
#> [1] "gets"
#>
#> [[16]]
#> [1] "listized!"
• Note how different this is than trying to catenate with list()
z <- list(a,N)  # this makes a new list with two components that are lists!

z[[1]]$another; # two two-bites! #> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE #> [12] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE #> [23] FALSE FALSE z[[1]][[5]][11] # two two-bites and a one-bite! #> [1] TRUE ### Coercing To/Away From List • as.list() to coerce an atomic vector to be a list with each component being a scalar: w <- c("this", "is", "only", "a", "test") as.list(w) # make a list of the vector #> [[1]] #> [1] "this" #> #> [[2]] #> [1] "is" #> #> [[3]] #> [1] "only" #> #> [[4]] #> [1] "a" #> #> [[5]] #> [1] "test" names(w) <- c("one", "two", "three", "four", "fish") as.list(w) # names are preserved #>$one
#> [1] "this"
#>
#> $two #> [1] "is" #> #>$three
#> [1] "only"
#>
#> $four #> [1] "a" #> #>$fish
#> [1] "test"
• unlist() to “flatten” a list into an atomic vector, coercing mode to the most general. Note that names if present get prepended to indexes, and NULL components are omitted:
unlist(a)
#>                    foo1                    foo2                     bar
#>                     "q"                     "w"                 "YOURS"
#>                                          boing1                  boing2
#>                  "3+5i"                     "1"                     "2"
#>                  boing3                  boing4                  boing5
#>                     "3"                     "4"                     "5"
#>                  boing6                  boing7                  boing8
#>                     "6"                     "7"                     "8"
#>                  boing9                 boing10                 boing11
#>                     "9"                    "10"                    "11"
#>                 boing12                 boing13                 boing14
#>                    "12"                    "13"                    "14"
#>                 boing15                 boing16                 boing17
#>                    "15"                    "16"                    "17"
#>                 boing18                 boing19                 boing20
#>                    "18"                    "19"                    "20"
#>                 boing21                 boing22                 boing23
#>                    "21"                    "22"                    "23"
#>                 boing24                another1                another2
#>                    "24"                 "FALSE"                 "FALSE"
#>                another3                another4                another5
#>                 "FALSE"                 "FALSE"                 "FALSE"
#>                another6                another7                another8
#>                 "FALSE"                 "FALSE"                 "FALSE"
#>                another9               another10               another11
#>                  "TRUE"                  "TRUE"                  "TRUE"
#>               another12               another13               another14
#>                  "TRUE"                 "FALSE"                 "FALSE"
#>               another15               another16               another17
#>                 "FALSE"                 "FALSE"                 "FALSE"
#>               another18               another19               another20
#>                 "FALSE"                 "FALSE"                 "FALSE"
#>               another21               another22               another23
#>                 "FALSE"                 "FALSE"                 "FALSE"
#>               another24
#>                 "FALSE" "this is way out there"