Menu

R 语言列表:创建、访问([[、$ 与 [)和修改

列表是 R 中什么都能装的容器:混合类型、嵌套结构、整个数据框。关键在于分清 [ 什么时候返回一个更小的列表,[[ 什么时候返回元素本身。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

列表什么都能装

向量有一条硬性规定:每个元素都必须是同一种类型。列表没有这条规定。列表的每个元素可以是不同的类型、不同的长度,甚至可以是另一个列表。它是 R 的通用容器:

length() 返回 4,也就是四个元素,尽管其中一个(scores)本身包含三个数字。列表统计的是它有几个格子,而不是格子里装了什么。str()(structure,结构)是查看列表实际内容最快的方式:每个元素一行,显示名称、类型和预览。养成用 str() 的习惯:每当某个函数交给你一个东西、而你不确定它是什么时,就 str() 一下。

名称是可选的(list(1, "a", TRUE) 是合法的),但几乎总是值得加上:带名称的列表自己就能说明自己。

三种访问方式:[、[[ 和 $

这是列表中每个人都会搞错一次的部分,所以我们要把它彻底弄清楚。访问运算符有三个,它们做的是两种不同的事:

  • [ 返回一个更小的列表:同类型的容器,里面装着你选中的内容。
  • [[ 返回元素本身:格子里实际的值。
  • $ 是带字面名称的 [[ 的简写:person$age 就是 person[["age"]]。

随手打印时你看不出区别,可一旦要做计算,区别就非常明显:

person["scores"] 的类是 "list":一个长度为 1、里面装着分数向量的列表。person[["scores"]] 的类是 "numeric":向量本身,可以直接交给 mean()。如果对单方括号的版本调用 mean(),你会得到一个提示参数不是数值的错误;看到这个错误信息,几乎总是说明你在该用 [[ 的地方用了 [。

一个好记的比喻:列表是一列火车。[ 给你一列更短的火车,它仍然是火车。[[ 则打开一节车厢,把货物交给你。

那么什么时候该用 [?当你想要多个元素,并且想让它们继续打包在一起时:

你无法一次性取出两个元素“本身”,两个值需要一个容器来装,所以选取多个元素总是用单方括号,结果也总是一个列表。

还有一个区别:$ 会做部分匹配(只要没有歧义,person$sc 就能找到 scores),这在控制台里很方便,在脚本里却是隐患。在需要长期稳定运行的代码中,优先使用写全名称的 [[,它还能接受保存在变量里的名称,这是 $ 做不到的。

添加、修改、删除

列表通过普通的赋值来增长和缩短,不需要专门的追加方法:

  • 给一个还不存在的名称赋值,会添加该元素。
  • 给一个已存在的名称赋值,会替换它。
  • 给位置 length(x) + 1 赋值,会追加一个没有名称的元素。
  • 赋值 NULL 会彻底删除该元素,列表随之变短。(这是 NULL 充当删除命令的唯一场合;如果你确实需要在某个格子里保存“空”,请用 x["k"] <- list(NULL)。)

要把两个列表首尾相接,c() 对列表和对向量一样有效:c(list_a, list_b) 返回一个更长的列表。

嵌套列表

因为列表的元素可以是另一个列表,列表可以嵌套任意多层,这让它天然适合表示结构化数据,比如解析后的 JSON 或分组后的结果:

逐层深入只需要把访问符串起来,每个 $ 或 [[ 往下走一层。当嵌套结构变得难以理解时,str(company) 会一次性显示整棵树,str(company, max.level = 1) 则只显示最顶层。

用 unlist() 展平

unlist() 会把一个列表(无论嵌套多深)压平成单个向量:

有两点值得注意。第一,unlist() 会为你生成名称(math1、math2、art),所以你仍然能看出每个值来自哪里。第二,也就是陷阱所在:向量只能保存一种类型,所以展平一个混合类型的列表时,所有内容都会被强制转换成其中最灵活的类型。数字 1 变成了字符串 "1"。如果你展平之后数字变成了文本,说明这个列表并不像你以为的那样统一。

列表为什么重要

列表可能让人觉得是初学者话题,以后用不上。恰恰相反,它们在整个语言中都起着承重作用:

  • 返回多个结果的函数会返回一个列表。 R 函数只返回一个对象;列表让这一个对象既能装拟合值,又能装系数表。做回归时,你得到的模型对象就是一个(很大、带有类的)列表,对它用 str() 就能证明。
  • 数据框就是由列组成的列表:等长的向量,再加上一些额外的行为。你刚学到的一切($、[[、通过赋值 NULL 删除元素)都可以原封不动地用在数据框的列上。
  • apply 函数族(lapply() 等)接收一个列表,对每个元素应用一个函数,再返回一个列表。列表进、列表出,是 R 中重复计算的标准形态。

要点回顾

  • 列表什么都能装:混合类型、不等长度、其他列表、整个数据框。
  • [ 返回一个更小的列表;[[ 返回元素本身;$ 是带字面名称的 [[。要做数学运算?你需要的是 [[ 或 $。
  • 给新名称赋值来添加,赋值 NULL 来删除,用 c() 合并。
  • unlist() 展平为向量,过程中会强制转换混合类型,记得检查结果的类。
  • 数据框是由列组成的列表,所以这些知识可以直接迁移过去。

下一篇:矩阵,与列表相对应的、只能装一种类型、按行和列组织的结构。

常见问题

R 语言中 [ 和 [[ 有什么区别?

单方括号 [ 总是返回一个包含所选元素的列表,也就是一个同类型的更小容器。双方括号 [[ 会深入内部,返回元素本身。如果 x$scores 保存的是一个数值向量,那么 x["scores"] 是长度为 1 的列表,而 x[["scores"]] 才是那个数值向量。当你要真正使用这个值时,用 [[(或 $)。

R 语言怎么向列表添加元素?

给一个还不存在的名称赋值:x$email <- "rosa@example.com" 或 x[["email"]] <- ...,列表会自动变长。要追加一个没有名称的元素,就给下一个位置赋值:x[[length(x) + 1]] <- value。要删除一个元素,给它赋值 NULL:x$email <- NULL。

R 语言怎么把列表转换成向量?

unlist(x) 会把一个列表(包括嵌套列表)展平成单个向量。因为向量只能保存一种类型,所有内容都会被强制转换为其中最灵活的那种类型:一个包含数字和字符串的列表会变成全是字符的向量。展平之后,用 class() 检查一下结果。

Coddy 编程语言插图

和 Coddy 一起学编程

开始