R 数据框:如何创建、附加、选择和子集

⚡ 智能摘要

R 数据框以相等的长度存储不同类型的列,这使其成为分析的标准矩形结构。方括号用于对行和列进行切片,美元符号用于指定列,而 `subset()` 函数用于按条件进行筛选。

  • 🧱 结构体: 数据框是等长向量的列表,因此每一列可以包含不同类型的数据。
  • 🛠️ 创建: data.frame(a, b, c, d) 将四个向量连接起来,names() 随后重命名每一列。
  • ✂️ 切片: df[A, B] 读取时先读取行,再读取列,空白侧会选择该侧的所有内容。
  • 追加: df$quantity 会分配一个新列,前提是新向量与行数完全匹配。
  • 🔎 过滤: subset(df, subset = price > 5) 只保留条件评估为 TRUE 的行。
  • ⚠️ 版本 Shift: 自 R 4.0.0 起,stringsAsFactors 的默认值为 FALSE,因此文本列保持字符型。

R 数据框创建、追加、选择和子集

什么是数据框?

A 数据框 数据框是由长度相等的向量组成的列表。矩阵只能包含一种类型的数据,而数据框可以接受不同的数据类型(数值型、字符型、因子型等)。

该定义将数据框置于 R 语言中你会不断遇到的两个相邻节点之间。 矩阵 是矩形的,但只有一种类型,而且 名单 数据是多类型的,但格式混乱。数据框从每种类型中借用了一个属性。

结构 列类型 元素长度 典型用途
向量 仅一种类型 单序列 一个变量
矩阵 仅一种类型 矩形 数值代数
列表 任何类型组合 可能因元素而异 任意收集
数据框 任何类型组合 每列长度相等 表格分析

因为数据框本质上是一个列表,所以列表上使用的访问器在这里也适用,这就是为什么本教程后面会再次出现美元符号的原因。

如何创建数据框

我们可以在 R 通过将变量 a、b、c 和 d 传递给 data.frame() 函数,我们可以创建数据框并使用 names() 函数命名列,只需指定每个变量的名称即可。

data.frame(df, stringsAsFactors = TRUE)

参数:

  • df:它可以是一个矩阵,转换为数据框,也可以是一组变量的集合
  • 字符串作为因子控制字符向量是否转换为因子列。在旧版本中,默认值为 TRUE,而自 R 4.0.0 起为 FALSE,因此当需要明确指定行为时,请显式传递此参数。

我们可以通过组合四个相同长度的变量在 R 中为我们的第一个数据集创建一个数据框。

# Create a, b, c, d variables
a <- c(10,20,30,40)
b <- c('book', 'pen', 'textbook', 'pencil_case')
c <- c(TRUE,FALSE,TRUE,FALSE)
d <- c(2.5, 8, 10, 7)
# Join the variables to create a data frame
df <- data.frame(a,b,c,d)
df

输出:

##     a         b     c     d
## 1  10        book  TRUE   2.5
## 2  20         pen  FALSE  8.0
## 3  30    textbook  TRUE   10.0
## 4  40 pencil_case  FALSE  7.0

每个向量都成为一列,每个向量中的四个值则成为四行。需要注意的是,参数没有命名,因此 R 会根据变量名自动生成列标题。

我们可以看到列标题与变量同名。我们可以使用函数 names() 更改 R 中的列名。查看下面的 R 创建数据框示例:

# Name the data frame
names(df) <- c('ID', 'items', 'store', 'price')
df

输出:

##   ID       items store price
## 1 10        book  TRUE   2.5
## 2 20         pen FALSE   8.0
## 3 30    textbook  TRUE  10.0
## 4 40 pencil_case FALSE   7.0

赋值给 names() 函数会一次性替换所有列标题,因此替换向量必须每列恰好包含一个条目。要重命名单个列,请对 names 向量进行索引,例如 names(df)[2] <- 'product'。

# Print the structure
str(df)

输出:

## 'data.frame':    4 obs. of  4 variables:
##  $ ID   : num  10 20 30 40
##  $ items: Factor w/ 4 levels "book","pen","pencil_case",..: 1 2 4 3
##  $ store: logi  TRUE FALSE TRUE FALSE
##  $ price: num  2.5 8 10 7

版本说明。 上述输出是在 R 4.0.0 之前的版本中生成的,该版本默认将 `data.frame()` 函数将字符向量转换为因子,因此 `items` 被报告为具有四个水平的因子。R 基础文档记录了这是出厂默认行为。 对于 R 4.0.0,已将 stringsAsFactors 设置为 FALSE。在当前版本上运行相同的代码会将数据项作为纯字符列,并且 `str()` 函数会输出 `chr` 字符。在 `data.frame()` 调用中添加 `stringsAsFactors = TRUE` 即可重现打印输出,或者阅读相关文档。 因子教程 当确实需要这些因素时。

切片数据框

框架搭建完成后,下一步就是将框架的各个部分提取出来。切片是 R 语言中实现这一目标的基本方法,它使用与向量相同的方括号,只是需要处理两个位置信息而不是一个。

可以对数据框的值进行切片。我们选择要返回的行和列,并将其放在方括号内,方括号前面加上数据框的名称。

数据框由行和列组成,df[A, B]。A 表示行,B 表示列。我们可以通过指定行和/或列来进行切片。

从下图1可以看出,左侧部分代表 行, 右边是 . 注意符号 : 表示 例如,1:3 表示从 1 中选择值 3.

R 数据框切片语法为 df[A, B],其中逗号左侧为行,逗号右侧为列。

下图展示了如何访问数据框的不同选项:

R 数据框上的彩色箭头分别表示单个单元格、行范围、整列和块的选择。

  • 黄色箭头选择 1 in 2
  • 绿色箭头选择 1到2
  • 红色箭头选择 1
  • 蓝色箭头选择 1年至3年以及 3到4

请注意,如果我们将左侧部分留空,R 将选择 所有的行以此类推,如果我们让右边的部分空白,R 会选择 所有的列.

我们可以在控制台中运行代码:

## Select row 1 in column 2
df[1,2]

输出:

## [1] book
## Levels: book pen pencil_case textbook

之所以会出现 Levels 行,是因为 items 是本次会话中的一个因子。在 R 4.0.0 及更高版本中,同样的调用会输出单个字符串“book”,而不会输出 Levels 行。

## Select Rows 1 to 2
df[1:2,]

输出:

##   ID items store price
## 1 10  book  TRUE   2.5
## 2 20   pen FALSE   8.0
## Select Columns 1
df[,1]

输出:

## [1] 10 20 30 40

使用 `df[,1]` 选择单列会移除数据框包装器并返回一个普通的向量。如果需要单列数据框,可以添加 `drop = FALSE`,例如 `df[, 1, drop = FALSE]`。

## Select Rows 1 to 3 and columns 3 to 4
df[1:3, 3:4]

输出:

##   store price
## 1  TRUE   2.5
## 2 FALSE   8.0
## 3  TRUE  10.0

也可以通过列名来选择列。例如,以下代码示例trac它有两列:ID 和 store。

# Slice with columns name
df[, c('ID', 'store')]

输出:

##   ID store
## 1 10  TRUE
## 2 20 FALSE
## 3 30  TRUE
## 4 40 FALSE

在生产代码中,名称比位置更安全,因为插入或重新排序列会悄无声息地改变 df[, 3] 所指的内容,而 df[, 'store'] 则始终指向相同的数据。

向数据框添加一列

你也可以向数据框中添加一列。在 R 语言中,你需要使用符号 $ 来命名新变量,然后向数据框中添加一列。

# Create a new vector
quantity <- c(10, 35, 40, 5)

# Add `quantity` to the `df` data frame
df$quantity <- quantity
df

输出:

##   ID       items store price quantity
## 1 10        book  TRUE   2.5       10
## 2 20         pen FALSE   8.0       35
## 3 30    textbook  TRUE  10.0       40
## 4 40 pencil_case FALSE   7.0        5

注意:向量中的元素个数必须与数据框中的元素个数相等。在 R 中执行以下语句向数据框添加一列。

quantity <- c(10, 35, 40)
# Add `quantity` to the `df` data frame
df$quantity <- quantity

给出错误:

Error in `

lt;-.data.frame`(`*tmp*`, quantity, value = c(10, 35, 40))
replacement has 3 rows, data has 4

该消息会指出数据框的替换运算符,并说明两种替换方式的计数,从而准确地告诉你需要修复什么。R 仅在替换值的长度能整除行数时才会重复使用,这就是为什么长度为 1 的值(例如 `df$currency <- 'EUR'`)会被接受,而长度为 3 的向量(对应四行数据)则会被拒绝。`cbind()` 函数以相同的方式添加列,而 `rbind()` 函数则是添加行的对应函数。

选择数据框的一列

有时,我们需要存储数据框中的某一列以供后续使用,或者对某一列执行特定操作。我们可以使用美元符号 $ 来选择数据框中的列。

# Select the column ID
df$ID

输出:

## [1] 1 2 3 4

仔细阅读输出结果。ID 由向量 c(10, 20, 30, 40) 构建而成,因此实时控制台会输出 10 20 30 40;上面显示的 1 2 3 4 是行位置,而不是存储的值。行首的 [1] 只是该行第一个元素的索引,并非数据的一部分。

三种路径都指向同一列:df$ID、df[['ID']] 和 df[, 'ID']。美元符号表示部分匹配,因此 df$I 仍然会找到 ID,这在控制台中很方便,但在已保存的脚本中却存在风险。

对数据框进行子集化

在上一节中,我们无条件地选择了整列。可以 子集 根据某个条件是否真实。

我们使用 subset() 函数。

subset(x, condition)
arguments:
- x: data frame used to perform the subset
- condition: define the conditional statement

我们只想退回价格高于 5 的商品,所以我们可以这样做:

# Select price above 5
subset(df, subset = price > 5)

输出:

ID       items store price
2 20         pen FALSE     8
3 30    textbook  TRUE    10
4 40 pencil_case FALSE     7

行标签 2、3 和 4 是从 df 继承过来的原始行名,因此您可以判断第一行已被过滤掉。`subset()` 函数也接受一个 `select` 参数,用于同时选择列,其等效的括号形式为 `df[df$price > 5, ]`。括号形式会保留条件缺失的 NA 行,而 `subset()` 函数会删除这些行,因此两者并非总是可以互换的。

常见问题

使用 `rbind(df, new_row)`,其中 `new_row` 是一个包含相同列且顺序相同的数据框或列表。列类型必须匹配,否则 R 会强制转换。对于大量行,建议先创建一个列表,然后再合并一次,因为重复调用 `rbind()` 会降低性能。

将其赋值为 NULL,例如 df$quantity <- NULL。负索引也有效:df[, -3] 删除第三列,而 df[, !names(df) %in% c("store")] 按名称删除列,而不取决于它们的位置。

dim(df) 同时返回行和列,而 nrow() 和 ncol() 分别返回行和列。str(df) 打印每一列的类型,summary(df) 提供每列的统计信息,head(df) 显示前六行。

对名称向量进行索引:`names(df)[2] <- "product"` 只会更改第二个标题。要按当前名称重命名,请使用 `names(df)[names(df) == "items"] <- "product"`,即使列顺序发生变化,此方法仍然有效。

tibble 是 tidyverse 数据框。它从不将字符串转换为因子,从不进行部分匹配列名,只打印前十行及其列类型,并且在使用单个括号进行子集操作时总是返回 tibble。

filter(df, price > 5) 是 dplyr 它与 select()、mutate() 和 arrange() 函数等效,并且可以通过管道链接使用。与 subset() 不同,filter() 专为程序化使用而设计,因此它在函数内部的行为是可预测的。

AI助手读取str()和summary()的输出,然后提出类型转换、缺失值处理策略和列重命名等可运行代码。它们还会解释晦涩难懂的替换错误,但所有生成的转换仍然需要与真实数据进行比对。

是的。 GitHub 副驾驶 跑进来 RStudio 2023.09.0 及更高版本可通过“工具”>“全局选项”>“代码助手”设置启用。它可以根据注释自动生成 data.frame()、subset() 和 rbind() 调用,但必须验证列名。

总结一下这篇文章: