In this lesson, you will learn about lists, how to create, modify, and access various parts of them.
We have already worked with one-dimensional objects: vectors; and two-dimensional objects: matrices and dataframes. Lists extend these ideas to more dimensions … Lists are recursive and hierarchical like trees or wardrobes: complex hierarchical objects.
Lists are useful for storing different kinds of objects, such as the output of statistical models. Lists can be accessed in very similar ways to how you have accessed vector, matrix, and dataframe elements.
OK, so let’s get going. First, create a vector of the integers 1, 2, and 3. Make sure that you use c() and do not assign it to any variable name.
c(1, 2, 3)
## [1] 1 2 3
OK, so we just made a simple vector. Now, we can create a list very easily with the list() function. Try listc(1, 2, 3).
list(1, 2, 3)
## [[1]]
## [1] 1
##
## [[2]]
## [1] 2
##
## [[3]]
## [1] 3
You should have both the vector and the list displayed on your Console screen now. We can compare them: What do they look like and how do they differ?
There is a clear difference between the vector and the list. The vector displays the three numbers on a single line; these are all in one vector with three elements. The list is showing a very different structure. First, it has three sections identified by double brackets [[i]], each followed by a single number with single brackets [i]. What do these mean?
As usual, single brackets indicate element position. Thus, in the first vector, there are three elements, and the first element is 1. In the list, there are three sections [[1]], [[2]], and [[3]], and each has a different first element, 1, 2, or 3, depending on which part of the list you look at.
Does that make sense? Please ask if not, and it should also become clearer as we make some more lists
OK, you should understand the basic structure of lists. Now make a simple list of three integers, and assign it to the name ‘x’
x <- list(1, 2, 3)
Now, look at the structure of x.
str(x)
## List of 3
## $ : num 1
## $ : num 2
## $ : num 3
Using str() is a bit more useful that just displaying the raw list itself. Here you can see that it is, really, a list, with three parts. Further, we are told what kind of data each part is (all three are numeric in this case), as well as a summary of what is in each part (a single integer in each).
We also see something else … ‘$ :’!
The dollar sign should remind you of a dataframe … You can see that they are also used to indicate parts of the list. We will use them again later.
As usual, we can name parts of this object. First, we can name the main parts of the list. We can give each part a name when we first make the list, as with dataframes. Try making the same list as above, but give each part a name, a, b, or c, for the first, second, and third parts. Call the list ‘x_named’
x_named <- list(a = 1, b = 2, c = 3)
Ok, so now display the structure of the new named list, x_named’
str(x_named)
## List of 3
## $ a: num 1
## $ b: num 2
## $ c: num 3
What does this new list look like? Pretty similar to ‘x’, but with the names ‘a’, ‘b’, and ‘c’ after the dollar sign for each section. You can also use the names() function to add names to the main parts of the list, as we have done with matrices and dataframes in the past.
Ok.. so, in class we made a big deal about how lists are great because they can have different kinds of objects within them. Vectors and matrices must be of one class (numeric, character, logical, etc); dataframes can contain vectors (i.e., columns) of different class; lists can contain different kind of objects (vectors, matrices, dataframes, even other lists!).
So, lets make one. Create a list that contains text, an integer, a number, and a logical statement. Type y <- list(“a”, 1, 1.5, TRUE).
y <- list("a", 1, 1.5, TRUE)
Great. Now, let’s look at this list … what is the structure?
str(y)
## List of 4
## $ : chr "a"
## $ : num 1
## $ : num 1.5
## $ : logi TRUE
You can see that each different part is represented, similarly to we saw in ‘x’ and ‘x_named’. Here, however, we have the different objects and classes within the list identified. How about putting a list in there, too?
How do you think we might include a list within a list? Let’s begin with two small lists: list(1, 2) and list(3, 4). Put these together into a list, called ‘z’.
z <- list(list(1, 2), list(3, 4))
We can see the structure of ‘z’ with the str() function.
str(z)
## List of 2
## $ :List of 2
## ..$ : num 1
## ..$ : num 2
## $ :List of 2
## ..$ : num 3
## ..$ : num 4
So, what did we see there? The main list was clearly identified at the top, as usual. Then, within that list, each of the two nested lists was displayed with their constituent parts.
Now you know how to make lists, by sticking other objects together within a call to list(), with the added bonus of naming each part.
Now lets go the other way and start subsetting! Woo hoo!
First, let us make a slightly larger list of four parts. include a = 1:3, b = “some text”, c = pi, d = list(4,5). Call it z1
z1 <- list(a = 1:3, b = "some text", c = pi, d = list(4, 5))
And, just for kicks, take a look at the structure…
str(z1)
## List of 4
## $ a: int [1:3] 1 2 3
## $ b: chr "some text"
## $ c: num 3.14
## $ d:List of 2
## ..$ : num 4
## ..$ : num 5
Now we have our slightly larger list. Let’s investigate how we can subset it. We can use single brackets ‘[ ]’, double brackets ‘[[ ]]’, and the dollar sign ‘$’. Each one selects a different part of the list. Let’s see.
First, let’s try single brackets. These extract a sub-list, so the result will always be a list. Within the single brackets you can use the number or name to indicate which parts to select. Try selecting the first part of ‘z1’ using its position number.
z1[1]
## $a
## [1] 1 2 3
Check that the result is a list. You can do this with str() as before, or with the is.list() function. Try that instead.
is.list(z1[1])
## [1] TRUE
Now, try subsetting the first part of the list z1 using its name (a). You should get the identical output as above.
z1["a"]
## $a
## [1] 1 2 3
Now, let’s try subsetting with double brackets ‘[[ ]]’. Using double brackets removes a level of hierarchy from the list. So, if we select the first part of ‘z1’ using double brackets, what should we get as the result?
Let’s check that answer! Use double brackets to select ‘a’ from the list ‘z1’.
z1[["a"]]
## [1] 1 2 3
And let’s double check that it is not a list with is.list().
is.list(z1[["a"]])
## [1] FALSE
Nope, not a list! What is it? Use str() to find out.
str(z1[["a"]])
## int [1:3] 1 2 3
So, it is a numeric vector. Remember that the output of single brackets was a list. With double brackets, we remove one level of the list hierarchy, so in this case we end up with a vector.
As usual, you can use names or position to subset using double brackets.
Now we come to the last way to subset out parts of a list: The $!.
The dollar sign works exactly like double brackets, except that quotes around the name are not needed. Try subsetting out part ‘a’ using ‘$’.
z1$a
## [1] 1 2 3
Great work! Now you can build and deconstruct lists to your heart’s content. Let’s practice a few times on ‘z1’ before letting you go on your way. Subset out the first and third parts of ‘z1’ by name using single brackets.
z1[c("a", "c")]
## $a
## [1] 1 2 3
##
## $c
## [1] 3.141593
Now subset out the last part of ‘z1’ using single brackets and position number.
z1[4]
## $d
## $d[[1]]
## [1] 4
##
## $d[[2]]
## [1] 5
How about subsetting the first part of ‘d’ within ‘z1’ using the dollar sign (you will also need some brackets). The result should be another list, or sub-list.
z1$d[1]
## [[1]]
## [1] 4
OK. Enough! Please submit the log of this lesson to Google Forms so that Simon may evaluate your progress.
For reals?!