数据挖掘与R语言

第6讲:数据管理基础——排序、合并、分组统计与简单数据读写

2026年09月16日

本讲导览

Data Management Basics

  • 课前测 ——4道题,热热身
  • 一、复习导入 ——项目一学的知识够用了吗?
  • 二、排序 ——order()与sort()
  • 三、合并 ——rbind()/cbind()/merge()
  • 四、分组统计 ——table()与tapply()
  • 五、最简单的数据读写体验 ——read.csv()/write.csv()
  • 六、项目一总结收官 ——把六次课串成一张地图,预告项目二

提示

今天是项目一"R语言基础"的最后一课。把数据管理的基础工具补齐,也为下周开始的项目二"数据准备"(用dplyr/tidyr专业工具)做好衔接。

课前测

温故知新,为今天的内容热热身

课前小测

请在正式上课前,凭记忆快速完成下面4道题(不查资料):

选择题1:查看一个矩阵"行数与列数"的函数是(  )

A. length() B. dim() C. class() D. str()

选择题2:下面哪一个是自定义函数正确的"三段式结构"?

A. 函数名 <- function(参数) { 函数体 } B. function(参数) <- 函数名 { 函数体 }

C. 函数体 <- function(函数名) { 参数 } D. 参数 <- function(函数体) { 函数名 }

填空题1:向量x <- c(10, 20, 30),取出第2个元素的写法是x[____]

填空题2:判断一个对象是不是数据框,可以用is.____()函数。

课前小测——参考答案

提示

这4道题分别复习了矩阵(第4课)、自定义函数(第5课)和向量索引(第2课),都是今天"排序""合并""分组统计"要用到的基本功。

一、复习导入

项目一学的知识够用了吗?

1.1 复习提问

自定义函数的三段式结构是什么?请举例说明(回顾第5课)

1.2 情境导入 —— 四个引导问题

问题一:3家公司的营收数据(第4课学的矩阵),如果想按营收从高到低排序展示,怎么做?

问题二:如果想把"3家公司Q1-Q2数据"和"3家公司Q3-Q4数据"两张表合并成一张完整表,怎么做?

问题三:如果想统计"不同评级(A/B/C)的公司各有几家、平均营收各是多少",怎么做?

问题四:如果这些数据存在一个CSV格式的文件里,怎么读进R里来、处理数据后怎么保存?

二、排序——order()与sort()

order()与sort()

2.1 sort()——对向量本身排序

▶️ 查看代码
revenue <- c(120, 135, 128, 150)
sort(revenue)                    # 从小到大
[1] 120 128 135 150
▶️ 查看代码
sort(revenue, decreasing = TRUE) # 从大到小
[1] 150 135 128 120

2.2 order()——排序的"位置索引"

▶️ 查看代码
company <- c("贵州茅台", "五粮液", "泸州老窖", "洋河股份")
revenue <- c(1500, 680, 320, 450)
order(revenue)                              # 返回排序后每个位置对应原来的第几个
[1] 3 4 2 1
▶️ 查看代码
company[order(revenue)]                     # 用order()的结果对company排序——公司名跟着营收排序!
[1] "泸州老窖" "洋河股份" "五粮液"   "贵州茅台"
▶️ 查看代码
company[order(revenue, decreasing = TRUE)]  # 从高到低
[1] "贵州茅台" "五粮液"   "洋河股份" "泸州老窖"

重要

sort()只能对单个向量排序,但财务数据往往是"多列一起"的(比如按营收给公司排名,同时要保持"公司名"和"营收"的对应关系),这时候就需要order()——这是sort()order()最本质的区别。

课堂练习:给定四家公司的评级(因子)和营收(向量),用order()按营收从高到低,同时排出对应的评级

三、合并——rbind()/cbind()/merge()

rbind()/cbind()/merge()

3.1 rbind()——按行合并,"增加新的记录"

▶️ 查看代码
q1_q2 <- matrix(c(120,135, 80,95, 200,210), nrow = 3, byrow = TRUE)
rownames(q1_q2) <- c("贵州茅台", "五粮液", "泸州老窖")
colnames(q1_q2) <- c("Q1", "Q2")
new_company <- c(60, 65)   # 新增一家公司的Q1、Q2数据
rbind(q1_q2, "洋河股份" = new_company)
          Q1  Q2
贵州茅台 120 135
五粮液    80  95
泸州老窖 200 210
洋河股份  60  65

3.2 cbind()——按列合并,"增加新的字段"

▶️ 查看代码
q3_q4 <- matrix(c(128,150, 88,100, 205,220), nrow = 3, byrow = TRUE)
colnames(q3_q4) <- c("Q3", "Q4")
full_year <- cbind(q1_q2, q3_q4)
full_year
          Q1  Q2  Q3  Q4
贵州茅台 120 135 128 150
五粮液    80  95  88 100
泸州老窖 200 210 205 220

警告

cbind()要求两边行数一致、公司顺序也一致才能正确对应——这是新手最容易踩的坑:如果两张表里公司的先后顺序不一样,合并出来的数据会"张冠李戴",财务分析中这种错误可能非常隐蔽且后果严重。

3.3 merge()——按"共同字段"智能合并

▶️ 查看代码
df1 <- data.frame(company = c("贵州茅台","五粮液","泸州老窖"), revenue = c(1500,680,320))
df2 <- data.frame(company = c("贵州茅台","五粮液","洋河股份"), rating = c("A","A","B"))
merge(df1, df2)              # 默认按共同列company合并,只保留两边都有的公司
   company revenue rating
1   五粮液     680      A
2 贵州茅台    1500      A
▶️ 查看代码
merge(df1, df2, all = TRUE)  # all=TRUE保留所有公司,没有匹配的补NA
   company revenue rating
1   五粮液     680      A
2 泸州老窖     320   <NA>
3 洋河股份      NA      B
4 贵州茅台    1500      A

注记

merge()cbind()的智能升级版,不要求顺序一致,会自动按"公司名"这个共同字段配对——这正是Excel里VLOOKUP的思路在R语言里的体现。更强大的join操作(left_join/inner_join等)会在项目二用dplyr系统学习,这里先建立"按共同字段合并"的概念。

3.4 merge()的by参数——当两表"关键列"名字不一样时

▶️ 查看代码
df3 <- data.frame(name = c("贵州茅台", "五粮液", "泸州老窖"),
                   region = c("贵州", "四川", "四川"))
merge(df1, df3, by.x = "company", by.y = "name")  # 两表关键列名字不同,分别用by.x/by.y指定
   company revenue region
1   五粮液     680   四川
2 泸州老窖     320   四川
3 贵州茅台    1500   贵州
▶️ 查看代码
merge(df1, df2, by = "company")                   # 两表关键列名字相同时,也可用by显式写明,增强可读性
   company revenue rating
1   五粮液     680      A
2 贵州茅台    1500      A

注记

merge()默认会自动找两个数据框里"名字相同"的列作为合并依据;但财务数据经常出现"公司""company""name"这类字段名不统一的情况,这时必须用by.x(左表的关键列名)和by.y(右表的关键列名)显式告诉R"按哪一列对应哪一列"合并。哪怕两表关键列名字相同,显式写出by = "列名"也是更严谨、更易读的好习惯。

课堂练习:

▶️ 查看代码
df4 <- data.frame(
  公司 = c("贵州茅台","五粮液","洋河股份"), 
  region = c("贵州","四川","江苏")
  )

公司名字段叫name而不是company,请用merge()df1df4按"公司名"正确合并

四、分组统计——table()与tapply()

table()与tapply()

4.1 table()——分类计数

复习第2课因子部分:

▶️ 查看代码
rating <- factor(c("A","B","A","C","B","A"), levels = c("A","B","C"))
table(rating)   # 各评级公司数量
rating
A B C 
3 2 1 

4.2 tapply()——按分组做汇总计算

▶️ 查看代码
company <- c("贵州茅台","五粮液","泸州老窖","洋河股份","古井贡酒","舍得酒业")
rating <- factor(c("A","B","A","C","B","A"), levels = c("A","B","C"))
revenue <- c(1500, 680, 320, 450, 210, 180)
data.frame(company, rating, revenue)
   company rating revenue
1 贵州茅台      A    1500
2   五粮液      B     680
3 泸州老窖      A     320
4 洋河股份      C     450
5 古井贡酒      B     210
6 舍得酒业      A     180
▶️ 查看代码
tapply(revenue, rating, sum)    # 按评级分组,计算每组的营收总和
   A    B    C 
2000  890  450 
▶️ 查看代码
tapply(revenue, rating, mean)   # 按评级分组,计算每组的平均营收
  A   B   C 
667 445 450 

注记

tapply(要汇总的数据, 分组依据, 汇总函数)——这是base R做"分组统计"最核心的函数,虽然项目二会学更好用的group_by()+summarise(),但"分组→汇总"这套逻辑是相通的。

财务场景练习:用tapply()分别算出每个评级公司的最高营收和最低营收(提示:把sum换成maxmin

五、最简单的数据读写体验

read.csv()/write.csv()

5.1 写出一个CSV文件

▶️ 查看代码
df <- data.frame(company, revenue, rating)
write.csv(df, "company_data.csv", row.names = FALSE)

row.names=FALSE:不把R自动生成的行号也写进CSV,这是一个容易被忽略但很重要的细节

5.2 读入一个CSV文件

▶️ 查看代码
new_df <- read.csv("company_data.csv")
new_df
   company revenue rating
1 贵州茅台    1500      A
2   五粮液     680      B
3 泸州老窖     320      A
4 洋河股份     450      C
5 古井贡酒     210      B
6 舍得酒业     180      A
▶️ 查看代码
str(new_df)
'data.frame':   6 obs. of  3 variables:
 $ company: chr  "贵州茅台" "五粮液" "泸州老窖" "洋河股份" ...
 $ revenue: int  1500 680 320 450 210 180
 $ rating : chr  "A" "B" "A" "C" ...

注记

这里只是最简单的"能读能写"体验,目的是建立直观印象;真正系统的数据导入导出(不同编码、不同分隔符、Excel/SPSS等更多格式)将在下周项目二"数据准备"中用readr、readxl等专业包系统学习,今天不做深入展开。

5.3 课堂练习

把这节课创建的company/revenue/rating数据框写出为一个CSV文件,再读回来,用str()验证内容与结构是否一致

六、项目一总结收官

六次课串成一张地图

6.1 项目一全景回顾

▶️ 查看代码
flowchart LR
    L1["第1课<br/>环境搭建·基础运算"] --> L2["第2课<br/>数据类型·核心对象"]
    L2 --> L3["第3课<br/>Markdown·Quarto"]
    L3 --> L4["第4课<br/>矩阵·列表·数组"]
    L4 --> L5["第5课<br/>流程控制·函数"]
    L5 --> L6["第6课<br/>数据管理基础"]

    style L1 fill:#1a3a5c,color:#fff
    style L2 fill:#2e5984,color:#fff
    style L3 fill:#375623,color:#fff
    style L4 fill:#7a4419,color:#fff
    style L5 fill:#4a2f6b,color:#fff
    style L6 fill:#c9a44c,color:#1a3a5c

flowchart LR
    L1["第1课<br/>环境搭建·基础运算"] --> L2["第2课<br/>数据类型·核心对象"]
    L2 --> L3["第3课<br/>Markdown·Quarto"]
    L3 --> L4["第4课<br/>矩阵·列表·数组"]
    L4 --> L5["第5课<br/>流程控制·函数"]
    L5 --> L6["第6课<br/>数据管理基础"]

    style L1 fill:#1a3a5c,color:#fff
    style L2 fill:#2e5984,color:#fff
    style L3 fill:#375623,color:#fff
    style L4 fill:#7a4419,color:#fff
    style L5 fill:#4a2f6b,color:#fff
    style L6 fill:#c9a44c,color:#1a3a5c

  • 第1课:环境搭建、基础运算、数列生成——打地基
  • 第2课:数据类型、向量、因子、数据框——认识基本数据
  • 第3课:Markdown/Quarto——学会规范整理成果
  • 第4课:矩阵、列表、数组——补全数据对象工具箱
  • 第5课:条件分支、循环、函数——学会控制代码逻辑、封装复用
  • 第6课(今天):排序、合并、分组、简单读写——数据管理的基础操作

提示

六次课合起来,就是独立完成"数据准备"之前所需要的全部R语言基本功

6.2 学习成果自查

对照一下,今天这五件事你都做到了吗?

6.3 Exit Ticket —— 写给自己的三个问题

请花1分钟,在纸条或在线问卷上简要回答:

  1. order()sort()最大的区别是什么?
  2. cbind()merge()合并数据时,各自对"顺序"有什么要求?
  3. 回顾项目一六次课,你觉得哪个知识点最重要、或者最有挑战?

6.4 预告项目二

下周开始项目二"数据准备",将系统学习dplyr、tidyr等专业工具包,把今天用base R"手动挡"做的排序、合并、分组,升级成更简洁高效的"自动挡"写法;同时系统学习CSV、Excel等多种格式的数据导入导出

课后作业

数据管理综合练习

  1. 给定5家公司的名称(自定)与营收向量(自定),用order()按营收从高到低排序,输出排序后的公司名称与对应营收
  2. 创建两个数据框:df1包含公司名称与营收,df2包含公司名称与所属地区(自定3-4个地区),完成:
    • merge()将两个数据框按公司名称合并
    • 尝试给df2中的公司顺序打乱,验证merge()是否依然能正确匹配(对比cbind()直接拼接会发生什么)
  3. tapply()按地区分组,计算每个地区的营收总和与平均营收
  4. 将(2)中合并后的数据框写出为一个CSV文件,再读回来,用str()验证结构一致

下讲预告

项目二:数据准备

  • 系统学习dplyr、tidyr等tidyverse专业工具包
  • 把base R"手动挡"的排序、合并、分组,升级成更简洁高效的"自动挡"写法
  • 系统学习CSV、Excel等多种格式的数据导入导出

提示

课前准备:回顾一下这六课学过的向量、数据框、因子操作,项目二会大量用到这些基础。

谢谢!

第6讲:数据管理基础——排序、合并、分组统计与简单数据读写

"In God we trust; all others must bring data." — W. Edwards Deming,质量管理与统计学家

朱 奇 | 锦城大学 · 财会学院