R语言中的列表:如何创建和选择元素

⚡ 智能摘要

R 语言的 List 对象可以将向量、矩阵、数据框甚至其他列表存储在同一个容器中。`list()` 函数用于创建 List,而双中括号则可以提取其中的任何元素。

  • 🧺 混合存储: 列表可以存储不同类型和长度的对象,而普通的向量则无法做到这一点。
  • 🛠️ 构建步骤: list(vect, mat, df) 将向量、矩阵和数据框打包到一个对象中。
  • 🔢 职位访问: my_list[[2]] 返回元素本身,而 my_list[2] 返回一个包含一个元素的列表。
  • 🏷️ 命名元素: 在创建时标记元素可以通过 $ 运算符或 [[“name”]] 访问。
  • ✏️ 就地修改: 重新赋值会更新元素,而赋值为 NULL 则会将其从列表中删除。
  • 🔗 展平: unlist() 将嵌套列表合并为一个原子向量,以便进行后续分析。

R 列表:在 R 编程中创建列表并选择元素

什么是 R 列表?

R 列表 是 R 编程中的一个对象,其中包括矩阵、向量、数据框或列表。R 列表也用于存储对象集合并在需要时使用它们。我们可以将 R 列表想象成一个可以放置许多不同物品的袋子。当我们需要使用某件物品时,我们可以打开袋子并使用它。

与向量的对比正是包的用处所在。向量强制每个元素归为一种类型,因此将单词放在数字旁边会将数字转换为文本。列表则没有这样的规则,所以它可以容纳一个包含五个元素的向量,也就是两行数据。 矩阵 并排显示一个 714 行的数据框,且不做任何修改。

R 中的列表语法

在 R 编程中,我们可以使用 list() 函数来创建列表:

list(element_1, ...)
arguments:
-element_1: store any type of R object
-...: pass as many objects as specifying. each object needs to be separated by a comma

每个参数都可以直接传递,也可以采用以下形式传递: 标签 = 价值裸参数会变成一个位置元素,只能通过其编号访问;而带标签的参数还会获得一个名称。R 基础参考 列表() 文档记录了两种形式,并指出 unlist() 的作用类似于 as.list() 的逆操作。

如何在 R 中创建列表

以下是在 R 中创建列表的分步过程:

在下面的例子中,我们将创建三个不同的对象,一个向量、一个矩阵和一个 数据帧 在 R 中使用列表函数。

步骤 1)创建矢量

使用以下代码在 R 中创建一个向量

# Vector with numeric from 1 up to 5
vect  <- 1:5

冒号运算符将序列 1、2、3、4、5 构建为一个整数向量,赋值箭头将其存储在名为 vect 的向量下。

步骤2)创建矩阵

现在,使用以下代码创建一个矩阵。

# A 2x 5 matrix
mat  <- matrix(1:9, ncol = 5)
dim(mat)

这里有两个细节需要注意。一个两行五列的网格需要十个值,但只提供了九个,因此 R 会重复使用序列,并打印一条警告,提示数据长度不是行数的倍数。正是由于重复使用了这些值,才导致打印出的矩阵第二行以 1 而不是 10 结尾。然后调用 dim() 函数会报告矩阵的形状。

输出:

## [1] 2 5

步骤 3)创建数据框

使用以下代码在 R 中创建数据框

# select the 10th row of the built-in R data set EuStockMarkets
df <- EuStockMarkets[1:10,]

注释中提到了第十行,但表达式保留了内置的 EuStockMarkets 系列的前十行。子集操作还会删除时间序列类,因此返回的是一个数值矩阵而不是真正的数据框,这正是为什么下面打印的结果带有 [1,] 样式的行标签而不是行名称的原因。每当需要真正的数据框时,请将调用包装在 as.data.frame() 中。

步骤 4)在 R 中创建列表

现在,我们可以使用以下代码将三个对象放入 R 列表中

# Construct list with these vec, mat, and df:
my_list <- list(vect, mat, df)
my_list

输出:

## [[1]]
## [1] 1 2 3 4 5

## [[2]]
##       [,1] [,2] [,3] [,4] [,5]
## [1,]    1    3    5    7    9
## [2,]    2    4    6    8    1

## [[3]]
##          DAX    SMI    CAC   FTSE
##  [1,] 1628.75 1678.1 1772.8 2443.6
##  [2,] 1613.63 1688.5 1750.5 2460.2
##  [3,] 1606.51 1678.6 1718.0 2448.2
##  [4,] 1621.04 1684.1 1708.1 2470.4
##  [5,] 1618.16 1686.6 1723.1 2484.7
##  [6,] 1610.61 1671.6 1714.3 2466.8
##  [7,] 1630.75 1682.9 1734.5 2487.9
##  [8,] 1640.17 1703.6 1757.4 2508.4
##  [9,] 1635.47 1697.5 1754.0 2510.5
##  [10,] 1645.89 1716.3 1754.3 2497.4

输出结果中的三个双括号标题表示列表位置。没有任何数据被扁平化或强制转换:第一个元素仍然是一个整数向量,第二个元素仍然是一个矩阵,第三个元素仍然包含全部十行股票价格。

从 R 列表中选择元素

创建列表后,我们可以轻松地访问它。我们需要使用 `[[index]]` 来选择列表中的元素。双中括号内的值表示我们要选择的项目在列表中的位置。trac例如,我们在括号内传递 2,R 返回列出的第二个元素。

现在在这个 R 教程,让我们尝试选择 R 中名为 my_list 的列表的第二项,我们使用 my_list[[2]]

# Print second element of the list
my_list[[2]]

输出:

##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    3    5    7    9
## [2,]    2    4    6    8    1

单括号和双括号不能互换使用,混淆它们是列表错误最常见的原因。一个括号保留包装器,两个括号移除包装器。

口语 返回的结果 结果的类别 在以下情况下使用
my_list[[2]] 存储的元素本身 无论存储了什么,这里都是一个矩阵 你需要对一个元素进行计算。
my_list[2] 包含该元素的子列表 名单 你需要保留列表包装器
my_list[c(1, 3)] 所选元素的子列表 名单 你需要同时使用多个元素。
my_list[-2] 除了第二个元素之外的每个元素 名单 你需要删除一个元素
# Compare the two bracket styles
class(my_list[[2]])   # the matrix itself
class(my_list[2])     # a list of length one

# Several elements, and everything but one element
my_list[c(1, 3)]
my_list[-2]

R语言中的命名列表

位置索引很脆弱:插入一个元素后,它后面的每个数字都会发生偏移。给元素命名可以消除这种风险,并使代码读起来就像它所描述的数据一样。可以在构建列表时提供名称,也可以之后使用 `names()` 函数添加名称。

# Tag each element at creation time
named_list <- list(numbers = vect, grid = mat, prices = df)

# Three ways to reach the same element
named_list$grid
named_list[["grid"]]
named_list["grid"]      # returns a one-element list

# Read or replace the names later
names(named_list)
names(named_list)[2] <- "grid_2x5"

$ 运算符和双括号在一个容易让初学者感到困惑的地方有所不同。$ 运算符执行的是部分匹配,因此 `named_list$pri` 仍然会找到价格,而 `named_list[[“pri”]]` 会返回 NULL,因为除非指定 `exact = FALSE`,否则双括号会进行精确匹配。部分匹配在控制台中很方便,但在脚本中不安全,因此在保存的代码中最好使用精确匹配。

# A compact map of the whole structure
str(named_list)

str() 函数会打印每个元素一行,包含其类型和大小,这是在对列表进行索引之前确认列表是否包含预期内容的最快方法。

修改、添加和删除 R 列表中的元素

列表一旦创建就不是固定的。读取元素的访问器也可以写入元素,因此更新、扩展和缩减列表都使用普通的赋值操作。

# Replace an element in place
named_list[["numbers"]] <- 1:10

# Add a new element simply by naming it
named_list[["created"]] <- Sys.Date()

# Append without a name: the element lands at the end
named_list <- append(named_list, list(TRUE))

# Delete an element by assigning NULL to it
named_list[["created"]] <- NULL
  • 更换: 给现有元素赋值会覆盖该元素,但不会改变其位置。
  • 添加: 将不存在的名称赋值给一个元素,会在列表末尾添加一个新元素。
  • 附加: append() 函数的第二个参数是一个列表,因此在传递裸值之前,请先用 list() 函数将其包装起来。
  • 删除: 使用双括号赋值为 NULL 会删除该元素并缩短列表。

一个常见的错误是:由于 NULL 表示删除,因此不能用双括号存储真正的 NULL 值。应该使用单括号赋值,例如 `named_list[“empty”] <- list(NULL)`,这样既保留了空位,又将 NULL 值放入其中。

R语言中的嵌套列表和unlist()函数

由于列表可以容纳任何 R 对象,因此它也可以容纳另一个列表。嵌套是 R 中配置对象、JSON 响应和模型结果的常用表示方式,因此访问嵌套列表和展平嵌套列表都是日常操作。

# A list whose elements are themselves lists
project <- list(
  meta = list(owner = "analyst", year = 2026),
  data = list(vect, mat)
)

# Chain the accessors to reach an inner value
project$meta$year
project[["data"]][[2]]

# Flatten every level into one atomic vector
flat <- unlist(project)

# Strip a single level and keep the rest as a list
half <- unlist(project, recursive = FALSE)

扁平化是有代价的,值得理解。`unlist()` 必须返回一个原子向量,因此它会将每个值强制转换为最通用的类​​型,顺序为逻辑值、整数、双精度浮点数、字符。因此,一个混合了数字和文本的嵌套列表将完全以文本形式返回。结果的名称由外部标签和内部标签连接而成,这就是为什么元素以 `meta.owner` 而不是 `owner` 的形式返回的原因。

如果不需要复合名称,请传递 `use.names = FALSE`;如果只想移除最外层,请设置 `recursive = FALSE`。如果值必须保持其类型,请保持结构完整并遍历列表,而不是将其扁平化。

R 中的内置数据框

列表通常包含从磁盘或网络读取的数据,因此了解数据框在存储到列表之前是如何到达的很有帮助。在创建我们自己的数据框之前,我们可以先查看在线提供的 R 数据集。监狱数据集是一个 714×5 的维度。我们可以使用 `tail()` 函数快速查看数据框的底部。类似地,`head()` 函数显示数据框的顶部。您可以使用 `head(df, 5)` 指定显示的行数。我们将在后续章节中学习更多关于 `read.csv()` 函数的内容。 在 R 中导入数据 教程。

PATH <-'https://raw.githubusercontent.com/guru99-edu/R-Programming/master/prison.csv'
df <- read.csv(PATH)[1:5]
head(df, 5)

输出:

##   X state year govelec black
## 1 1     1   80       0 0.2560
## 2 2     1   81       0 0.2557
## 3 3     1   82       1 0.2554
## 4 4     1   83       0 0.2551
## 5 5     1   84       0 0.2548

`read.csv()` 后面的 `[1:5]` 选择的是前五列,而不是前五行,因为数据框本身就是一个列列表。对列表使用单括号索引会返回一个更小的列表,而在这里,这个更小的列表仍然是一个数据框。

我们可以用 str 检查数据框的结构:

# Structure of the data
str(df)

输出:

## 'data.frame':    714 obs. of  5 variables:
##  $ X      : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ state  : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ year   : int  80 81 82 83 84 85 86 87 88 89 ...
##  $ govelec: int  0 0 1 0 0 0 1 0 0 0 ...
##  $ black  : num  0.256 0.256 0.255 0.255 0.255 ...

所有变量都存储在 数字的 格式。每行的 $ 前缀与之前命名列表中使用的运算符相同,这证实了数据框是由等长列组成的列表,并带有矩形标签。因此,所有关于命名、选择和删除列表元素的知识都可以直接应用到数据框中。 R 数据类型 并映射到数据框列。

常见问题

向量强制所有元素使用同一种类型,因此混合文本和数字会将所有内容强制转换为字符型。列表则保持每个元素的原始类型,包括不同长度的向量、矩阵和函数。可以使用 `typeof()` 函数来区分向量和列表。

调用 `is.list(x)`,对于列表和键值对列表,它返回 TRUE。`class(x)` 返回对象类型,而 `typeof(x)` 即使对于数据框也返回“list”,因为数据框本质上是一个等长列的列表。

`length(my_list)` 只计算顶层元素,所以嵌套列表也只算一个元素。`lengths(my_list)` 一次性返回每个元素的大小,比 `low` 更快更清晰。ping 当元素不同时。

lapply(my_list, FUN) 应用一个 function 对列表中的每个元素进行操作并返回一个列表。`sapply()` 函数会尽可能地将结果简化为向量或矩阵。使用简单的 `for` 循环遍历 `seq_along(my_list)` 也能实现相同的功能,而且代码清晰易懂。

当每个元素的长度都相同时,`as.data.frame(my_list)` 可以直接使用。对于等宽行列表,`do.call(rbind, my_list)` 会先将它们堆叠起来。 dplyr 生态系统提供了 as_tibble() 函数,用于在列类型必须保持不变的情况下进行操作。

列表没有内置排序功能,因此例如trac首先,比较键:my_list[order(sapply(my_list, length))] 按元素大小重新排序。对于矩形数据,转换为框架并 对数据框进行排序 改用 order()。

AI 助手会读取 str() 函数的输出,并解释为什么某个索引返回的是 NULL 或子列表而不是实际值。它们还会建议正确的括号形式,设计 lapply() 管道,并在静默类型强制转换破坏结果之前将其标记出来。

是的。 GitHub 副驾驶 在...工作 RStudio 2023.09.0 及更高版本可通过“工具”>“全局选项”>“代码助手”设置启用。它可以自动补全注释中的 list()、names() 和 unlist() 调用,但所有建议仍需审核。

总结一下这篇文章: