课程
几乎所有 R 数据分析项目都会涉及从更大的数据集中提取特定的行、列或数值。子集操作是在 R 中实现这一点的方式:使用括号表示法 [] 进行底层索引控制,使用 subset() 基于条件进行可读性强的筛选,或使用 dplyr 中的 filter() 和 select() 等动词构建现代数据管道。
要点速览
-
使用括号表示法
df[rows, cols]按位置对行和列进行子集选择 -
使用
$提取单列为向量:df$column_name -
在交互式会话中,使用
subset(df, condition)进行可读的条件筛选 -
在选择单列且希望结果保持为数据框时,添加
drop = FALSE -
在生产代码和数据管道中,优先使用括号表示法或
dplyr::filter(),而非subset()
选择行
下面是在名为 debt 的数据框上进行子集操作的示例。
debt[3:6, ]
name payment
3 Dan 150
4 Rob 50
5 Rob 75
6 Rob 100
df[rows, columns] 语法是 R 子集操作的基础。这里,3:6 选择第 3 到第 6 行,逗号后的空白表示“返回所有列”。结果是一个更小的数据框,列结构与原始数据相同。
从特定列选择行
当您只选择单列时,需要留意结果的“简化”行为。仅选择 payment 列的前三行会将结果简化为一个向量。
debt[1:3, 2]
100 200 150
这里的 2 指的是 debt 的第二列,即 payment。由于我们选择了单列,R 会丢弃数据框结构并返回一个普通的数值向量。
数据框格式
要将其保持为数据框,只需像下面这样添加 drop = FALSE:
debt[1:3, 2, drop = FALSE]
payment
1 100
2 200
3 150
选择特定列
您也可以使用 $ 运算符选择单列,写作 dataframe$column。下面的示例从 debt 中提取 payment 列。R 会将结果返回为向量,而不是数据框。
debt$payment
100 200 150 50 75 100
使用 subset() 函数
当需要创建更复杂的子集或基于条件的子集时,更进一步的方法是使用 subset() 函数。比如,若您想查看名为 Dan 的人的债务。您可以直接用括号选择他的记录并求和,但这种方式不够健壮,尤其是在数据集可能发生变化的情况下。
# 这能用,但不具可读性
debt[1:3, ]
对分类变量使用 subset()
更好的做法是使用 subset() 选择 name 列等于 Dan 的行。请注意需要使用双等号(==),它被称为关系运算符。
# 这能用,但既不直观也不健壮
debt[1:3, ]
# 更加直观!
subset(debt, name == "Dan")
name payment
1 Dan 100
2 Dan 200
3 Dan 150
结果只包含 name 等于 "Dan" 的三行。注意原始行号(1、2、3)被保留,这有助于将结果追溯回源数据框。
对数值变量使用 subset()
我们也可以对子集进行数值条件筛选。若想查看付款额等于 $100 的行,可以这样做:
subset(debt, payment == 100)
name payment
1 Dan 100
6 Rob 100
有两行满足该条件:第 1 行(Dan)和第 6 行(Rob)的付款额都正好是 $100。这表明 subset() 会不受 name 影响扫描每一行并返回所有匹配项。
访问与子集化数据框
如果您只需要公司 A 的现金流怎么办?这里 subset() 函数对另一个数据框 cash 进行行筛选:
subset(cash, company == "A")
company cash_flow year
1 A 1000 1
2 A 4000 3
3 A 550 4
请记住:
-
传递给
subset()的第一个参数是数据框名称,这里是cash。 -
注意不要给
company加引号! -
==是相等运算符。它用于测试两个值是否相等并返回逻辑向量。
subset() 方法的交互示例
在下面的示例中,我们使用 subset() 选择 cash 中属于公司 B 的行,然后筛选出 1 年到期的现金流。
# 关于公司 B 的行
subset(cash, company == "B")
# 1 年到期的现金流
subset(cash, year == 1)
运行上述代码,会得到如下结果:
company cash_flow year
4 B 1500 1
5 B 1100 2
6 B 750 4
7 B 6000 5
company cash_flow year
1 A 1000 1
4 B 1500 1
在我们的“Introduction to R for Finance”课程中,亲自试一试。
本文内容摘自 DataCamp 由 Lore Dirick 授课的 Introduction to R for Finance 课程。
使用 dplyr 进行子集操作
tidyverse 套件中的 dplyr 为数据框子集操作提供了现代化替代方案。其 filter() 用于选择行,select() 用于选择列,二者都采用基于管道的语法将操作串联起来。
library(dplyr)
# 过滤 name 为 "Dan" 的行
debt %>% filter(name == "Dan")
# 仅选择 payment 列(保持为数据框)
debt %>% select(payment)
# 组合:在一条管道中同时过滤行并选择列
debt %>%
filter(payment >= 100) %>%
select(name, payment)
与 subset() 不同,dplyr 函数可在其他函数内部可靠运行,并能在同一管道内处理分组操作、连接与汇总。如果您在构建数据分析工作流,而非一次性查询,dplyr 是标准工具。
子集方法对比
我们用一张表来比较 R 中不同的子集方法:
| 方法 | 语法 | 最适合 | 备注 |
|---|---|---|---|
括号 [] |
df[rows, cols] |
低层控制、编程 | 适用于所有 R 对象 |
$ |
df$col |
快速提取单列 | 返回向量,而非数据框 |
subset() |
subset(df, cond) |
交互式筛选 | 不建议在函数内部使用 |
dplyr::filter() |
df %>% filter(cond) |
数据管道、可读代码 | 需安装 dplyr 包 |
总结
R 提供多种子集方式:括号表示法 [] 提供全面控制,subset() 适合可读的交互式筛选,dplyr::filter() 适合数据管道。括号适用于任何 R 对象,subset() 便于一次性探索,而 dplyr 在串联多步操作时更具扩展性。
若想更深入学习 R 的数据处理,推荐 DataCamp 的 Data Manipulation with dplyr 课程或 Introduction to the Tidyverse 课程。
R 子集操作常见问题
R 中 subset() 与 dplyr::filter() 有何区别?
两者都能基于条件筛选行,但在编程场景下行为不同。subset() 使用非标准求值,交互式使用便捷,但在用户自定义函数中不够可靠。dplyr::filter() 旨在在函数内部安全运行,并与管道运算符(%>%)集成以串联多步数据处理。
如何一次性在 R 中选择多列?
您可以在括号中传入列名或列索引的向量。例如,df[, c("name", "payment")] 按名称选择两列,而 df[, c(1, 3)] 按位置选择两列。使用 dplyr 时,可以写 select(df, name, payment),或用 starts_with() 与 contains() 等辅助函数按模式匹配列。
为何在 R 中选择单列会返回向量而非数据框?
当仅使用括号表示法选择单列时,R 会默认简化结果。该行为由参数 drop 控制,默认值为 TRUE。若要让结果保持为数据框,请在括号表达式中添加 drop = FALSE,例如 df[, 2, drop = FALSE]。
我可以根据多个条件在 R 中筛选行吗?
可以。您可以使用 &(与)和 |(或)组合条件。例如,subset(df, name == "Dan" & payment > 100) 返回 name 为 Dan 且 payment 大于 100 的行。使用 dplyr 时,可在 filter() 中用逗号分隔条件:df %>% filter(name == "Dan", payment > 100)。