A list is a generic vector that is allowed to include different types of objects, even other lists.
It is useful for its flexibility. For example, the result of a linear model fit in R is basically a list object that contains rich results of a linear regression such as linear coefficients (numeric vectors), residuals (numeric vectors), QR decomposition (a list containing a matrix and other objects), and so on.
It is very handy to extract the information without calling different functions each time because these results are all packed into a list.
We can use list()
to create a list, as the function name suggests. Different types of objects can be put into one list. For example, the following code creates a list that contains a single-element numeric vector, a two-entry logical vector, and a character vector of three values:
l0 <- list(1, c(TRUE, FALSE), c("a", "b", "c")) l0 ## [[1]] ## [1] 1 ## ## [[2]] ## [1] TRUE FALSE ## ## [[3]] ## [1] "a" "b" "c"
We can assign names to each list entry using named arguments:
l1 <- list(x = 1, y = c(TRUE, FALSE), z = c("a", "b", "c")) l1 ## $x ## [1] 1 ## ## $y ## [1] TRUE FALSE ## ## $z ## [1] "a" "b" "c"
There are various ways to access the elements of a list. The most common way is to use a dollar-sign $
to extract the value of a list element by name:
l1 <- list(x = 1, y = c(TRUE, FALSE), z = c("a", "b", "c"), m = NULL) l1$x ## [1] 1 l1$y ## [1] TRUE FALSE l1$z ## [1] "a" "b" "c" l1$m ## NULL
Note that if we ask for a non-existing element m
, NULL
will be returned.
Alternatively, we can supply a number in double square brackets to extract the value of the nth list member. For example, we can extract the value of the second member of list l1
, as follows:
l1[[2]] ## [1] TRUE FALSE
With the same notation, we can also supply a name to extract the value of the list member with that name, just like using a dollar sign:
l1[["y"]] ## [1] TRUE FALSE
It can be more flexible to use double square brackets for value extraction from a list because, sometimes, we might not know which member we need to extract before a computation:
member <- "z" # you can dynamically determine which member to extract l1[[member]] ## [1] "a" "b" "c"
Here, we supply a runtime-evaluated, single-element character vector to the brackets. But why should we use double brackets here? Where are the single brackets?
In many cases, we need to extract multiple elements from a list. These multiple members also construct a list as a subset of the original list.
To subset a list, we can use single-square-bracket notation, just like what we use for vectors and matrices. We can extract some elements of a list and put them into a new list.
The notation is very much consistent with how it works for vectors. We can extract elements from a list by name using a character vector, or by position using a numeric vector, or by criterion using a logical vector:
l1["x"] ## $x ## [1] 1 l1[c("x", "y")] ## $x ## [1] 1 ## ## $y ## [1] TRUE FALSE l1[1] ## $x ## [1] 1 l1[c(1, 2)] ## $x ## [1] 1 ## ## $y ## [1] TRUE FALSE l1[c(TRUE, FALSE, TRUE)] ## $x ## [1] 1 ## ## $z ## [1] "a" "b" "c"
To summarize, we can say that [[
means extracting one element from a vector or list, and [
means subsetting a vector or list. Subsetting a vector will result in a vector. Likewise, subsetting a list will result in a list.
Irrespective of whether the list members have already got names when the list is created, we can always name or rename the members of a list, by simply naming a vector:
names(l1) <- c("A","B","C") l1 ## $A ## [1] 1 ## ## $B ## [1] TRUE FALSE ## ## $C ## [1] "a" "b" "c"
To remove their names, we replace the names of l1
with NULL
:
names(l1) <- NULL l1 ## [[1]] ## [1] 1 ## ## [[2]] ## [1] TRUE FALSE ## ## [[3]] ## [1] "a" "b" "c"
Once the names of list members are removed, we can no longer access the list members by name but by position and logical criterion.
Setting the values in a list is as straightforward as working with vectors:
l1 <- list(x = 1, y = c(TRUE, FALSE), z = c("a", "b", "c")) l1$x <- 0
If we assign a value to a nonexisting member, we will add a new member to the list with the given name or position:
l1$m <- 4 l1 ## $x ## [1] 0 ## ## $y ## [1] TRUE FALSE ## ## $z ## [1] "a" "b" "c" ## ## $m ## [1] 4
Also, we can set multiple values at the same time:
l1[c("y", "z")] <- list(y = "new value for y", z = c(1, 2)) l1 ## $x ## [1] 0 ## ## $y ## [1] "new value for y" ## ## $z ## [1] 1 2 ## ## $m ## [1] 4
If we need to remove some of the members in a list, just assign the NULL
value to them:
l1$x <- NULL l1 ## $y ## [1] "new value for y" ## ## $z ## [1] 1 2 ## ## $m ## [1] 4
We can remove more than one member from a list altogether:
l1[c("z", "m")] <- NULL l1 ## $y ## [1] "new value for y"
Many functions in R are related to lists. For example, if we are not sure whether an object is a list or not, we can call is.list()
to find out:
l2 <- list(a = c(1, 2, 3), b = c("x", "y", "z", "w")) is.list(l2) ## [1] TRUE is.list(l2$a) ## [1] FALSE
Here, l2
is a list, and butl2$a
is a numeric vector rather than a list.
We can also convert a vector to a list using as.list()
:
l3 <- as.list(c(a = 1, b =2, c = 3)) l3 ## $a ## [1] 1 ## ## $b ## [1] 2 ## ## $c ## [1] 3
It is also easy to coerce a list to a vector by calling unlist
that basically converts all list members and puts them to a vector of a compatible type:
l4 <- list(a = 1, b = 2, c = 3) unlist(l4) ## a b c ## 1 2 3
If we unlist a list of numbers and texts in mixture, all members will be converted to the closest type that each one can be converted to:
l4 <- list(a = 1, b = 2, c = "hello") unlist(l4) ## a b c ## "1" "2" "hello"
Here, l4$a
and l4$b
are numbers and can be converted to a character; however, butl4$c
is a character vector and cannot be converted to numeric values. Therefore, their closest type that is compatible with all elements is a character vector.