Data Management

R语言入门课程

尹俊贺

第1节:为什么需要数据管理

  • 课程主题:从原始数据到可分析数据
  • 贯穿案例:student 数据
  • 本节重点:
    • 为什么需要数据管理
    • 数据分析流程
    • 数据管理在分析中的位置

为什么需要数据管理

原始数据常见问题

  • 缺失值
  • 重复值
  • 类型不一致
  • 命名不规范
  • 异常值
  • 表与表之间无法直接匹配

如果不先做数据管理

  • 统计结果可能失真
  • 图表可能误导
  • 模型结果不稳定
  • 分析过程难以复现
  • 小组协作容易出错

数据管理不是附属步骤,而是数据分析的基础步骤。
只有先把数据整理成可读、可算、可解释的形式,后续分析才有意义。

贯穿案例:student 数据

student <- data.frame(
  id = c(1001, 1002, 1003, 1004, 1005),
  name = c("Alice", "Bob", "Cathy", "David", "Emma"),
  gender = c("F", "M", "F", "M", "F"),
  age = c(19, 20, 19, 21, 20),
  score = c(88, 76, 91, NA, 84),
  attendance = c(0.95, 0.82, 0.98, 0.60, 0.88),
  class = c("A", "A", "B", "B", "A")
)

student
    id  name gender age score attendance class
1 1001 Alice      F  19    88       0.95     A
2 1002   Bob      M  20    76       0.82     A
3 1003 Cathy      F  19    91       0.98     B
4 1004 David      M  21    NA       0.60     B
5 1005  Emma      F  20    84       0.88     A

这份 student 数据将作为整门课的贯穿案例。
后续各节会围绕它学习缺失值、重复值、筛选、变换、汇总与合并。

数据分析流程

flowchart LR
    A[研究问题] --> B[获取数据]
    B --> C[数据管理]
    C --> D[探索性分析]
    D --> E[统计建模]
    E --> F[可视化与汇报]

flowchart LR
    A[研究问题] --> B[获取数据]
    B --> C[数据管理]
    C --> D[探索性分析]
    D --> E[统计建模]
    E --> F[可视化与汇报]

数据管理处在“获取数据”和“正式分析”之间。
如果这一环节处理不好,后面的描述统计、建模和可视化都会受到影响。

本节小结

  • 数据管理是数据分析流程中的关键环节
  • 原始数据通常不能直接进入建模
  • 本课程将围绕 student 数据逐步学习常见整理方法
names(student)
[1] "id"         "name"       "gender"     "age"        "score"     
[6] "attendance" "class"     

下一节将进入 student 数据的结构查看,学习如何用 str()summary() 快速理解一个数据框。

示例数据:student

student <- data.frame(
  id = c(1001, 1002, 1003, 1004, 1005),
  name = c("Alice", "Bob", "Cathy", "David", "Emma"),
  gender = c("F", "M", "F", "M", "F"),
  age = c(19, 20, 19, 21, 20),
  score = c(88, 76, 91, NA, 84),
  attendance = c(0.95, 0.82, 0.98, 0.60, 0.88),
  class = c("A", "A", "B", "B", "A")
)

在进行任何数据处理之前,第一步始终是:
先理解数据本身的结构与内容。

使用 str() 查看数据结构

str(student)
'data.frame':   5 obs. of  7 variables:
 $ id        : num  1001 1002 1003 1004 1005
 $ name      : chr  "Alice" "Bob" "Cathy" "David" ...
 $ gender    : chr  "F" "M" "F" "M" ...
 $ age       : num  19 20 19 21 20
 $ score     : num  88 76 91 NA 84
 $ attendance: num  0.95 0.82 0.98 0.6 0.88
 $ class     : chr  "A" "A" "B" "B" ...

str() 提供的信息

  • 数据类型(data.frame)
  • 行数与列数
  • 每一列的数据类型
  • 每列的前几个观测值

常见变量类型

  • int:整数
  • num:连续数值
  • chr:字符
  • factor:分类变量

str() 的作用是快速回答一个问题:
这个数据“长什么样”,以及“每一列是什么类型”。

使用 summary() 查看统计信息

summary(student)
       id           name              gender               age      
 Min.   :1001   Length:5           Length:5           Min.   :19.0  
 1st Qu.:1002   Class :character   Class :character   1st Qu.:19.0  
 Median :1003   Mode  :character   Mode  :character   Median :20.0  
 Mean   :1003                                         Mean   :19.8  
 3rd Qu.:1004                                         3rd Qu.:20.0  
 Max.   :1005                                         Max.   :21.0  
                                                                    
     score         attendance       class          
 Min.   :76.00   Min.   :0.600   Length:5          
 1st Qu.:82.00   1st Qu.:0.820   Class :character  
 Median :86.00   Median :0.880   Mode  :character  
 Mean   :84.75   Mean   :0.846                     
 3rd Qu.:88.75   3rd Qu.:0.950                     
 Max.   :91.00   Max.   :0.980                     
 NA's   :1                                         

数值变量输出

  • 最小值
  • 四分位数
  • 中位数
  • 最大值

非数值变量输出

  • 各类别的出现次数
  • 或长度信息

summary() 可以帮助你快速发现:

  • 是否存在缺失值(NA)
  • 是否有异常值
  • 数据是否分布合理

聚焦单个变量

summary(student$score)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  76.00   82.00   86.00   84.75   88.75   91.00       1 

观察重点:

  • 是否存在 NA
  • 分数范围是否合理
  • 中位数位置

例如这里可以发现 score 中存在缺失值,
这意味着后续计算平均值或建模时必须先处理 NA。

类型判断的重要性

str(student$gender)
 chr [1:5] "F" "M" "F" "M" "F"

可能的问题:

  • 性别被当作字符(chr)
  • 而不是分类变量(factor)

变量类型会直接影响:

  • 统计方法选择
  • 可视化方式
  • 模型设定

因此,理解类型是数据管理的基础步骤之一。

识别缺失值(NA)

student$score
[1] 88 76 91 NA 84

什么是 NA

  • NA 表示“缺失值”
  • 不是 0
  • 不是空字符串
  • 是一种特殊标记

常见来源

  • 数据录入遗漏
  • 问卷未回答
  • 数据合并失败
  • 采集错误

在 R 中,NA 是“有意义的缺失”,
如果不处理,会影响几乎所有统计计算。

检测缺失值

is.na(student$score)
[1] FALSE FALSE FALSE  TRUE FALSE

is.na() 作用

  • 判断每个位置是否为 NA
  • 返回 TRUE / FALSE

统计缺失数量

sum(is.na(student$score))
[1] 1

is.na() 是最基础也是最重要的函数之一,
通常与 sum()which() 等函数一起使用。

查看缺失值所在位置

which(is.na(student$score))
[1] 4

which() 的作用

  • 返回 TRUE 的位置索引
  • 帮助定位问题数据

应用场景

  • 找出缺失行
  • 删除或替换特定记录

定位缺失值比“只知道有缺失”更重要,
因为你需要决定:删除、填补还是保留。

删除含 NA 的观测

na.omit(student)
    id  name gender age score attendance class
1 1001 Alice      F  19    88       0.95     A
2 1002   Bob      M  20    76       0.82     A
3 1003 Cathy      F  19    91       0.98     B
5 1005  Emma      F  20    84       0.88     A

特点

  • 删除所有包含 NA 的行
  • 返回一个“干净数据集”

风险

  • 可能丢失大量数据
  • 样本不再具有代表性

删除是最简单的方法,但不一定是最好的方法。
特别是在样本较小或缺失较多时,需要谨慎使用。

简单填补缺失值

student$score[is.na(student$score)] <- mean(student$score, na.rm = TRUE)

student$score
[1] 88.00 76.00 91.00 84.75 84.00

方法说明

  • 用均值替换 NA
  • na.rm = TRUE 表示忽略 NA

优点与问题

  • 优点:简单、快速
  • 问题:可能低估波动性

缺失值处理没有唯一正确答案,
不同研究问题会采用不同策略(删除、均值填补、模型填补等)。

识别重复值

student_dup <- rbind(student, student[2, ])
student_dup
     id  name gender age score attendance class
1  1001 Alice      F  19 88.00       0.95     A
2  1002   Bob      M  20 76.00       0.82     A
3  1003 Cathy      F  19 91.00       0.98     B
4  1004 David      M  21 84.75       0.60     B
5  1005  Emma      F  20 84.00       0.88     A
21 1002   Bob      M  20 76.00       0.82     A

重复值(duplicate records)是数据管理中常见问题之一。
它通常来源于数据合并、导入或记录错误。

使用 duplicated() 检测重复

duplicated(student_dup)
[1] FALSE FALSE FALSE FALSE FALSE  TRUE

duplicated() 作用

  • 判断每一行是否为重复
  • 第一次出现为 FALSE
  • 后续重复为 TRUE

统计重复数量

sum(duplicated(student_dup))
[1] 1

duplicated() 是识别重复记录的标准方法,
通常用于数据清洗的第一步。

查看重复的具体内容

student_dup[duplicated(student_dup), ]
     id name gender age score attendance class
21 1002  Bob      M  20    76       0.82     A

目的

  • 找出哪些行是重复的
  • 检查重复是否合理

思考

  • 是完全重复?
  • 还是关键变量重复?

在实际研究中,有时“重复”是有意义的(例如多次观测),
因此需要结合研究问题判断是否删除。

删除重复值

student_clean <- student_dup[!duplicated(student_dup), ]
student_clean
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A

逻辑说明

  • duplicated() 标记重复
  • ! 表示取反
  • 保留非重复行

效果

  • 每条记录唯一
  • 数据更加规范

去重是数据整理的基础步骤之一,
尤其在合并多个数据源后非常重要。

基于特定变量去重

student_dup[!duplicated(student_dup$id), ]
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A

特点

  • 只根据 id 判断重复
  • 保留每个 id 的第一条记录

应用场景

  • 一个学生多条记录
  • 只保留唯一身份

在实际数据中,通常不会用“整行去重”,
而是基于关键变量(如 ID)来判断重复。

数据排序(Sorting)

student
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A

排序可以帮助我们快速观察数据分布,例如:

  • 谁的分数最高?
  • 是否存在极端值?
  • 数据是否异常集中或分散?

按单个变量排序

student[order(student$score), ]
    id  name gender age score attendance class
2 1002   Bob      M  20 76.00       0.82     A
5 1005  Emma      F  20 84.00       0.88     A
4 1004 David      M  21 84.75       0.60     B
1 1001 Alice      F  19 88.00       0.95     A
3 1003 Cathy      F  19 91.00       0.98     B

默认行为

  • 从小到大排序
  • NA 通常排在最后

降序排序

student[order(-student$score), ]
    id  name gender age score attendance class
3 1003 Cathy      F  19 91.00       0.98     B
1 1001 Alice      F  19 88.00       0.95     A
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A
2 1002   Bob      M  20 76.00       0.82     A

排序是发现异常值的第一步,
尤其在数值变量(如 score)中非常直观。

按多个变量排序

student[order(student$class, -student$score), ]
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
5 1005  Emma      F  20 84.00       0.88     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B

排序逻辑

  • 先按 class 排序
  • 再在组内按 score 降序

应用场景

  • 班级内排名
  • 分组比较

多变量排序常用于“分组后排序”的情境,
是后续分组分析的重要基础。

识别异常值(Outliers)

summary(student$score)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  76.00   84.00   84.75   84.75   88.00   91.00 

观察重点

  • 最大值是否异常大
  • 最小值是否异常小
  • 是否存在 NA

异常值来源

  • 输入错误
  • 测量误差
  • 极端个体

异常值不一定是错误数据,
但必须被识别和解释,否则会影响统计结果。

简单识别极端值

student[student$score > 90, ]
    id  name gender age score attendance class
3 1003 Cathy      F  19    91       0.98     B

筛选逻辑

  • 找出高分学生
  • 观察是否合理

也可以检测低值

student[student$score < 70, ]
[1] id         name       gender     age        score      attendance class     
<0 rows> (or 0-length row.names)

异常值识别通常结合排序 + 条件筛选,
为后续清洗或建模做准备。

dplyr:统一的数据操作框架

  • 数据以“表(data.frame)”为中心
  • 每一步操作都是一个函数
  • 操作可以串联(pipeline)

👉 数据处理 = 一系列连续变换

核心函数体系

  • 行:filter()
  • 列:select()
  • 变量:mutate()
  • 分组:group_by()
  • 汇总:summarise()

dplyr 提供了一种结构化的数据操作语言

把复杂的数据处理过程,拆解为一系列清晰、可读的步骤。

dplyr 的典型流程(pipeline)

library(dplyr)

student %>%
  filter(!is.na(score)) %>%
  mutate(score2 = score * 1.1) %>%
  group_by(class) %>%
  summarise(avg_score = mean(score2))
# A tibble: 2 × 2
  class avg_score
  <chr>     <dbl>
1 A          90.9
2 B          96.7

步骤逻辑

  1. 去除缺失值
  2. 构造新变量
  3. 分组
  4. 汇总

特点

  • 每一步都清晰可读
  • 顺序就是分析逻辑
  • 易于修改与扩展

这段代码基本代表了数据分析中最常见的结构:

清洗 → 变换 → 分组 → 汇总

使用 filter 进行行筛选

library(dplyr)

student
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A

在数据管理中,最常见的操作之一是:
根据条件筛选出需要的观测(行)

基本筛选:单个条件

filter(student, score > 85)
    id  name gender age score attendance class
1 1001 Alice      F  19    88       0.95     A
2 1003 Cathy      F  19    91       0.98     B

含义

  • 保留分数大于 85 的学生

等价写法(基础R)

student[student$score > 85, ]
    id  name gender age score attendance class
1 1001 Alice      F  19    88       0.95     A
3 1003 Cathy      F  19    91       0.98     B

filter() 语法更清晰、可读性更强,
是数据分析中更推荐的写法。

多条件筛选

filter(student, score > 80, class == "A")
    id  name gender age score attendance class
1 1001 Alice      F  19    88       0.95     A
2 1005  Emma      F  20    84       0.88     A

逻辑

  • 条件之间默认是 AND(且)

使用 OR

filter(student, score > 90 | attendance > 0.9)
    id  name gender age score attendance class
1 1001 Alice      F  19    88       0.95     A
2 1003 Cathy      F  19    91       0.98     B

多条件筛选可以帮助我们构建更精确的数据子集,
例如“高分且某班级”的学生。

处理 NA 的筛选

filter(student, is.na(score))
[1] id         name       gender     age        score      attendance class     
<0 rows> (or 0-length row.names)

作用

  • 找出 score 缺失的记录

非缺失

filter(student, !is.na(score))
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A

NA 不能用 == 判断,
必须使用 is.na()

使用 select 选择变量

select(student, id, score, class)
    id score class
1 1001 88.00     A
2 1002 76.00     A
3 1003 91.00     B
4 1004 84.75     B
5 1005 84.00     A

作用

  • 选择部分列
  • 减少数据复杂度

去掉某些列

select(student, -name, -gender)
    id age score attendance class
1 1001  19 88.00       0.95     A
2 1002  20 76.00       0.82     A
3 1003  19 91.00       0.98     B
4 1004  21 84.75       0.60     B
5 1005  20 84.00       0.88     A

select() 用于控制“变量维度”,
filter()(控制行)相对应。

使用 mutate 创建新变量

library(dplyr)

student
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A

在数据分析中,我们常常需要:

  • 构造新变量
  • 对原变量进行转换
  • 生成可用于建模或可视化的指标

这些操作主要通过 mutate() 完成。

基本用法:生成新变量

student2 <- mutate(student, score2 = score * 1.1)

student2
    id  name gender age score attendance class  score2
1 1001 Alice      F  19 88.00       0.95     A  96.800
2 1002   Bob      M  20 76.00       0.82     A  83.600
3 1003 Cathy      F  19 91.00       0.98     B 100.100
4 1004 David      M  21 84.75       0.60     B  93.225
5 1005  Emma      F  20 84.00       0.88     A  92.400

含义

  • 新建变量 score2
  • 对原始分数进行放大

特点

  • 不会覆盖原数据(除非同名)
  • 可以连续创建多个变量

mutate() 是“变量层面操作”的核心函数,
常用于特征构造(feature engineering)。

修改已有变量

student3 <- mutate(student, score = score + 5)

student3
    id  name gender age score attendance class
1 1001 Alice      F  19 93.00       0.95     A
2 1002   Bob      M  20 81.00       0.82     A
3 1003 Cathy      F  19 96.00       0.98     B
4 1004 David      M  21 89.75       0.60     B
5 1005  Emma      F  20 89.00       0.88     A

行为

  • 覆盖原变量 score

适用场景

  • 标准化处理
  • 分数调整

当新变量名称与旧变量相同,
mutate() 会直接替换原变量。

case_when:条件变量构造

student4 <- mutate(
  student,
  level = case_when(
    score >= 90 ~ "A",
    score >= 80 ~ "B",
    score >= 70 ~ "C",
    TRUE ~ "D"
  )
)

student4
    id  name gender age score attendance class level
1 1001 Alice      F  19 88.00       0.95     A     B
2 1002   Bob      M  20 76.00       0.82     A     C
3 1003 Cathy      F  19 91.00       0.98     B     A
4 1004 David      M  21 84.75       0.60     B     B
5 1005  Emma      F  20 84.00       0.88     A     B

逻辑

  • 根据 score 分段
  • 生成等级变量

结构特点

  • 每一行是一个条件
  • ~ 表示“对应结果”

case_when() 是处理“分段变量”的标准方法,
比多层 ifelse 更清晰、更稳定。

结合 NA 的情况

student5 <- mutate(
  student,
  level = case_when(
    is.na(score) ~ "Missing",
    score >= 85 ~ "High",
    score >= 70 ~ "Medium",
    TRUE ~ "Low"
  )
)

student5
    id  name gender age score attendance class  level
1 1001 Alice      F  19 88.00       0.95     A   High
2 1002   Bob      M  20 76.00       0.82     A Medium
3 1003 Cathy      F  19 91.00       0.98     B   High
4 1004 David      M  21 84.75       0.60     B Medium
5 1005  Emma      F  20 84.00       0.88     A Medium

关键点

  • 先判断 NA
  • 再判断数值条件

原因

  • NA 不参与比较
  • 否则结果可能出错

在使用 case_when() 时,
NA 的处理顺序非常重要,通常要放在最前面。

分组操作:group_by

library(dplyr)

student
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A

很多分析问题本质上是:

  • 按组比较(如不同班级)
  • 按组计算统计量(如平均分)

这类问题需要使用 group_by()

基本分组

group_by(student, class)
# A tibble: 5 × 7
# Groups:   class [2]
     id name  gender   age score attendance class
  <dbl> <chr> <chr>  <dbl> <dbl>      <dbl> <chr>
1  1001 Alice F         19  88         0.95 A    
2  1002 Bob   M         20  76         0.82 A    
3  1003 Cathy F         19  91         0.98 B    
4  1004 David M         21  84.8       0.6  B    
5  1005 Emma  F         20  84         0.88 A    

作用

  • class 分组
  • 不改变数据内容

结果特点

  • 数据被标记为“分组数据”
  • 后续操作会在组内进行

group_by() 本身不计算结果,
它只是为后续计算“设定分组结构”。

summarise:组内汇总

student %>%
  group_by(class) %>%
  summarise(avg_score = mean(score, na.rm = TRUE))
# A tibble: 2 × 2
  class avg_score
  <chr>     <dbl>
1 A          82.7
2 B          87.9

含义

  • 按班级分组
  • 计算平均分

关键点

  • na.rm = TRUE 忽略 NA

summarise() 会把每个组“压缩”为一行,
是生成统计结果的核心函数。

多个统计指标

student %>%
  group_by(class) %>%
  summarise(
    avg_score = mean(score, na.rm = TRUE),
    max_score = max(score, na.rm = TRUE),
    count = n()
  )
# A tibble: 2 × 4
  class avg_score max_score count
  <chr>     <dbl>     <dbl> <int>
1 A          82.7        88     3
2 B          87.9        91     2

可以同时计算

  • 平均值
  • 最大值
  • 样本数量

常用函数

  • mean()
  • max()
  • min()
  • n()

summarise() 支持同时生成多个统计指标,
非常适合用于描述性统计与报告输出。

aggregate(基础R方法)

aggregate(score ~ class, data = student, mean, na.rm = TRUE)
  class    score
1     A 82.66667
2     B 87.87500

含义

  • 按 class 分组
  • 计算 score 的均值

特点

  • 属于基础 R 写法
  • 语法相对不直观

aggregate() 是传统方法,
但在实际数据分析中,dplyr 更常用、更清晰。

管道操作:%>%

library(dplyr)

student
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A

在实际数据分析中,往往需要连续执行多步操作:

  • 筛选
  • 变换
  • 分组
  • 汇总

管道 %>% 可以把这些步骤连接起来,使代码更清晰。

基本思想

student %>%
  filter(score > 80)
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1003 Cathy      F  19 91.00       0.98     B
3 1004 David      M  21 84.75       0.60     B
4 1005  Emma      F  20 84.00       0.88     A

含义

  • student 传入 filter()
  • 保留 score > 80 的数据

等价写法

filter(student, score > 80)
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1003 Cathy      F  19 91.00       0.98     B
3 1004 David      M  21 84.75       0.60     B
4 1005  Emma      F  20 84.00       0.88     A

%>% 的作用是:
把左边的结果,作为右边函数的第一个参数。

多步操作串联

student %>%
  filter(score > 80) %>%
  select(id, score, class)
    id score class
1 1001 88.00     A
2 1003 91.00     B
3 1004 84.75     B
4 1005 84.00     A

执行顺序

  1. 筛选高分学生
  2. 选择部分变量

优势

  • 代码按“逻辑顺序”书写
  • 更容易阅读和修改

管道让代码更接近“思考过程”,
而不是嵌套函数的写法。

结合分组与汇总

student %>%
  group_by(class) %>%
  summarise(avg_score = mean(score, na.rm = TRUE))
# A tibble: 2 × 2
  class avg_score
  <chr>     <dbl>
1 A          82.7
2 B          87.9

流程

  • 先分组
  • 再计算统计量

可扩展性

  • 可以继续接更多步骤

管道特别适合处理“分组 → 汇总 → 输出”这类分析流程。

管道的可读性优势

student %>%
  filter(!is.na(score)) %>%
  mutate(score2 = score * 1.1) %>%
  group_by(class) %>%
  summarise(avg = mean(score2))
# A tibble: 2 × 2
  class   avg
  <chr> <dbl>
1 A      90.9
2 B      96.7

步骤拆解

  • 去除缺失值
  • 创建新变量
  • 分组
  • 汇总

对比

  • 比嵌套函数更直观
  • 更适合团队协作

在实际项目中,管道几乎是标准写法,
可以显著提升代码的可读性与可维护性。

数据合并(merge)

student
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A

在实际数据分析中,数据通常来自多个表:

  • 学生成绩表
  • 学生背景信息表
  • 班级信息表

因此需要进行“数据合并(merge)”。

构造第二个数据表

info <- data.frame(
  id = c(1001, 1002, 1003, 1004, 1006),
  major = c("Econ", "Math", "Econ", "CS", "Physics"),
  scholarship = c(1, 0, 1, 0, 1)
)

info
    id   major scholarship
1 1001    Econ           1
2 1002    Math           0
3 1003    Econ           1
4 1004      CS           0
5 1006 Physics           1

特点

  • 同样有 id
  • 包含新的变量

注意

  • id 1005 缺失
  • id 1006 新出现

数据合并的核心是:
找到“共同变量”(通常是 id)作为连接键。

基本 merge

merge(student, info, by = "id")
    id  name gender age score attendance class major scholarship
1 1001 Alice      F  19 88.00       0.95     A  Econ           1
2 1002   Bob      M  20 76.00       0.82     A  Math           0
3 1003 Cathy      F  19 91.00       0.98     B  Econ           1
4 1004 David      M  21 84.75       0.60     B    CS           0

含义

  • 按 id 匹配
  • 只保留两表共有的 id

结果特点

  • 类似“交集”
  • 自动去掉无法匹配的记录

默认 merge 是“内连接”(inner join),
只保留两张表中都存在的观测。

保留左表(left join)

merge(student, info, by = "id", all.x = TRUE)
    id  name gender age score attendance class major scholarship
1 1001 Alice      F  19 88.00       0.95     A  Econ           1
2 1002   Bob      M  20 76.00       0.82     A  Math           0
3 1003 Cathy      F  19 91.00       0.98     B  Econ           1
4 1004 David      M  21 84.75       0.60     B    CS           0
5 1005  Emma      F  20 84.00       0.88     A  <NA>          NA

含义

  • 保留 student 的所有行
  • info 中匹配不到的填 NA

应用

  • 以主数据为基准
  • 补充信息变量

left join 是最常用的合并方式,
因为通常我们希望“保留原始样本”。

保留所有数据(full join)

merge(student, info, by = "id", all = TRUE)
    id  name gender age score attendance class   major scholarship
1 1001 Alice      F  19 88.00       0.95     A    Econ           1
2 1002   Bob      M  20 76.00       0.82     A    Math           0
3 1003 Cathy      F  19 91.00       0.98     B    Econ           1
4 1004 David      M  21 84.75       0.60     B      CS           0
5 1005  Emma      F  20 84.00       0.88     A    <NA>          NA
6 1006  <NA>   <NA>  NA    NA         NA  <NA> Physics           1

含义

  • 保留两表所有 id
  • 不匹配部分用 NA 填充

结果

  • 会出现新 id(如 1006)
  • 也会保留缺失信息

full join 用于探索性分析,
可以帮助发现数据覆盖范围问题。

使用 dplyr 的 join

student %>%
  left_join(info, by = "id")
    id  name gender age score attendance class major scholarship
1 1001 Alice      F  19 88.00       0.95     A  Econ           1
2 1002   Bob      M  20 76.00       0.82     A  Math           0
3 1003 Cathy      F  19 91.00       0.98     B  Econ           1
4 1004 David      M  21 84.75       0.60     B    CS           0
5 1005  Emma      F  20 84.00       0.88     A  <NA>          NA

优势

  • 语法统一
  • 可与管道结合

常见函数

  • left_join()
  • inner_join()
  • full_join()

在实际项目中,dplyr 的 join 系列函数更常用,
因为它们与 %>% 管道可以自然结合。

从数据到可视化

library(dplyr)

student
    id  name gender age score attendance class
1 1001 Alice      F  19 88.00       0.95     A
2 1002   Bob      M  20 76.00       0.82     A
3 1003 Cathy      F  19 91.00       0.98     B
4 1004 David      M  21 84.75       0.60     B
5 1005  Emma      F  20 84.00       0.88     A

数据管理的最终目标之一,是为可视化做好准备:

  • 数据结构清晰
  • 变量定义明确
  • 不含错误或干扰值

为什么需要先整理数据

未整理的数据问题

  • 存在 NA
  • 类型错误
  • 重复值干扰
  • 难以直接绘图

整理后的数据优势

  • 可直接用于绘图
  • 结果更稳定
  • 更容易解释

可视化不是“直接画图”,
而是建立在“已经整理好的数据”之上。

为可视化准备数据

plot_data <- student %>%
  filter(!is.na(score)) %>%
  group_by(class) %>%
  summarise(avg_score = mean(score))

步骤拆解

  • 去除缺失值
  • 按班级分组
  • 计算平均分

结果

  • 每个班级一行
  • 可直接用于绘图

这一步实际上就是:
把“原始数据”转换为“可视化数据”。

基础可视化(Base R)

barplot(plot_data$avg_score,
        names.arg = plot_data$class)

图形含义

  • x轴:班级
  • y轴:平均分

优点

  • 简单直接
  • 无需额外包

即使是最基础的图,也依赖于已经整理好的数据结构。

可视化与数据管理的关系

数据管理负责

  • 清洗数据
  • 构造变量
  • 聚合信息

可视化负责

  • 表达结果
  • 发现模式
  • 支持解释

数据管理与可视化不是两个独立步骤,
而是一个连续流程:

数据 → 清洗 → 转换 → 汇总 → 可视化