2.4 列表 (list)

R中的列表是一种特殊的数据存储形式。使用list()函数来创建列表,比如list(1, 2, 3).

在一个列表和向量上分别使用四个判断数据结构类型的函数得到的结果
data is.vector() is.list() is.atomic() is.recursive()
list(1, 2, 3) TRUE TRUE FALSE TRUE
c(1 ,2 , 3) TRUE FALSE TRUE FALSE

尝试对列表和向量使用is.vector(), is.list(), is.atomic()is.recursive()函数,你会发现列表虽然也是“vector”,但我们一般说的“vector”都是指只能存储一种数据类型的atomic vector;而lists是recursive vector.

这意味着一个list能存储多种类型的数据,且可以包含子列表。列表中的每个分量可以是任何R中的对象 (object):除了常用的 (atomic) vector和另外一个(子)列表以外,还可以有dataframe/tibble和函数:

y <- list(1, c("a","あ"), list(1+3i, c(FALSE, NA, TRUE)), 
          data.frame(x = c("阿拉木图", "什切青"), y = c(2, 3)),
          t.test)
y
#> [[1]]
#> [1] 1
#> 
#> [[2]]
#> [1] "a"  "あ"
#> 
#> [[3]]
#> [[3]][[1]]
#> [1] 1+3i
#> 
#> [[3]][[2]]
#> [1] FALSE    NA  TRUE
#> 
#> 
#> [[4]]
#>          x y
#> 1 阿拉木图 2
#> 2   什切青 3
#> 
#> [[5]]
#> function (x, ...) 
#> UseMethod("t.test")
#> <bytecode: 0x7fae43b99318>
#> <environment: namespace:stats>

这个列表有5个分量,其中第3个是一个有2个分量的子列表。

2.4.1 列表的索引/取子集

使用上面的例子:

y[2] # 使用单方括号,得到的是一个只有一个分量的列表
#> [[1]]
#> [1] "a"  "あ"
y[[2]] # 使用双方括号,得到的是一个向量
#> [1] "a"  "あ"
y[[3]][[2]] # 得到的也是一个向量;父列表的索引在前,子列表的在后
#> [1] FALSE    NA  TRUE
y[[3]] # 这个位置包含两个子列表,因此得到一个有两个分量的列表
#> [[1]]
#> [1] 1+3i
#> 
#> [[2]]
#> [1] FALSE    NA  TRUE
y[[3]][[2]][2] # 得到向量时,直接在后面用单方括号
#> [1] NA

列表里的分量可以有名字;被命名的元素可以通过$符号抓取:

z <- list(c(1, 3), z2 = c(4, 5, 6), c("a", "b"))
z # `[[2]]`被`$z2`所取代
#> [[1]]
#> [1] 1 3
#> 
#> $z2
#> [1] 4 5 6
#> 
#> [[3]]
#> [1] "a" "b"
z$z2 == z[[2]] # `z[[2]]`仍然是可用的,结果和`z$z2`一样
#> [1] TRUE TRUE TRUE

2.4.2 合并与拆解

通过c()函数来合并多个列表。

c(list(1, 2), list(3, 4, list(5,6)))
# 将等同于list(1, 2, 3, 4, list(5,6))

也许你想把需要“合并”的列表作为子列表放在另一个列表里;这也很简单,在本节一开始就讲了:

list(list(1, 2), list(3, 4))
#> [[1]]
#> [[1]][[1]]
#> [1] 1
#> 
#> [[1]][[2]]
#> [1] 2
#> 
#> 
#> [[2]]
#> [[2]][[1]]
#> [1] 3
#> 
#> [[2]][[2]]
#> [1] 4

通过unlist()函数来拆解列表中的子列表。若参数recursiveTRUE(默认值),将一直拆解至无子列表的列表,如果此最简列表的所有分量都属于五种atomic vector中的数据20,此列表还会被进一步化简成向量。若recursive = FALSE,最“靠外”的一级列表(可能是多个)将会被拆解。

unlist(list(1, list(2, list(3, 4)), list(5, 6), 7, 8, 9))
# 将等同于c(1, 2, 3, 4, 5, 6, 7, 8, 9) 
# 注意被化简成了向量

unlist(list(1, list(2, list("a", 4)), list(5, TRUE), 7L, 8, 9+0i))
# 将等同于c("1", "2", "a", 4, 5, "TRUE", "7", 8, "9+0i")
# 化简成向量时,非字符元素被强制转换成字符了

unlist(list(1, list(2, list(t.test, 4)), list(5, TRUE), 7L, x, 9+0i))
# t.test无法存储于向量中,因此最简结果为一个list:
# list(1, 2, t.test, 4, 5, TRUE, 7L, x, 9+0i)


unlist(list(1, list(2, 3, list(4, 5)), list(6, 7), 8, 9), recursive = FALSE)
# 将等同于list(1, 2, 3, list(4, 5), 6, 7, 8, 9)

因此,当A, B为列表,unlist(list(A, B), recursive = FALSE)等同于c(A, B).

2.4.3 其他性质和操作

上面说到unlist(list(A, B), recursive = FALSE)等同于c(A, B),你可能很想用==验证一下。很不幸,你会得到一条错误信息:

comparison of these types is not implemented

在第2.6.2节讲过,==只能用于atomic vectors;对于列表(和其他对象)可以用identical()函数确认两者是否完全一致。

A <- list("a", 1, TRUE); B <- list(5+8i, NA, 4L)
C1 <- unlist(list(A, B), recursive = FALSE); C2 <- c(A, B)
identical(C1, C2)
#> [1] TRUE

  1. dataframe也是可以unlist成向量的,但是并不实用。(试试unlist(list(data.frame(x = c(1,2), y = c(3,4)), 5, 6))