In contrast to a vector, in which all elements must be of the same mode, R’s list structure can combine objects of different types.
Creating Lists
Technically, a list is a vector. Ordinary vectors—those of the type we’ve been using so far in this book—are termed atomic vectors, since their components cannot be broken down into smaller components. In contrast, lists are referred to as recursive vectors.
For our first look at lists, let’s consider an employee database. For each employee, we wish to store the name, salary, and a Boolean indicating union membership. Since we have three different modes here—character, numeric, and logical—it’s a perfect place for using lists. Our entire database might then be a list of lists, or some other kind of list such as a data frame, though we won’t pursue that here. We could create a list to represent our employee, Joe, this way:
j <- list(name="Joe", salary=55000, union=T)
We could print out j, either in full or by component:
j
## $name
## [1] "Joe"
##
## $salary
## [1] 55000
##
## $union
## [1] TRUE
j$salary
## [1] 55000
j$name
## [1] "Joe"
j$union
## [1] TRUE
General List Operations
Now that you’ve seen a simple example of creating a list, let’s look at how to access and work with lists.
List Indexing
You can access a list component in several different ways:
j$salary
## [1] 55000
j[["salary"]]
## [1] 55000
j[[2]]
## [1] 55000
We can refer to list components by their numerical indices, treating the list as a vector. However, note that in this case, we use double brackets instead of single ones. So, there are three ways to access an individual component c of a list lst and return it in the data type of c:
• lst$c • lst[[“c”]] • lst[[i]], where i is the index of c within lst
Adding and Deleting List Elements
The operations of adding and deleting list elements arise in a surprising number of contexts. This is especially true for data structures in which lists form the foundation, such as data frames and R classes.
New components can be added after a list is created.
z <- list(a="abc",b=12)
z
## $a
## [1] "abc"
##
## $b
## [1] 12
z$c <- "sailing" # add a c component
z
## $a
## [1] "abc"
##
## $b
## [1] 12
##
## $c
## [1] "sailing"
We confirmed that a third element c was added to the list named c.
Adding components can also be done via a vector index:
z[[4]] <- 28
z[5:7] <- c(FALSE,TRUE,TRUE)
z
## $a
## [1] "abc"
##
## $b
## [1] 12
##
## $c
## [1] "sailing"
##
## [[4]]
## [1] 28
##
## [[5]]
## [1] FALSE
##
## [[6]]
## [1] TRUE
##
## [[7]]
## [1] TRUE
You can delete a list component by setting it to NULL.
z$b <- NULL
z
## $a
## [1] "abc"
##
## $c
## [1] "sailing"
##
## [[3]]
## [1] 28
##
## [[4]]
## [1] FALSE
##
## [[5]]
## [1] TRUE
##
## [[6]]
## [1] TRUE
You can also concatenate lists.
c(list("Joe", 55000, T),list(5))
## [[1]]
## [1] "Joe"
##
## [[2]]
## [1] 55000
##
## [[3]]
## [1] TRUE
##
## [[4]]
## [1] 5
Getting the Size of a List
Since a list is a vector, you can obtain the number of components in a list via length().
length(j)
## [1] 3
length(z)
## [1] 6
Accessing List Components and Values
If the components in a list do have tags, as is the case with name, salary, and union for j in Section 4.1, you can obtain them via names():
names(j)
## [1] "name" "salary" "union"
To obtain the values, use unlist():
ulj <- unlist(j)
ulj
## name salary union
## "Joe" "55000" "TRUE"
class(ulj)
## [1] "character"
On the other hand, if we were to start with numbers, we would get numbers.
z <- list(a=5,b=12,c=13)
y <- unlist(z)
class(y)
## [1] "numeric"
y
## a b c
## 5 12 13
So the output of unlist() in this case was a numeric vector. What about a mixed case?
w <- list(a=5,b="xyz")
wu <- unlist(w)
class(wu)
## [1] "character"
wu
## a b
## "5" "xyz"
Here, R chose the least common denominator: character strings. This sounds like some kind of precedence structure, and it is.
Applying Functions to Lists
Two functions are handy for applying functions to lists: lapply and sapply.
Using the lapply() and sapply() Functions
The function lapply() (for list apply) works like the matrix apply() function, calling the specified function on each component of a list (or vector coerced to a list) and returning another list. Here’s an example:
lapply(list(1:3,25:29),median)
## [[1]]
## [1] 2
##
## [[2]]
## [1] 27
R applied median() to 1:3 and to 25:29, returning a list consisting of 2 and 27.In some cases, such as the example here, the list returned by lapply() could be simplified to a vector or matrix. This is exactly what sapply() (for simplified [l]apply) does.
sapply(list(1:3,25:29),median)
## [1] 2 27
Using sapply(), rather than applying the function directly, gave us the desired matrix form in the output.
Extended Example: Back to the Abalone Data
Let’s use the lapply() function in our abalone gender example. Recall that at one point in that example, we wished to know the indices of the observations that were male, female, and infant. For an easy demonstration, let’s use the same test case: a vector of genders.
g <- c("M","F","F","I","M","M","F")
lapply(c("M","F","I"),function(gender) which(g==gender))
## [[1]]
## [1] 1 5 6
##
## [[2]]
## [1] 2 3 7
##
## [[3]]
## [1] 4
The lapply() function expects its first argument to be a list. Here it was a vector, but lapply() will coerce that vector to a list form. Also, lapply() expects its second argument to be a function. This could be the name of a function, as you saw before, or the actual code, as we have here. This is an anonymous function, which you’ll learn more about in Section 7.13.
Then lapply() calls that anonymous function on “M”, then on “F”, and then on “I”. In that first case, the function calculates which(g==“M”), giving us the vector of indices in g of the males. After determining the indices for the females and infants, lapply() will return the three vectors in a list.
Note that even though the object of our main attention is the vector g of genders, it is not the first argument in the lapply() call in the example. Instead, that argument is an innocuous-looking vector of the three possible gender encodings. By contrast, g is mentioned only briefly in the function, as the second actual argument. This is a common situation in R. An even better way to do this will be presented in Section 6.2.2.
Recursive Lists Lists can be recursive, meaning that you can have lists within lists. Here’s an example:
b <- list(u = 5, v = 12)
c <- list(w = 13)
a <- list(b,c)
a
## [[1]]
## [[1]]$u
## [1] 5
##
## [[1]]$v
## [1] 12
##
##
## [[2]]
## [[2]]$w
## [1] 13
length(a)
## [1] 2
This code makes a into a two-component list, with each component itself also being a list. The concatenate function c() has an optional argument recursive, which controls whether flattening occurs when recursive lists are combined.
c(list(a=1,b=2,c=list(d=5,e=9)))
## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## $c$d
## [1] 5
##
## $c$e
## [1] 9
c(list(a=1,b=2,c=list(d=5,e=9)),recursive=T)
## a b c.d c.e
## 1 2 5 9
In the first case, we accepted the default value of recursive, which is FALSE, and obtained a recursive list, with the c component of the main list itself being another list. In the second call, with recursive set to TRUE, we got a single list as a result; only the names look recursive. (It’s odd that setting recursive to TRUE gives a nonrecursive list.) Recall that our first example of lists consisted of an employee database. I mentioned that since each employee was represented as a list, the entire database would be a list of lists. That is a concrete example of recursive lists.