R: R 数据框与因子:tidyverse 数据科学基础

最后更新:2026-08-26

前面 9 课我们学了向量、矩阵、列表——它们各自有局限。数据框 (data.frame) 解决了所有问题:像 Excel 表格一样好读,又像矩阵一样高效。这一课我们学 R 真正的"主角"——数据框。

数据框 + tidyverse 是 R 数据科学的核心。从这一课开始,我们正式进入"用 R 做数据分析"的世界。

1. 你将学到



2. 一个 Excel 转 R 的故事

(1) 痛点:Excel 撑不住

小陈的市场调研表有 10,000 行 × 8 列:

TEXT 📖 仅展示
员工ID  姓名  部门    工资   工龄 绩效 性别 入职日期
2024001 Alice  数据部  15000  3    A   男  2021-03-15
2024002 Bob  工程部  18000  5    A+  男  2019-08-20
...

要按"部门"分组算平均工资、按"绩效"排序、找出高薪员工……Excel 要 30 分钟,公式嵌套到崩溃。

(2) R 的解法

R
library(dplyr)

# 5 行代码完成 5 个分析
df |>
  group_by(部门) |>
  summarise(平均工资 = mean(工资), 人数 = n()) |>
  arrange(desc(平均工资)) |>
  filter(平均工资 > 15000)

5 行代码替代 Excel 30 分钟。这就是数据框 + dplyr 的威力。



3. data.frame:R 的"主角"

(1) 什么是数据框?

100%
graph TB
    A["数据框 data.frame"] --> B["每列 = 1 个等长向量"]
    A --> C["每行 = 1 条记录"]
    A --> D["可混合类型(每列独立)"]
    A --> E["底层是'等长向量的列表'"]
    
    style A fill:#fff3cd
    style B fill:#cce5ff
    style C fill:#d4edda
    style D fill:#f8d7da
    style E fill:#e1d4ff

数据框是"等长向量的列表"

(2) 创建数据框

R
# 方法 1:data.frame()(基础 R)
df <- data.frame(
  name = c("Alice", "Bob", "Charlie"),
  age = c(25, 30, 35),
  score = c(95, 88, 92),
  passed = c(TRUE, TRUE, TRUE)
)
print(df)
#       name age score passed
# 1    Alice  25    95   TRUE
# 2      Bob  30    88   TRUE
# 3 Charlie  35    92   TRUE

# 方法 2:tibble(tidyverse 版本,推荐)
# install.packages("tibble")
library(tibble)
df2 <- tibble(
  name = c("Alice", "Bob", "Charlie"),
  age = c(25, 30, 35),
  score = c(95, 88, 92)
)
print(df2)

(3) data.frame vs tibble

特性 data.frame tibble
打印 默认打印所有行(大数据卡死) 默认前 10 行 + 列类型
列名 支持特殊字符 强制合法
子集取子集 df[, "col"] 返回向量 始终返回 tibble
性能 较慢 更快
字符串 默认转 factor 保持字符
R
# 查看结构
str(df)
# 'data.frame':	3 obs. of  4 variables:
#  $ name  : chr  "Alice" "Bob" "Charlie"
#  $ age   : num  25 30 35
#  $ score : num  95 88 92
#  $ passed: logi  TRUE TRUE TRUE

# 摘要统计
summary(df)
#      name                age        score          passed
#  Length:3           Min.   :25   Min.   :88   Mode :logical
#  Class :character   1st Qu.:27   Median :92   TRUE:3
#  Mode  :character   Mean   :30   Mean   :91   NA's :0
#                     3rd Qu.:32   3rd Qu.:93
#                     Max.   :35   Max.   :95


4. 访问数据框

(1) 4 种访问方式

方式 语法 返回 用途
df[i, j] 第 i 行第 j 列 单值/向量/数据框 行列组合
df[i, ] 第 i 行 数据框 取行
df[, j] 第 j 列 向量(base R)/ 数据框 取列
df$name 按列名 向量 取列
df[["name"]] 按列名 向量 取列
R
df <- data.frame(
  name = c("Alice", "Bob", "Charlie"),
  age = c(25, 30, 35),
  score = c(95, 88, 92)
)

# 取单个元素
df[2, 3]              # [1] 88
df[2, "score"]        # [1] 88

# 取整行
df[1, ]               # 整行(数据框)
#     name age score
# 1 Alice  25    95

# 取整列
df[, "age"]           # 数字向量 [1] 25 30 35
df$age                # 同上
df[["age"]]           # 同上

(2) 逻辑索引(筛选)

R
# 找分数 > 90 的行
df[df$score > 90, ]
#       name age score
# 1    Alice  25    95
# 3 Charlie  35    92

# 多条件
df[df$age > 25 & df$score > 85, ]

(3) ⚠️ 重要:base R 取列 vs tibble

R
# base R:取列返回向量
df[, "age"]           # [1] 25 30 35  ← 数字向量

# tibble:取列返回 tibble
library(tibble)
tb <- as_tibble(df)
tb[, "age"]           # ← tibble(1 行 1 列)

# 想从 tibble 取出向量:用 [[ ]] 或 $
tb[["age"]]           # [1] 25 30 35  ← 数字向量
⚠️ 注意:tibble 的 [, "col"] 不自动转向量。这是"防意外"设计——避免大数据框误以为是向量后整个打印卡死。



5. 修改数据框

(1) 添加列

R
# 用 $ 添加
df$city <- c("北京", "上海", "广州")

# 用 transform()
df <- transform(df, bonus = score * 100)

# 用 within() 引用列名
df <- within(df, level <- ifelse(score >= 90, "A", "B"))

print(df)
#       name age score   city bonus level
# 1    Alice  25    95   北京  9500     A
# 2      Bob  30    88   上海  8800     B
# 3 Charlie  35    92   广州  9200     A

(2) 修改列

R
# 整列赋值
df$age <- df$age + 1

# 条件修改
df$score[df$name == "Bob"] <- 90

(3) 删除列

R
# 赋 NULL
df$bonus <- NULL
df$level <- NULL

(4) 添加/删除行

R
# 添加行:用 rbind
new_row <- data.frame(name = "Diana", age = 28, score = 85, city = "深圳")
df <- rbind(df, new_row)

# 删除行:用负索引
df <- df[-1, ]  # 删第 1 行


6. 因子 factor:分类变量

(1) 为什么需要因子?

R 用因子 (factor) 表示分类变量(如"低/中/高"),比字符向量强大:

R
# 字符向量(无序)
gender_char <- c("男", "女", "男", "女", "男")

# 因子(有序 + 有限取值)
gender_factor <- factor(gender_char, levels = c("男", "女"))
gender_factor
# [1] 男 女 男 女 男
# Levels: 男 女

(2) 因子的优势

100%
graph TB
    A["字符向量 '低' '中' '高'"] --> B["因子 factor<br/>+ levels 顺序<br/>+ 有限取值<br/>+ 节省内存"]
    A --> C["缺点: 无法排序比较"]
    B --> D["优点: 可比较大小<br/>可按顺序分组<br/>统计/建模知道类别数"]
    
    style A fill:#f8d7da
    style B fill:#d4edda
    style D fill:#cce5ff
R
# 比较大小(字符不能,因子可以)
sizes <- factor(c("小", "大", "中", "大", "小"),
                levels = c("小", "中", "大"),
                ordered = TRUE)

sizes[1] < sizes[2]   # TRUE  ← 字符做不到!

(3) 因子常用操作

R
# 查看 levels
levels(sizes)
# [1] "小" "中" "大"

# 统计频次
table(sizes)
# sizes
# 小 中 大
#  2  1  2

# 重新排序
sizes <- factor(sizes, levels = c("大", "中", "小"))  # 反转顺序
levels(sizes)
# [1] "大" "中" "小"
⚠️ 注意:tidyverse 时代推荐用 forcats 包(第 11 课学)处理因子,比 base R 更优雅。

(4) 数据框里的因子

R
# data.frame() 默认把字符列转因子(这是坑!)
df <- data.frame(
  name = c("Alice", "Bob"),  # 字符向量
  stringsAsFactors = FALSE   # 禁用自动转换(推荐)
)
str(df)
# $ name: chr "Alice" "Bob"  ← 保持字符
💡 提示新版 R(4.x)默认 stringsAsFactors = FALSE,但老代码可能不同。建议显式写 stringsAsFactors = FALSE



7. dplyr 入门:数据处理五件套

dplyr 是 tidyverse 的核心,5 个函数覆盖 80% 数据处理场景

(1) 安装和加载

R
install.packages("dplyr")
library(dplyr)

(2) 五件套函数

函数 作用 类比 SQL
filter() 筛选行 WHERE
select() 选择列 SELECT
mutate() 添加/修改列
arrange() 排序 ORDER BY
summarise() 聚合统计 GROUP BY

(3) 链式操作符 |>

R
# |> 是 R 4.1+ 的"管道符",把上一步结果传给下一步
df |> filter(age > 25) |> select(name, age)

(4) 实战:员工数据处理

R
# 准备数据
employees <- tibble(
  id = 2024001:2024010,
  name = c("Alice", "Bob", "Charlie", "Diana", "Eve",
           "Frank", "Grace", "Henry", "Ivy", "Jack"),
  department = c("数据部", "工程部", "数据部", "产品部", "工程部",
                "数据部", "工程部", "产品部", "数据部", "工程部"),
  salary = c(15000, 18000, 16000, 20000, 17000,
            15500, 19000, 21000, 16500, 17500),
  years = c(3, 5, 4, 7, 4, 2, 6, 8, 3, 5),
  performance = factor(
    c("A", "A+", "B", "A+", "A", "B", "A", "A+", "B", "A"),
    levels = c("B", "A", "A+"),
    ordered = TRUE
  )
)

# 1. filter:筛选数据部员工
data_dept <- employees |> filter(department == "数据部")

# 2. select:选择部分列
basic_info <- employees |> select(name, department, salary)

# 3. mutate:添加年薪列
employees <- employees |> mutate(annual_salary = salary * 12)

# 4. arrange:按工资降序
ranked <- employees |> arrange(desc(salary))

# 5. summarise:按部门统计
dept_stats <- employees |>
  group_by(department) |>
  summarise(
    人数 = n(),
    平均工资 = mean(salary),
    最高工资 = max(salary),
    总年薪 = sum(annual_salary)
  ) |>
  arrange(desc(平均工资))

print(dept_stats)
# # A tibble: 3 × 5
#   department 人数 平均工资 最高工资 总年薪
#   <chr>    <int>    <dbl>    <dbl>  <dbl>
# 1 产品部       2   20500    21000 492000
# 2 工程部       4   17875    19000 858000
# 3 数据部       4   15750    16500 756000

(5) 5 步组合示例

R
# 综合示例:找出高薪优秀员工
top_talent <- employees |>
  filter(performance %in% c("A+", "A")) |>     # ① 筛选绩效 A+ 或 A
  filter(salary > 16000) |>                     # ② 筛选高薪
  mutate(salary_level = ifelse(salary > 18000, "高", "中高")) |>  # ③ 添加分类
  arrange(desc(salary)) |>                      # ④ 排序
  select(name, department, salary, performance, salary_level)  # ⑤ 选择列

print(top_talent)
# # A tibble: 3 × 5
#   name   department salary performance salary_level
#   <chr>  <chr>       <dbl> <ord>       <chr>       
# 1 Henry  产品部      21000 A+          高          
# 2 Diana  产品部      20000 A+          高          
# 3 Bob    工程部      18000 A+          中高


8. 完整示例:员工数据综合分析

下面是一个完整工作流示例,把本课所有知识串起来。

▶ 示例:公司员工数据综合分析

R 📖 仅展示
# ============================================
# 公司员工数据综合分析
# 功能:用数据框 + dplyr 完整分析 10 个员工
# ============================================

library(dplyr)

# 1. Prepare data
employees <- tibble(
  id = 2024001:2024010,
  name = c("Alice", "Bob", "Charlie", "Diana", "Eve",
           "Frank", "Grace", "Henry", "Ivy", "Jack"),
  department = c("数据部", "工程部", "数据部", "产品部", "工程部",
                "数据部", "工程部", "产品部", "数据部", "工程部"),
  salary = c(15000, 18000, 16000, 20000, 17000,
            15500, 19000, 21000, 16500, 17500),
  years = c(3, 5, 4, 7, 4, 2, 6, 8, 3, 5),
  performance = factor(
    c("A", "A+", "B", "A+", "A", "B", "A", "A+", "B", "A"),
    levels = c("B", "A", "A+"),
    ordered = TRUE
  )
)

cat("=== 原始数据 ===\n")
print(employees)

# 2. 查看结构
cat("\n=== 数据结构 ===\n")
str(employees)

# 3. 摘要统计
cat("\n=== 摘要 ===\n")
summary(employees)

# 4. 按部门统计
cat("\n=== 按部门统计 ===\n")
dept_summary <- employees |>
  group_by(department) |>
  summarise(
    人数 = n(),
    平均工资 = mean(salary),
    中位工资 = median(salary),
    最高工资 = max(salary),
    最低工资 = min(salary),
    平均工龄 = round(mean(years), 1)
  ) |>
  arrange(desc(平均工资))
print(dept_summary)

# 5. 找出绩效优秀员工(A+ 或 A)
cat("\n=== 绩效优秀员工 ===\n")
top_perf <- employees |>
  filter(performance %in% c("A+", "A")) |>
  arrange(desc(salary)) |>
  select(name, department, salary, performance, years)
print(top_perf)

# 6. 添加"工资级别"列
cat("\n=== 工资级别分类 ===\n")
employees <- employees |>
  mutate(
    salary_level = factor(
      ifelse(salary >= 18000, "高",
      ifelse(salary >= 16000, "中", "低")),
      levels = c("低", "中", "高"),
      ordered = TRUE
    ),
    annual_salary = salary * 12
  )

# 按工资级别统计
level_summary <- employees |>
  group_by(salary_level) |>
  summarise(人数 = n(), 平均工资 = mean(salary))
print(level_summary)

# 7. 找出最优员工(高薪 + 高绩效 + 资深)
cat("\n=== 最优员工(高薪 + 优秀绩效 + 资深)===\n")
top_talent <- employees |>
  filter(salary >= 17000, performance == "A+", years >= 5) |>
  select(name, department, salary, years, performance, annual_salary)
print(top_talent)
逻辑代码 61 行(超过 40 行限制,仅展示)

预期输出(节选):

TEXT 📖 仅展示
=== 按部门统计 ===
# A tibble: 3 × 7
  department  人数 平均工资 中位工资 最高工资 最低工资 平均工龄
  <chr>     <int>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
1 产品部        2   20500    20500    21000    20000      7.5
2 工程部        4   17875    17750    19000    17000      4.5
3 数据部        4   15750    15750    16500    15000      3  

=== 绩效优秀员工 ===
# A tibble: 6 × 5
  name   department salary performance years
  <chr>  <chr>       <dbl> <ord>       <dbl>
1 Henry  产品部      21000 A+              8
2 Diana  产品部      20000 A+              7
3 Bob    工程部      18000 A+              5
4 Grace  工程部      19000 A                6
5 Eve    工程部      17000 A                4
6 Jack   工程部      17500 A                5

❓ 常见问题

Q factor 有什么优势?
A 3 大优势:① 可比较大小(ordered = TRUE) ② 节省内存(只存整数编码) ③ 统计/建模知道类别数(避免"未知值"问题)。

📖 小节


📝 作业

  1. 基础题:用 data.frame() 创建一个 students 数据框(6 个学生 × 4 列:name/age/score/passed)。打印数据框、str()summary(),访问第 3 行的所有列。

  2. 基础题:把上题的 name 列转成 factor(3 种 level),比较 as.character()as.numeric() 的区别。验证因子支持比较大小(>)。

  3. 基础题:安装并加载 dplyr,对上题的 studentsfilter() 筛选 score > 85 的学生,用 arrange() 按分数降序排列。

  4. 进阶题:用 tibble 创建一个 sales 数据框(10 行 × 4 列:product/region/amount/date)。① 用 mutate()discount = amount * 0.1;② 用 group_by(region) + summarise() 算各区域总销售额;③ 用 arrange(desc()) 排名。

  5. 挑战题:模拟员工数据(10 人,3 个部门,工资/工龄/绩效),完成 7 步分析:① 加载 dplyr ② 按部门统计人数/平均工资 ③ 找出工龄 ≥ 5 年的高薪员工 ④ 用 mutate 加年薪列 ⑤ 用 case_when(或 ifelse)分等级 ⑥ 按绩效排序 ⑦ 写入新数据框 top_employees 并保存为 CSV(write.csv())。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏