第十章:数据框 DataFrame(数据分析主力)

数据框(Data Frame)是 R 语言中 最重要的数据结构,也是数据分析的绝对主力。Excel 表格中的每一行每一列,在 R 里就是数据框。90% 的数据分析工作都围绕数据框展开。

一、什么是数据框?

数据框 = 增强版的矩阵

  • 像矩阵一样有行和列

  • 但不同列可以是不同类型(数值列、字符串列、逻辑列......)

  • 每一列必须等长

  • 本质上就是列表的列表(每列是一个向量)

    复制代码
    姓名  年龄  数学  语文  通过
    张三   20   92    85   TRUE
    李四   21   88    72   TRUE
    王五   19   76    95   TRUE

二、创建数据框

1. 用 data.frame() 创建

r 复制代码
students <- data.frame(
  name  = c("张三", "李四", "王五", "赵六", "孙七"),
  age   = c(20, 21, 19, 20, 22),
  math  = c(92, 88, 76, 65, 95),
  chinese = c(85, 72, 95, 58, 88),
  pass  = c(TRUE, TRUE, TRUE, FALSE, TRUE)
)

print(students)

运行结果:

txt 复制代码
  name age math chinese  pass
1 张三  20   92      85  TRUE
2 李四  21   88      72  TRUE
3 王五  19   76      95  TRUE
4 赵六  20   65      58 FALSE
5 孙七  22   95      88  TRUE

2. 从向量组合创建

r 复制代码
names <- c("张三", "李四", "王五")
scores <- c(92, 85, 78)

df <- data.frame(names, scores)
print(df)

运行结果:

txt 复制代码
  names scores
1  张三     92
2  李四     85
3  王五     78

3. 字符串列的处理(重要!)

R 默认会把字符串列转成因子(Factor),可以通过参数禁止:

R 4.0 以上版本 默认已经不会自动转因子了

所以新版 R 可以不写,但:
为了兼容、为了不出错、为了教程规范 → 建议永远写上!

r 复制代码
df <- data.frame(
  name = c("张三", "李四", "王五"),
  score = c(92, 85, 78),
  stringsAsFactors = FALSE    # 不自动转因子
)

三、查看数据框

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五", "赵六", "孙七"),
  age = c(20, 21, 19, 20, 22),
  math = c(92, 88, 76, 65, 95),
  chinese = c(85, 72, 95, 58, 88)
)

nrow(students)          # 行数 → 5
ncol(students)          # 列数 → 4
dim(students)           # 维度 → 5 4
names(students)         # 列名
head(students, 3)       # 前3行
tail(students, 2)       # 后2行
str(students)           # 结构
summary(students)       # 统计摘要

summary() 是 R 中非常实用的函数:

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五", "赵六", "孙七"),
  age = c(20, 21, 19, 20, 22),
  math = c(92, 88, 76, 65, 95),
  chinese = c(85, 72, 95, 58, 88),
  stringsAsFactors = FALSE
)

summary(students)

运行结果:

txt 复制代码
    name                age            math         chinese
 Length:5           Min.   :19.0   Min.   :65.0   Min.   :58.0
 Class :character   1st Qu.:20.0   1st Qu.:76.0   1st Qu.:72.0
 Mode  :character   Median :20.0   Median :88.0   Median :85.0
                    Mean   :20.4   Mean   :83.2   Mean   :79.6
                    3rd Qu.:21.0   3rd Qu.:92.0   3rd Qu.:88.0
                    Max.   :22.0   Max.   :95.0   Max.   :95.0

一行代码就能看到每列的最小值、最大值、平均值、中位数!

四、访问数据框元素

1. 用 $ 按列名访问(最常用)

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五"),
  math = c(92, 88, 76),
  stringsAsFactors = FALSE
)

print(students$name)     # 取出 name 列(返回向量)
print(students$math)     # 取出 math 列

运行结果:

txt 复制代码
[1] "张三" "李四" "王五"
[1] 92 88 76

2. 用 [行, 列] 访问

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五", "赵六"),
  math = c(92, 88, 76, 65),
  chinese = c(85, 72, 95, 58),
  stringsAsFactors = FALSE
)

students[1, 2]            # 第1行第2列 → 92
students[1, ]             # 第1行所有列
students[, 2]             # 所有行第2列
students[1:3, ]           # 第1-3行
students[, c("name", "math")]  # 只取 name 和 math 列

3. 用列名直接取子集

r 复制代码
students[, c("name", "math")]

运行结果:

txt 复制代码
  name math
1 张三   92
2 李四   88
3 王五   76
4 赵六   65

五、筛选数据框(条件查询)

这是数据分析中最常用的操作!

1. 单条件筛选

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五", "赵六", "孙七"),
  math = c(92, 88, 76, 65, 95),
  chinese = c(85, 72, 95, 58, 88),
  stringsAsFactors = FALSE
)

# 数学成绩大于80的学生
result <- students[students$math > 80, ]
print(result)

运行结果:

txt 复制代码
  name math chinese
1 张三   92      85
2 李四   88      72
5 孙七   95      88

注意逗号 students[条件, ]------条件筛选行,逗号后面留空表示所有列。

2. 多条件筛选

r 复制代码
# 数学>80 且 语文>80
result <- students[students$math > 80 & students$chinese > 80, ]
print(result)

运行结果:

txt 复制代码
  name math chinese
1 张三   92      85
5 孙七   95      88

3. 用 subset() 筛选(更易读)

r 复制代码
# 筛选数学及格的学生,只显示 name 和 math 列
result <- subset(students, math >= 60, select = c(name, math))
print(result)

运行结果:

txt 复制代码
  name math
1 张三   92
2 李四   88
3 王五   76
4 赵六   65
5 孙七   95

六、修改数据框

1. 修改值

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五"),
  math = c(92, 88, 76),
  stringsAsFactors = FALSE
)

# 修改某个单元格
students[2, "math"] <- 90
print(students)

# 修改整列
students$math <- students$math + 5
print(students)

运行结果:

txt 复制代码
  name math
1 张三   92
2 李四   90
3 王五   76
  name math
1 张三   97
2 李四   95
3 王五   81

2. 添加新列

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五"),
  math = c(92, 88, 76),
  chinese = c(85, 72, 95),
  stringsAsFactors = FALSE
)

# 方法1:直接赋值
students$english <- c(78, 90, 82)

# 方法2:用 cbind 添加
students$total <- students$math + students$chinese + students$english

# 方法3:计算派生列
students$average <- round((students$math + students$chinese + students$english) / 3, 1)

print(students)

运行结果:

txt 复制代码
  name math chinese english total average
1 张三   92      85      78   255    85.0
2 李四   88      72      90   250    83.3
3 王五   76      95      82   253    84.3

3. 添加新行

r 复制代码
students <- data.frame(
  name = c("张三", "李四"),
  math = c(92, 88),
  stringsAsFactors = FALSE
)

# 用 rbind 添加行
new_row <- data.frame(name = "王五", math = 76, stringsAsFactors = FALSE)
students <- rbind(students, new_row)
print(students)

运行结果:

txt 复制代码
  name math
1 张三   92
2 李四   88
3 王五   76

4. 删除列和行

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五"),
  math = c(92, 88, 76),
  chinese = c(85, 72, 95),
  english = c(78, 90, 82),
  stringsAsFactors = FALSE
)

# 删除列(赋值为 NULL)
students$english <- NULL
print(students)

# 删除行(用负索引)
students <- students[-2, ]    # 删除第2行
print(students)

运行结果:

txt 复制代码
  name math chinese
1 张三   92      85
2 李四   88      72
3 王五   76      95
  name math chinese
1 张三   92      85
3 王五   76      95

七、排序

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五", "赵六", "孙七"),
  math = c(92, 88, 76, 65, 95),
  stringsAsFactors = FALSE
)

# 按 math 降序排列
sorted <- students[order(students$math, decreasing = TRUE), ]
print(sorted)

运行结果:

txt 复制代码
  name math
5 孙七   95
1 张三   92
2 李四   88
3 王五   76
4 赵六   65

多列排序:

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五"),
  math = c(92, 88, 92),
  chinese = c(85, 72, 95),
  stringsAsFactors = FALSE
)

# 先按数学降序,数学相同按语文降序
sorted <- students[order(students$math, -students$chinese, decreasing = TRUE), ]
print(sorted)

运行结果:

txt 复制代码
  name math chinese
3 王五   92      95
1 张三   92      85
2 李四   88      72

八、分组统计

1. 用 aggregate() 分组

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五", "赵六", "孙七", "周八"),
  class = c("A", "A", "B", "B", "A", "B"),
  math = c(92, 88, 76, 65, 95, 80),
  stringsAsFactors = FALSE
)

# 按班级计算数学平均分
result <- aggregate(math ~ class, data = students, FUN = mean)
print(result)

运行结果:

txt 复制代码
  class     math
1     A 91.66667
2     B 73.66667

2. 用 tapply 分组

r 复制代码
# 按班级计算数学最高分
result <- tapply(students$math, students$class, max)
print(result)

运行结果:

txt 复制代码
 A  B
95 80

九、处理缺失值 NA

真实数据经常有缺失值,R 用 NA 表示:

r 复制代码
students <- data.frame(
  name = c("张三", "李四", "王五", "赵六"),
  math = c(92, NA, 76, 65),
  chinese = c(85, 72, NA, 58),
  stringsAsFactors = FALSE
)

print(students)

运行结果:

txt 复制代码
  name math chinese
1 张三   92      85
2 李四   NA      72
3 王五   76      NA
4 赵六   65      58

1. 检测缺失值

r 复制代码
is.na(students$math)           # 每个位置是否缺失
any(is.na(students$math))      # 整列有没有缺失
sum(is.na(students))           # 总共有多少个缺失值

2. 处理缺失值

r 复制代码
# 删除有缺失值的行
na.omit(students)

# 计算时忽略缺失值
mean(students$math, na.rm = TRUE)    # na.rm = TRUE 很重要!

# 用特定值填充缺失值
students$math[is.na(students$math)] <- 0

注意 :含 NA 的向量做 mean()sum() 等运算,结果也是 NA。必须加 na.rm = TRUE

十、综合实战练习(可直接复制运行)

r 复制代码
# ==============================
# R 综合练习:数据框实战
# 场景:班级成绩管理与分析
# ==============================

# 1. 创建学生数据框
students <- data.frame(
  id     = 1:8,
  name   = c("张三", "李四", "王五", "赵六", "孙七", "周八", "吴九", "郑十"),
  gender = c("男", "女", "男", "男", "女", "女", "男", "女"),
  class  = c("A", "A", "B", "B", "A", "B", "A", "B"),
  math   = c(92, 88, 76, 65, 95, 80, 70, 85),
  chinese = c(85, 72, 95, 58, 88, 90, 62, 78),
  english = c(78, 90, 82, 70, 92, 68, 55, 88),
  stringsAsFactors = FALSE
)

print("=== 原始数据 ===")
print(students)

# 2. 计算总分和平均分,添加为新列
students$total   <- students$math + students$chinese + students$english
students$average <- round(students$total / 3, 1)

print("=== 添加总分和平均分 ===")
print(students[, c("name", "math", "chinese", "english", "total", "average")])

# 3. 添加等级列
students$grade <- ifelse(students$average >= 90, "优秀",
                   ifelse(students$average >= 80, "良好",
                   ifelse(students$average >= 60, "及格", "不及格")))

print("=== 成绩等级 ===")
print(students[, c("name", "average", "grade")])

# 4. 按总分排名
ranked <- students[order(students$total, decreasing = TRUE), ]
ranked$rank <- 1:nrow(ranked)
print("=== 总分排名 ===")
print(ranked[, c("rank", "name", "total", "average", "grade")])

# 5. 按班级分组统计
print("=== 各班数学平均分 ===")
print(aggregate(math ~ class, data = students, FUN = mean))

print("=== 各班总分平均 ===")
print(aggregate(total ~ class, data = students, FUN = mean))

# 6. 按性别统计
print("=== 各性别平均分 ===")
print(aggregate(cbind(math, chinese, english) ~ gender, data = students, FUN = mean))

# 7. 筛选优秀学生
print("=== 优秀学生(平均分≥85)===")
top <- subset(students, average >= 85, select = c(name, class, average, grade))
print(top)

# 8. 找出有不及格科目的学生
print("=== 有不及格科目的学生 ===")
for (i in 1:nrow(students)) {
  if (students$math[i] < 60 | students$chinese[i] < 60 | students$english[i] < 60) {
    failed <- c()
    if (students$math[i] < 60) failed <- c(failed, paste("数学", students$math[i]))
    if (students$chinese[i] < 60) failed <- c(failed, paste("语文", students$chinese[i]))
    if (students$english[i] < 60) failed <- c(failed, paste("英语", students$english[i]))
    print(paste(students$name[i], "不及格科目:", paste(failed, collapse = ",")))
  }
}

运行结果:

txt 复制代码
[1] "=== 原始数据 ==="
  id name gender class math chinese english
1  1 张三     男     A   92      85      78
2  2 李四     女     A   88      72      90
3  3 王五     男     B   76      95      82
4  4 赵六     男     B   65      58      70
5  5 孙七     女     A   95      88      92
6  6 周八     女     B   80      90      68
7  7 吴九     男     A   70      62      55
8  8 郑十     女     B   85      78      88
[1] "=== 添加总分和平均分 ==="
  name math chinese english total average
1 张三   92      85      78   255    85.0
2 李四   88      72      90   250    83.3
3 王五   76      95      82   253    84.3
4 赵六   65      58      70   193    64.3
5 孙七   95      88      92   275    91.7
6 周八   80      90      68   238    79.3
7 吴九   70      62      55   187    62.3
8 郑十   85      78      88   251    83.7
[1] "=== 成绩等级 ==="
  name average  grade
1 张三    85.0   良好
2 李四    83.3   良好
3 王五    84.3   良好
4 赵六    64.3   及格
5 孙七    91.7   优秀
6 周八    79.3   及格
7 吴九    62.3   及格
8 郑十    83.7   良好
[1] "=== 总分排名 ==="
  rank name total average grade
5    1 孙七   275    91.7  优秀
1    2 张三   255    85.0  良好
3    3 王五   253    84.3  良好
8    4 郑十   251    83.7  良好
2    5 李四   250    83.3  良好
6    6 周八   238    79.3  及格
4    7 赵六   193    64.3  及格
7    8 吴九   187    62.3  及格
[1] "=== 各班数学平均分 ==="
  class math
1     A 86.25
2     B 76.50
[1] "=== 各班总分平均 ==="
  class  total
1     A 241.75
2     B 233.75
[1] "=== 各性别平均分 ==="
  gender     math  chinese  english
1     男 75.75000 75.00000 71.25000
2     女 87.00000 82.00000 84.50000
[1] "=== 优秀学生(平均分≥85)==="
  name class average grade
1 张三     A    85.0  良好
5 孙七     A    91.7  优秀
[1] "=== 有不及格科目的学生 ==="
[1] "吴九 不及格科目: 英语 55"
相关推荐
Honker_yhw2 小时前
大数据管理与应用系列丛书《数据挖掘》(吕欣等著)读书笔记-非线性相关分析
人工智能·数据挖掘
雁迟2 小时前
第五章:条件判断与分支语句
开发语言·r语言
Ricky05533 小时前
AgriDet:基于农业检测框架的植物叶片病害严重程度分类(印度2023年研究)
人工智能·分类·数据挖掘
weelinking7 小时前
【2026】08_Claude与版本控制:Git协作技巧
数据库·人工智能·git·python·数据挖掘·交互·cloudera
没有梦想的咸鱼185-1037-166313 小时前
AI-Python机器学习、深度学习核心技术与前沿应用及OpenClaw、Hermes自动化编程
人工智能·python·深度学习·机器学习·chatgpt·数据挖掘·数据分析
Ada大侦探13 小时前
新手小白学习数据分析03----Excel 报表之大厂周报(2026最新版实操,包教包会!)
学习·数据分析·excel
小王毕业啦15 小时前
2009-2025年 华证ESG年度季度评级评分数据 xlsx
大数据·人工智能·数据挖掘·数据分析·社科数据·实证分析·经管数据
程序员猫哥_18 小时前
AI建站工具选型指南:主流模式对比与避坑清单
数据挖掘
Asa1213819 小时前
Nature Microbiology|Cayman:大规模解析肠道微生物组碳水化合物活性酶谱的新工具
数据分析