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, orlength, orclassor anything! - This makes the
listclass very powerful (!!) and makes lists a very important type of object in R. - Most high level functions (like
lmorhist, etc.) return their results in lists- So, you really need to know how to:
- Recognize lists when you see them
- Access elements from lists
- Operate upon the components of lists
- You will also want to know how to create and use lists yourself.
- So, you really need to know how to:
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
namesattribute, so too can lists have names. - You can pass names in to the
list()function:list()takes arguments that are have avalueortag=valueformat.The
tags are recorded in thenamesattribute 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
namescan be assigned to the elements of atomic vectors during assignment withc(), just like one can withlist():# 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
Zfrom 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
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,
vecis 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
Lwith[ ], then- If
Lis a list, the result is a list. - If
Lis an atomic vector, the result is an atomic vector.
- If
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,- We go to boxcar i
- 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
iin[[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.
- However, if the contents of the boxcar is another list (for example the “zonkers” component in
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
Adding or Changing Components
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 thatnamesif 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"
comments powered by Disqus