章节概述
本章聚焦于“把数据变得可分析”。围绕数据类型与结构、整洁数据与
tidyverse
的核心动词链,本章将建立一套从“看懂表格”到“规范处理”的基础能力:理解数据集、变量与观测单位的关系,掌握
select() / filter() / mutate() /
group_by() + summarise()
等常用操作,并学会通过 left_join() 将多表按共同字段(key)对齐合并,从而形成可复用的数据整理流程。
在此基础上,本章进一步覆盖数据质量诊断与清洗策略,包括缺失值(Deletion /
Imputation)、重复记录、文本标准化与隐性数值转换等关键环节,并引入
EDA 的统计框架:从 summary() 过渡到
skimr::skim()
的结构化概览,再到定制化描述统计与分组洞察,以支持更稳健的变量处理与后续建模决策。
本章学习内容
欢迎开始 第 2 章 的学习。请点击 下方导航卡 进入相应小节:
数据类型与结构
class / str 识别
表格数据与变量
行列与观测单位
整洁数据与动词
dplyr 核心操作
数据清洗与修正
缺失/重复/格式
探索性数据分析
统计摘要与分组
章节练习
复现与挑战
💡 提示:学习完一个小节后,请再次点击 屏幕右下角的章节主页按钮回到本导航页
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
数据是 R 语言执行计算与分析的基础单元,构成后续数据处理与空间建模的核心要素。在 R 环境中,数据依据其底层属性被严格划分为不同的数据类型(data type / class)。
以下为 R 语言中常见的基础数据类型概览:
| 数据类型(Type) | 中文名称 | 代码示例(Example) |
|---|---|---|
logical |
逻辑型(真/假) | TRUE / FALSE |
integer |
整数型 | 1L / -3L |
numeric (或 double) |
数值型(默认实数/浮点数) | 3.14 / 2 |
complex |
复数型 | 2 + 3i |
character |
字符型(文本数据) | "Shanghai" / "Hello" |
raw |
原始字节型 | as.raw(0xb0) |
结合第一章关于 R Project 的操作规范,建议在第二章对应的项目环境中创建独立的 R 脚本(R script),以便对示例代码进行运行与验证。
chapter2_practice.R scripts/
文件夹内)。小提示:
23默认是numeric类型,而23L才是integer
在数据分析过程中,常出现数值表面呈现为数字(如
13),但在 R 环境中被解析为字符型(character)的情况(如 "13")
(例如从 Excel 或 CSV
文件导入数据时,列属性被误识为文本数据)。
数据类型的匹配错误会导致基础算术运算无法执行并触发报错。
处理方案:类型检验与强制转换
在 R 语言环境中,数据结构(data structure)决定了数据的组织与存储逻辑。同构数据采用不同的数据结构,将直接影响后续子集选取、数值计算、统计汇总与数据可视化的执行效率,因此构成数据处理与探索性数据分析(EDA)的基础。
基础数据结构主要包含:一维的向量(vector)与列表(list),二维的矩阵(matrix)与数据框(data frame),以及多维的数组(array)。此外,因子(factor)专用于存储分类变量(categorical variables),在统计建模与分组运算中具备特殊属性。
建议在独立的 R 脚本中逐行执行下表所示的创建与索引代码,重点对比
[]、[ , ]、[[ ]] 及
$ 符号在元素提取规则上的差异。执行运算后,建议结合
class() 与 str()
函数检验目标对象的所属类与底层嵌套逻辑。
在上述结构中,向量与数据框构成了多数复杂对象的基础单元与标准表格形态,后续多源城市数据的清洗与空间探索分析工作多以此为基础展开。
| 数据结构 | 构建函数与代码示例 | 子集索引示例 |
|---|---|---|
| 向量 vector |
x <- c(1, 2, 3)构建长度为 3 的数值向量 x |
x[2]提取第 2 个位置的元素 |
| 列表 list |
lst <- list(num = 1, chr = "a", flag = TRUE)构建列表 lst,支持异构元素并赋予键值名称 |
lst[[2]]提取第 2 个位置的底层元素 lst$chr按名称提取 chr 元素 |
| 矩阵 matrix |
m <- matrix(1:6, nrow = 2)将数值 1 至 6 按 2 行约束生成矩阵 |
m[2, 3]提取第 2 行第 3 列的元素 |
| 数组 array |
a <- array(1:8, dim = c(2, 2, 2))构建维度为 2×2×2 的三维数组 |
a[1, 2, 2]提取第 1 行、第 2 列、第 2 层维度的元素 |
| 因子 factor |
f <- factor(c("男", "女", "女"))将字符向量编码为定性分类变量 |
f[1]提取第 1 个观测值 levels(f)检视该因子的所有类别水平 |
| 数据框 data frame |
df <- data.frame(id = 1:3, y = c(2, 5, 9))构建包含 id 与 y 两列特征的表格数据 |
df[1, "y"]提取第 1 行 y 列对应的观测值 df$y提取 y 列对应的完整向量 |
重点
在实证数据分析中,最常处理的数据形态为表格数据(tabular
data)。该结构由二维的“行 × 列”构成,在
R 语言环境中通常被定义为 data.frame (或后续涉及的现代数据框格式
tibble)。
在执行具体的数据处理运算前,建立对表格底层逻辑的系统认知至关重要。解析标准数据表时,需准确界定以下三个核心结构要素:当前数据集(dataset)的宏观分析背景、每一列所代表的属性变量(variable),以及每一行所对应的独立观测值(observation)。
后续的缺失值清洗、探索性数据分析与统计建模工作,本质上均是对上述基本元素的操作与重组。
准确解析表格底层结构是执行有效数据处理的先决条件。
Gapminder 是一组常用的公开数据集(public dataset),广泛应用于数据分析与可视化教学。 该数据集以“国家 × 年份”为基础观测单元,记录了全球多国在不同历史时期的经济与健康指标。
数据表内包含以下核心变量(variables):
gdpPercap (GDP per capita)lifeExp (life expectancy)pop (population)continent (continent)说明:
gapminder是一个数据包
# 请根据第一章所学知识自行安装gapminder包
# 每次打开 RStudio 新会话后,需要加载包
library(gapminder)
gapminder # gapminder 数据对象本身就是一张表(data.frame/tibble) # A tibble: 1,704 × 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 Afghanistan Asia 1952 28.8 8425333 779.
2 Afghanistan Asia 1957 30.3 9240934 821.
3 Afghanistan Asia 1962 32.0 10267083 853.
4 Afghanistan Asia 1967 34.0 11537966 836.
5 Afghanistan Asia 1972 36.1 13079460 740.
6 Afghanistan Asia 1977 38.4 14880372 786.
7 Afghanistan Asia 1982 39.9 12881816 978.
8 Afghanistan Asia 1987 40.8 13867957 852.
9 Afghanistan Asia 1992 41.7 16317921 649.
10 Afghanistan Asia 1997 41.8 22227415 635.
# ℹ 1,694 more rows
在控制台执行 gapminder
对象后,将返回上述输出结果
表头维度信息
# A tibble: 1,704 × 6
该行表明 gapminder 数据集当前以 tibble
格式呈现。
tibble ≈ data.frame (两者本质均为关系型表格数据)
tibble 是 R 语言中经过优化的现代数据框格式。其主要优势在于提供更友好的控制台排版视图 (如防止长数据填满屏幕、直观显示变量类型),且不改变基础的行列数据逻辑。
其中 1,704 × 6 明确标注了该表包含 1704 行 (观测值) 与 6 列 (变量)。
变量名与数据类型
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
第一行显示各列的变量名:
country:国家continent:所属大洲year:年份lifeExp:预期寿命pop:人口数量gdpPercap:人均 GDP第二行提供对应变量的数据类型缩写
(此为 tibble 提供的辅助视图,非数据本体内容):
<fct>:因子(factor)——
定性的分类变量 (如国家、大洲)<int>:整数(integer)—— 无小数的离散数值 (如年份、人口)<dbl>:浮点数(double)—— 包含小数的连续数值 (如预期寿命、人均 GDP)数据截断与预览机制
为提升阅读体验,tibble 默认仅渲染前 10
行观测值,并在尾部输出省略提示:
# ℹ 1,694 more rows
此提示表明另有 1694 行数据被折叠隐藏。该机制有效避免了大规模数据输出导致的控制台拥堵,便于快速进行数据预览。
拓展测试
建议执行 as.data.frame(gapminder)
以观察传统数据框的输出效果,直观对比两者在排版上的差异。
(此函数可将 gapminder 强制转换为传统的 data.frame
结构)
R Markdown
我们可以使用上一章所学的 Markdown语言 knitr::kable()在
.Rmd 中插入表格
knitr::kable(
head(gapminder, 3), # head() 默认显示前6行,这里设置3就是前3行,以此类推
caption = "Table X. gapminder 数据集前 3 行",
digits = 1 # digits=1 表示:数值列默认保留 1 位小数,便于阅读与对齐
)| country | continent | year | lifeExp | pop | gdpPercap |
|---|---|---|---|---|---|
| Afghanistan | Asia | 1952 | 28.8 | 8425333 | 779.4 |
| Afghanistan | Asia | 1957 | 30.3 | 9240934 | 820.9 |
| Afghanistan | Asia | 1962 | 32.0 | 10267083 | 853.1 |
注意 R Markdown 只能在
.Rmd中knit
在数据分析过程中,通常从“观测主体(研究对象)、观测情境(时间/空间等条件)、属性特征(具体指标)”三个维度来解析表格数据。
变量(variable):描述观测主体特定属性或特征的维度。
(例如:性别、年龄、城市人口、GDP、空气污染浓度等)
数据集(dataset):由多个变量与记录构成的结构化集合,在 R
中多以表格形态呈现(如 data.frame 或
tibble)。
观测(observation):表格中的单行记录。每一行需对应一个确定的观测单位(unit of observation)。
(例如:单一受访者、特定社区、特定城市的单日监测结果等)
在 gapminder 数据集中:
country:国家(观测主体)year:年份(观测时间)lifeExp / pop /
gdpPercap:核心指标(属性特征)gapminder 的单行记录表示: 特定国家在特定年份的一组指标读数。因此,gapminder
的基本观测单位并非单一的“国家”或“年份”,而是:
国家 × 年份(Country-Year) 的面板组合。
导入新的数据集(如 data.frame 或
tibble)后,通常需要进行初步的结构检验。
目的:确认数据规模、变量构成、字段类型以及整体表现形态
# 1) 看这张表有多大(多少行 × 多少列)
dim(数据名)
# 2) 看有哪些变量(列名)
names(数据名)
# 3) 看每一列是什么类型 + 数据结构概览(非常常用)
str(数据名)
# 4) 只预览前几行(避免直接打印刷屏)
head(数据名)
# 5) 只预览后几行(避免直接打印刷屏)
tail(数据名)小提示:查找缺失值可以用
anyNA()或colSums(is.na())或gapminder[!complete.cases(gapminder), ]
案例展示
tibble [1,704 × 6] (S3: tbl_df/tbl/data.frame)
$ country : Factor w/ 142 levels "Afghanistan",..: 1 1 1 1 1 1 1 1 1 1 ...
$ continent: Factor w/ 5 levels "Africa","Americas",..: 3 3 3 3 3 3 3 3 3 3 ...
$ year : int [1:1704] 1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
$ lifeExp : num [1:1704] 28.8 30.3 32 34 36.1 ...
$ pop : int [1:1704] 8425333 9240934 10267083 11537966 13079460 14880372 12881816 13867957 16317921 22227415 ...
$ gdpPercap: num [1:1704] 779 821 853 836 740 ...
str() 函数输出结果str() (structure)
函数用于以紧凑的形式展示数据对象的底层结构。通过该函数可快速获取数据集的宏观特征,包括对象类型、维度规模、各变量的数据类型及前序观测值预览(便于进行数据形态的初步核对)。
典型输出示例:
tibble [1,704 × 6] (S3: tbl_df/tbl/data.frame)
具体释义如下:
[1,704 × 6]:表明该数据集包含
1704 行与 6 列。tibble:指示对象当前呈现为
tibble 格式 tbl_df/tbl/data.frame:标注对象的
S3 类标签(S3 class
attributes),表明其在多数运算场景下继承了标准
data.frame 的操作逻辑。输出主体部分展示了各列的详细信息,例如:
$ country : Factor w/ 142 levels ...
$ year : int [1:1704] 1952 1957 1962 ...
每行对应单一变量(列),主要包含以下结构化要素:
$ country /
$ year:指示变量名称。 (此处的
$ 符号为 str() 的层级打印标记;在实际 R
语法中,$ 常被用作提取特定数据列的操作符,如
gapminder$year)
冒号后的类型标识:声明该列的基础数据类型:
Factor:因子 (用于定性分类变量,包含固定的类别水平
levels)。int:整数 (integer,离散型数值)。num:数值 (numeric,支持小数的连续型数值)。方括号与示例值:标注元素长度与前序观测值预览:
[1:1704]:表示该变量包含 1704 个元素 (与总行数匹配)。1952 1957 ...:提供该列前序数值的直观展示 (非全量数据)。© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
在实证数据分析中,原始数据通常难以直接用于计算。许多统计汇总、数据可视化与空间建模工作之所以遇到障碍,往往并非源于算法的复杂性,而是由于底层数据的组织结构不适配分析需求。
为此,本节引入贯穿数据处理流程的核心概念:整洁数据(tidy data)。这是一种旨在优化数据清洗、汇总与可视化效率的标准化表格结构形态(Wickham, 2014)。
核心原则
补充说明:在标准的表格构建中,通常还需保证“单一表格仅记录单一观测层级”。
Figure 1. 整洁数据基本概念图
整洁数据的核心价值在于:通过建立统一的数据组织规范,在分析初期解决底层结构问题。这种标准化形态使得后续的数据子集提取、分组汇总、可视化与空间建模能够在一致的语法框架下执行,从而有效降低因统计口径不一、重复记录或遗漏带来的误差风险,并显著提升分析工作流的可复用性与可核查性(Wickham, 2014)。
在实证研究中,许多原始数据虽呈现二维表格形态,但其内在结构并不适配数据分析的逻辑要求。常见的问题包括:数据值被作为列名存储、多个独立变量被合并于单一字段,或不同观测层级的数据被混合存放。识别这些非标准结构,有助于在数据清洗与建模前明确结构重组的方向。
列头包含数据值而非变量名
(例如将具体年份 1952、1957
作为列名,而“年份”本应作为独立的变量列)
单一列中混合多个独立变量
(例如将“性别与年龄”、“城市与区县”合并拼接为一个字段)
变量同时存在于行与列之中
(行列结构交叉,导致基础统计与映射难以直接执行)
不同观测层级的数据混杂于同一张表
(例如将个体微观记录与区域宏观汇总指标并列存在,易引发重复计算)
同级观测单位被分散存放于多个文件
(例如同一观测维度的记录按年份拆分为多个独立文件,增加合并与管理成本)
| person_id | measure | value |
|---|---|---|
| P001 | height_cm | 170 |
| P001 | weight_kg | 62 |
| P002 | height_cm | 165 |
| P002 | weight_kg | 58 |
结构解析:表中的 measure
列存储了属性名称,属于“变量标签”而非独立的观测变量。若需计算身高或体重的统计量,该长格式结构需转换为包含
height_cm 与 weight_kg
两个独立变量列的宽格式。
| group_id | id | score | group_mean |
|---|---|---|---|
| G1 | A01 | 82 | 78 |
| G1 | A02 | 74 | 78 |
| G2 | B01 | 91 | 85 |
结构解析:表中混合了不同观测层级(粒度)的信息。个体层面的微观观测(score)与组级别的宏观汇总指标(group_mean)并列放置,在执行总体计算时会直接导致汇总指标被重复统计。
| city | 2019 | 2020 | 2021 |
|---|---|---|---|
| A市 | 120 | 110 | 115 |
| B市 | 95 | 90 | 92 |
结构解析:2019、2020、2021
实际上是“年份”这一变量的具体取值。在整洁数据标准下,“年份”应被提取为独立的列变量,而表中的数值应归集为对应的数据列。
| person_id | sex_age | income |
|---|---|---|
| P001 | F_23 | 5200 |
| P002 | M_31 | 6800 |
结构解析:sex_age
字段混合了性别与年龄两类独立信息。若需分别执行按性别分组的汇总或年龄分布的统计,必须优先对该字段进行文本拆分,将其重构为两个独立变量。
类似 2025/04/29 19:03:52 的时间戳(timestamp)通常被视为单一复合变量进行存储,符合整洁数据的规范要求。
仅在特定的分析场景下(如按日期、小时、星期进行周期性分组汇总),才需从时间戳中提取出
date、hour、weekday
等新列。此过程属于特征工程(feature
engineering)范畴,而非底层结构错误。
评估准则
在数据处理实践中,判断数据表是否符合整洁数据标准,其核心不在于数据体量,而在于底层结构是否具备逻辑一致性与清晰的解释力。一套可执行的结构诊断框架主要涵盖三个维度:观测单位的界定(行记录的现实映射)、变量属性的归位(列索引的独立性),以及单元格数据的原子化(单一单元格仅承载单一基元信息)。当上述三个维度均能被清晰界定时,后续的列拆分、数据合并或宽长表转换等重组操作,其执行路径将变得更具确定性与可控性。
Step 1界定观测单位(明确单行记录的分析粒度)
确认单行记录是对应单一受访者、特定地理单元,还是“空间 × 时间”的复合面板结构。
若难以用规范的陈述句界定,通常意味着数据表中混杂了不同层级的信息,或关键变量被错误映射为行或列索引。
Step 2检验变量映射(确认变量是否独立成列)
标准的列名应表征独立的变量维度(如
year、sex、income)。
若列名呈现为具体的属性取值(如
2019、2020、male、female),则提示特定的变量维度(如年份、性别)被错误地横向展开为列结构。
Step 3审查单元格基元(核验“一格一值”原则)
标准单元格应仅包含不可再分的原子化数值(单一数值、单一分类标签或标准时间戳)。
若出现复合拼接字符(如
F_23、GDP_USD),则表明多个独立变量被合并存储,需进行特征分离。
重组导向
在完成结构诊断后,数据整理的核心目标可归纳为:恢复数据的标准映射关系
(即变量归于列、观测归于行、单元格原子化)。不同类型的非整洁结构对应特定的重组路径:
变量横向隐匿于列名 → 将列标签重组为独立变量列
(例:列名呈现为
1952、1957
等年份取值;重组目标是通过宽长转换,将“年份”提取为独立的
year 变量列)
多变量嵌套于单一列 → 将复合列拆分为多个独立列
(例:存在 male_23
等拼接字段;重组目标是将其结构化拆分为 sex 与
age 两个独立变量,以支持后续的分组建模)
单变量交叉存在于行列 →
统一变量映射维度,消除结构冗余
(例:行索引与列索引同时反映空间类别或时间维度;重组目标是消除交叉表达,使同一变量仅存在于单一的列维度)
异质观测层级混杂 → 按分析粒度进行层级分离
(例:同一表格交替出现微观个体记录与宏观总体均值;重组目标是将明细数据与汇总数据拆分为不同表格,确保单表的观测单位绝对一致)
同构数据分散存放 → 沿纵向或横向维度执行数据拼接
(例:同结构的数据按年份拆分为多个 csv
文件;重组目标是合并为单一的长格式表格,并生成特定的来源标识列)
本节侧重于培养数据结构诊断思维,而非单纯强调具体函数的机械记忆。代码语法与工具细节可通过技术文档或人工智能辅助快速补充;但在实证分析中,唯有底层逻辑清晰,才能明确数据重组的下一步操作方向、内在动机,并精准运用检索工具获取解决方案。
简而言之,本教程倡导:在实施代码干预前,优先理清数据的底层结构与重组逻辑。逻辑框架的完备性,优先于代码编写的熟练度;前者决定了分析的上限,后者仅为具体执行的工具手段。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
在本教程第一章中,已对
tidyverse 核心包集进行了基础概述。前文2.0 节也明确了实证分析所依赖的基础形态——整洁数据(即变量归于列、观测归于行、单元格原子化)。本节的核心在于将上述结构化原则转化为具体、可执行的数据操作指令。
掌握 tidyverse 语法体系的核心,在于建立基于数据结构的诊断与重构意识。在准确识别非整洁结构特征的前提下,匹配相应的处理函数(如数据子集提取、形态转换与字段拆分),可显著提升数据清洗流程的逻辑性与可解释性。
以下内容将以 gapminder 数据集为例,系统展示
tidyverse 工具箱中核心函数的输入与输出映射关系。
建议在独立的 R 脚本中逐行执行以下代码,并实时观察控制台(Console)或数据视图(Viewer)中的输出变化:
# 1) 加载核心包
library(tidyverse)
library(gapminder)
# 2) 准备一个测试练习数据 test_data(将 gapminder数据赋值给 test_data)
test_data <- gapminder
# 3) 确认数据已就绪
head(test_data,3) # A tibble: 3 × 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 Afghanistan Asia 1952 28.8 8425333 779.
2 Afghanistan Asia 1957 30.3 9240934 821.
3 Afghanistan Asia 1962 32.0 10267083 853.
Tidyverse 核心工具箱
本节甄选了 tidyverse 数据处理体系中最为基础且常用的
9
个核心函数。
点击下方卡片,即可进入对应的功能解析与代码实践:
3.2.1 数据概览
glimpse()
3.2.2 变量选择
select()
3.2.3 观测筛选
filter()
3.2.4 数据排序
arrange()
3.2.5 变量构造
mutate()
3.2.6 表格合并
left_join()
3.2.7 分组汇总
group_by()
3.2.8 拆分合并
separate()
3.2.9 长宽转换
pivot_longer()
💡 提示:此处仅列出了 tidyverse
生态中高频使用的函数。实际数据处理中还有更多强大的工具(如
slice, rename, bind_rows
等)。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
glimpse()glimpse()导入新数据集后,首要步骤通常为解析其底层结构,而非直接执行数据清洗或数值计算。明确数据集的维度规模、变量构成以及基础形态,将直接决定后续数据重组与分析的执行路径。
在 tidyverse 语法体系中,glimpse()
函数提供了一种紧凑的数据结构预览方案。该函数将表格数据进行转置显示,依次陈列变量名称、数据类型(data
type)以及对应的部分前序观测值,从而支持对数据表微观结构的系统性审查。
Figure 2. glimpse() 基本概念图
Rows: 1,704
Columns: 6
$ country <fct> "Afghanistan", "Afghanistan", "Afghanistan", "Afghanistan", …
$ continent <fct> Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, …
$ year <int> 1952, 1957, 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1997, …
$ lifeExp <dbl> 28.801, 30.332, 31.997, 34.020, 36.088, 38.438, 39.854, 40.8…
$ pop <int> 8425333, 9240934, 10267083, 11537966, 13079460, 14880372, 12…
$ gdpPercap <dbl> 779.4453, 820.8530, 853.1007, 836.1971, 739.9811, 786.1134, …
%>%:用顺序表达多步操作
重要
在 tidyverse 的工作流中,%>%
用于将左侧对象传递给右侧函数作为输入。
(例如:test_data %>% glimpse()
等价于 glimpse(test_data))
其优势在于:当需要连续执行多步处理时,可以把步骤按逻辑顺序串联成一条清晰的“处理链”,从而减少嵌套写法带来的阅读与调试负担。
简而言之,防止过度‘套娃’!
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
select()select()数据处理的常规首要步骤是界定所需的核心特征(即提取目标变量列,剔除冗余字段)。
在处理大规模数据时,优先提取必要变量可有效降低内存消耗并提升计算效率。
在 tidyverse 中,select()
函数用于执行基于列的提取操作。可通过直接输入变量名保留目标列,或使用减号
- 前缀剔除特定列,从而将数据表重构为紧凑的分析子集。
Figure 3. select() 基本概念图
# 例如:只保留与“国家—年份—人口-人均GDP”相关的列
test_data %>%
select(country, year, lifeExp, pop, gdpPercap) %>%
head(3) # A tibble: 3 × 5
country year lifeExp pop gdpPercap
<fct> <int> <dbl> <int> <dbl>
1 Afghanistan 1952 28.8 8425333 779.
2 Afghanistan 1957 30.3 9240934 821.
3 Afghanistan 1962 32.0 10267083 853.
select() 基础提取语法
基础操作
基于变量名提取 -
test_data %>% select(country, year, lifeExp)
(适用于目标变量较少且命名字段明确的场景)
基于列索引提取 - 连续列 (提取第
1 至第 4
列):test_data %>% select(1:4)
- 不连续列 (提取第 1、3、5、6
列):test_data %>% select(c(1, 3, 5, 6))
- 剔除连续列 (排除第 1 至第 2
列):test_data %>% select(-c(1:2))
(适用于变量较多时的批量保留或剔除)
剔除特定变量 -
test_data %>% select(-gdpPercap)
(适用于仅需排除少数特定列的数据降维场景)
进阶操作
当目标变量具有特定的命名模式特征时,可利用选择辅助函数(select helpers)进行批量提取。
(以下示例基于虚拟数据集 my_data)
[Image of dplyr select helpers functions such as starts_with and contains]
my_data %>% select(contains("percent"))my_data %>% select(starts_with("pct_"))my_data %>% select(ends_with("_rate"))my_data %>% select(matches("^pct|_share$"))若需剔除上述匹配列,在辅助函数前添加 -
即可。
(如
select(-contains("percent")))
解析 matches("^pct|_share$") (正则表达式基础)
matches():通过输入正则表达式对变量名进行模式匹配。^pct:^
为定位符,表示字符串起始位置,即匹配“以 pct
开头的列名”。_share$:$
为定位符,表示字符串结束位置,即匹配“以 _share
结尾的列名”。|:逻辑操作符,表示“或” (OR)。该语句的整体逻辑为:
提取所有以 pct 起始或以 _share 结尾的变量列
典型应用场景
提取包含数字的列 (如
Q1–Q30、gene_1 等):
my_data %>% select(matches("\\d"))
(\\d
代表匹配任意单一数字)
提取包含特殊字符的列 (如
* 等):
my_data %>% select(matches("\\*"))
(正则表达式中的元字符需使用双反斜杠进行转义;此处
* 写作 \\*)
基于多重模式提取 (无位置限定):
my_data %>% select(matches("rna|dna|gene|_mean$|_sd$"))
(通过逻辑或 |
同时匹配多个关键词及特定后缀的汇总列)
剔除标识列,保留指标变量 (如排除 id 与时间字段):
my_data %>% select(-matches("^(id|ID|year|date|time)$"))
(常用于特征标准化或统计汇总前的非数值列清洗)
基于字符向量的柔性提取:
my_data %>% select(any_of(c("lifeExp", "pop", "gdpPercap", "pm25_mean", "pm25_sd")))
(any_of()
具备较高的容错率,若向量中某些列名在当前数据集中不存在,系统将自动忽略且不触发报错)
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
filter()filter()在表格数据处理中,根据特定的研究条件提取目标观测记录(即筛选数据行)是常规的基础操作。 (如仅保留特定年份、特定空间区域或满足设定阈值的样本记录)
在 tidyverse 语法体系中,filter()
函数专用于基于逻辑条件执行数据行的提取。通过构建特定的逻辑判断式,可精准滤除无关数据,进而构建符合研究边界的分析子集。
Figure 4. filter() 基本概念图
# A tibble: 4 × 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 West Bank and Gaza Asia 2007 73.4 4018332 3025.
2 Yemen, Rep. Asia 2007 62.7 22211743 2281.
3 Zambia Africa 2007 42.4 11746035 1271.
4 Zimbabwe Africa 2007 43.5 12311143 470.
# 例 2:多条件筛选(同时满足):2007 年 + 欧洲 并展示前 4 行
test_data %>%
filter(year == 2007, continent == "Europe" ) %>%
head(3) # A tibble: 3 × 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 Albania Europe 2007 76.4 3600523 5937.
2 Austria Europe 2007 79.8 8199783 36126.
3 Belgium Europe 2007 79.4 10392226 33693.
# 例 3:范围条件:1992年,人均寿命超过 75 的记录,保留 3 列变量,并展示前 3 行
test_data %>%
filter(year == 1992, lifeExp > 75) %>%
select(country, year, lifeExp) %>%
head(3) # A tibble: 3 × 3
country year lifeExp
<fct> <int> <dbl>
1 Australia 1992 77.6
2 Austria 1992 76.0
3 Belgium 1992 76.5
filter() 条件写法速记
常用比较符号
==:等于(注意:不是
=)!=:不等于>、>=、<、<=:大于/小于(含等号)多个条件怎么写?
filter(A, B) 或
filter(A & B)filter(A | B)集合匹配:%in%非常常用
filter(x %in% c("a", "b", "c"))test_data %>% filter(continent %in% c("Europe", "Asia"))区间筛选:一次写清“范围”
filter(x >= 10, x <= 20)filter(between(x, 10, 20))between() 读作:x 在 10 到 20
之间)模糊匹配:只记得“包含/开头/结尾”怎么办?
当需要按字符模式筛选时,可配合 stringr::str_detect()
(tidyverse
载入后可直接用)
filter(str_detect(country, "land"))filter(str_detect(country, "^United"))filter(str_detect(country, "stan$"))缺失值:NA 需要显式处理
filter(!is.na(x))filter(is.na(x))提醒
continent == "Europe"(字符比较)matches() 写法(同为正则思路)© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
arrange()arrange()数据处理的常规操作包含根据特定变量对观测记录进行重排,以支持极值检验、趋势观察或生成位序结果。
(如按年份升序、按人均 GDP 降序,或执行“所属大洲 →
年份”的分层多级排序)
在 tidyverse 工具链中,arrange()
函数专用于执行数据行的重新排列。该操作不改变基础列结构,仅调整观测记录的呈现顺序。
Figure 5. arrange() 基本概念图
# 示例 1:按人均 GDP 升序(默认)
test_data %>%
arrange(gdpPercap) %>% #请注意,arrange只是改变了排序,并没有覆盖原始数据
head(5) #如需将原始数据排列,请使用 test_data_sorted <- test_data %>% arrange(gdpPercap) # A tibble: 5 × 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 Congo, Dem. Rep. Africa 2002 45.0 55379852 241.
2 Congo, Dem. Rep. Africa 2007 46.5 64606759 278.
3 Lesotho Africa 1952 42.1 748747 299.
4 Guinea-Bissau Africa 1952 32.5 580653 300.
5 Congo, Dem. Rep. Africa 1997 42.6 47798986 312.
# A tibble: 5 × 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 Kuwait Asia 1957 58.0 212846 113523.
2 Kuwait Asia 1972 67.7 841934 109348.
3 Kuwait Asia 1952 55.6 160000 108382.
4 Kuwait Asia 1962 60.5 358266 95458.
5 Kuwait Asia 1967 64.6 575003 80895.
arrange() 常用写法速记
升序(ascending)
默认:arrange(x)
(从小到大 / 从早到晚)
降序(descending):arrange(desc(x))
(desc =
descending;从大到小)
多列排序:arrange(a, b)
(先按 a 排,再在 a
相同的组内按 b 排)
混合升/降序:arrange(a, desc(b))
(常用于“先分组排序,再按指标做排名”)
按字符排序:字符型变量默认按字典序(A → Z)。
(若需自定义顺序,可将其设为 factor
并调整 levels)
常见场景:arrange(year, desc(gdpPercap))
(按年份组织,并在每年内查看人均 GDP
的高值记录)
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
mutate()mutate()在数据重组与特征工程中,基于现有数据列派生新变量是常规的基础操作。
(例如:频数转比例、数值单位换算、对数变换,或生成分类标签变量)
在 tidyverse 语法体系中,mutate()
函数专用于执行基于列维度的新增或覆盖操作。该运算不改变基础的观测记录行数,仅在原数据表尾部追加新变量列,或通过重新赋值运算更新已有同名变量的值。
Figure 6. mutate() 基本概念图
# 示例:基于已有变量生成新变量(创建新列)
test_data %>%
mutate(
pop_million = round(pop / 1000000,1), # 百万人口:从“人数”换算为“百万”,保留1位
gdp_total = gdpPercap * pop, # GDP 总量:人均 GDP × 人口
log_gdp_pc = log(gdpPercap) # 人均GDP 对数变换
) %>%
select(country, year, pop_million, gdp_total, log_gdp_pc) %>% # 选择新建的列
head(5) # A tibble: 5 × 5
country year pop_million gdp_total log_gdp_pc
<fct> <int> <dbl> <dbl> <dbl>
1 Afghanistan 1952 8.4 6567086330. 6.66
2 Afghanistan 1957 9.2 7585448670. 6.71
3 Afghanistan 1962 10.3 8758855797. 6.75
4 Afghanistan 1967 11.5 9648014150. 6.73
5 Afghanistan 1972 13.1 9678553274. 6.61
mutate() 常用写法速记
新增变量(新列):mutate(new_var = ...)
一次生成多个变量:mutate(a = ..., b = ..., c = ...)
覆盖同名变量(谨慎使用):
mutate(x = ...)
(若 x
已存在,将被新结果覆盖;建议先用新变量名,确认无误后再覆盖)
配合 if_else()
做规则标签(常用):
例如:按年份把数据分成“2007 年”与“非
2007 年”两类:
(适合生成“是/否”“高/低”“组别标签”等规则变量)
用 case_when()
做多分支规则(多类别标签更清晰):
例如:把“国家全称”映射为简写代号,并额外给出一个“洲内优先级标签”
test_data %>%
mutate(
country_code = case_when(
country == "China" ~ "CN",
country == "United States" ~ "US",
country == "United Kingdom" ~ "UK",
country == "Japan" ~ "JP",
country == "India" ~ "IN",
TRUE ~ "Other"
),
# 再举一例:用 case_when() 生成一个“分组标签”
group_tag = case_when(
continent == "Asia" ~ "Asia",
continent %in% c("Europe", "Oceania") ~ "Eur+Oce",
TRUE ~ "Rest"
)
) %>%
select(country, continent, country_code, group_tag) %>%
head(10)(当规则不止两类时,优先用
case_when();通常优于嵌套
if_else())
mutate() + countrycode
生成国家标准代号(ISO2/ISO3)
进阶操作
当数据中的国家以英文全称表示(如
China、United States)时,常见需求是生成更规范、也更便于合并的国家代号(如
CN/CHN、US/USA)。
此时可在 mutate() 中配合 countrycode
包进行标准化映射。(使用前需先安装并加载该包)
test_data %>%
mutate(
iso3 = countrycode::countrycode( # 使用 countrycode里的函数
sourcevar = country, # 我们数据中的 country列
origin = "country.name", # 输入:国家英文全称
destination = "iso3c" # 输出:ISO3(如 CHN / USA / GBR)
),
iso2 = countrycode::countrycode( # 使用 countrycode里的函数
sourcevar = country, # 我们数据中的 country列
origin = "country.name", # 输入:国家英文全称
destination = "iso2c" # 输出:ISO2(如 CHN / USA / GBR)
)
) %>%
select(country, iso3, iso2) %>%
head(5) # A tibble: 5 × 3
country iso3 iso2
<fct> <chr> <chr>
1 Afghanistan AFG AF
2 Afghanistan AFG AF
3 Afghanistan AFG AF
4 Afghanistan AFG AF
5 Afghanistan AFG AF
提醒:若出现 NA 映射失败通常意味着国家名称存在别名/拼写差异,可先用
filter(is.na(iso3))找出问题国家名,再做修正或自定义匹配
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
left_join()left_join()常用
在多源数据分析中,单一数据表通常难以涵盖所有的分析维度:主表多用于记录核心观测指标(如人口规模、预期寿命),而附表则提供必要的属性补充(如空间区划、政策分组等)。此时需依据关联字段(key)对多张表执行横向匹配与拼接。
在 tidyverse 工具链中,left_join()
实现了以左表为基准的关联逻辑:在保留左表全量观测记录的前提下,将右表中匹配的变量字段追加至左表尾部。
Figure 7. left_join()基本概念图
为gapminder数据补充一个是否为OECD成员国的标签
示例
# 注意:这里只列出少数国家作为例子;未匹配到的国家会得到 NA
oecd_lookup <- tibble::tibble(
country = c("United States", "United Kingdom", "Japan", "Germany", "France", "Canada", "Australia"),
oecd = "OECD"
) # OECD成员国数据
test_data_joined <- test_data %>% left_join(oecd_lookup, by = "country")
# 快速查看合并后的结果(只展示过滤后的部分)
test_data_joined %>%
select(country, year, lifeExp, oecd) %>%
filter(country == 'Germany') %>% # 看一下德国数据后是否标上了 OECD
head() # A tibble: 6 × 4
country year lifeExp oecd
<chr> <int> <dbl> <chr>
1 Germany 1952 67.5 OECD
2 Germany 1957 69.1 OECD
3 Germany 1962 70.3 OECD
4 Germany 1967 70.8 OECD
5 Germany 1972 71 OECD
6 Germany 1977 72.5 OECD
left_join() 在做什么?
核心概念
必须有“共同字段”(key)
例如使用 country 作为 key:左表 test_data
的每一行,会在右表 oecd_lookup 中查找同名的
country,并将匹配到的信息拼接到左表。
“左表优先”
左表的行数与顺序不会减少;即使右表找不到匹配项,也会保留该行,只是右表新增列会出现
NA。
by = "country" 的含义
明确指定两张表用哪一列进行匹配。
常见 若两表 key
列名不同,可写成:by = c("left_key" = "right_key")(左表列名 = 右表列名)
常见错误
类型不一致导致匹配失败:例如左表 key
为字符,右表 key 为因子/数值。必要时先统一类型:
mutate(country = as.character(country))
重复 key 引发“行数膨胀”:如果右表同一个 key
出现多行,会形成多对多匹配,结果行数可能显著增加。
(通常需要先检查右表 key
是否唯一,并明确预期的合并关系)
left_join() 常用
inner_join():仅保留两表都能匹配到的行(交集)
right_join():以右表为主(较少用;多数场景可交换左右表后用
left_join())
full_join():保留两表所有行(并集;无法匹配处补 NA)
anti_join() /
semi_join():用于“找差异 / 做筛选”
anti_join():找出左表中“在右表找不到匹配”的行semi_join():仅保留左表中“能在右表匹配到”的行(不带入右表列)补充
不通过 key 的“平行合并”
(当数据结构一致时)
有时两张表并不是“按 key 补信息”,而是行顺序与行含义完全一致(例如同一批样本、同一排序方式、同一时间序列),只需要把列横向拼接即可。此类合并不属于 join,更接近“列绑定”
# 演示用;将数据分开之后再拼回去
left_part <- test_data %>%
select(country, year) # 演示用左边数据
right_part <- test_data %>%
select(lifeExp, pop, gdpPercap) # 演示用右边数据
merged_cbind <- bind_cols(left_part, right_part) # 直接将左右数据按照当前排序合并
merged_cbind %>% head() # A tibble: 6 × 5
country year lifeExp pop gdpPercap
<fct> <int> <dbl> <int> <dbl>
1 Afghanistan 1952 28.8 8425333 779.
2 Afghanistan 1957 30.3 9240934 821.
3 Afghanistan 1962 32.0 10267083 853.
4 Afghanistan 1967 34.0 11537966 836.
5 Afghanistan 1972 36.1 13079460 740.
6 Afghanistan 1977 38.4 14880372 786.
进阶补充
适用场景(当 key
无法“完全一致”)
当两张表的 key 存在轻微差异时,常规 left_join()
会匹配失败,例如:
"Untied States" vs
"United States""UK" vs
"United Kingdom""Cote dIvoire" vs
"Côte d'Ivoire"核心思想(近似匹配)
模糊匹配会根据“字符串相似度”寻找最接近的候选,从而把原本匹配不到的记录“尽可能对齐”。
结果通常会附带一个距离/相似度指标,便于人工核查匹配质量。
示例. (了解皮毛即可)
# 需要额外包:fuzzyjoin(用于模糊 join)
# install.packages("fuzzyjoin")
library(fuzzyjoin)
# 根据字符串相似度进行 left join,并输出匹配距离
result <- stringdist_left_join(
left_tbl, right_tbl,
by = c("country" = "country_name"),
method = "jw", # Jaro-Winkler(常用于名称匹配)
max_dist = 0.15, # 阈值越小越严格
distance_col = "dist" # 输出距离列,便于检查
)输出会发生什么变化?
dist(匹配距离)NA(与 left_join() 行为一致)注意:模糊匹配应作为“备选手段”。实践中通常先做规范化清洗(如
trimws()、统一大小写、去除多余空格/标点),再考虑 fuzzy matching,以降低误匹配风险。
延伸阅读:建议自行检索关键词
- fuzzyjoin stringdist_left_join
- Jaro-Winkler distance
- record linkage(记录链接)
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
group_by()group_by() 与
summarise()常用
在标准整洁数据形态下,按组汇总(group-by
aggregation)是提取聚合特征的常规运算。
(如对比不同空间单元的平均寿命、统计特定时间节点的总人口,或执行“大洲
× 年份”的交叉层级汇总)
该类运算通常遵循“拆分-应用-合并”(Split-Apply-Combine)范式,主要包含以下两个执行步骤:
group_by():界定分组变量(grouping
variable),即确立数据拆分与聚合的维度。summarise():针对各独立数据子集执行标量计算并生成汇总指标(如计算均值、中位数、总和或观测频数等)。Figure 8. group_by() + summarise() 基本概念图
示例 1 按洲汇总(每个洲有多少条记录?平均寿命是多少?)
test_data %>%
group_by(continent) %>% # 按照洲名分组
summarise( # 在分好的组内进行汇总
n = n(), # 组内观测数(行数) n() 检查行数
lifeExp_mean = mean(lifeExp), # 平均寿命
lifeExp_median = median(lifeExp), # 中位数寿命
.groups = "drop" # 重要!取消分组-方便后续
) %>%
arrange(desc(lifeExp_mean)) # 将数据重新按平均寿命降序排列 # A tibble: 5 × 4
continent n lifeExp_mean lifeExp_median
<fct> <int> <dbl> <dbl>
1 Oceania 24 74.3 73.7
2 Europe 360 71.9 72.2
3 Americas 300 64.7 67.0
4 Asia 396 60.1 61.8
5 Africa 624 48.9 47.8
示例 2 按年份汇总(全球人口总量 + 平均寿命)
test_data %>%
group_by(year) %>% # 按照年份分组
summarise( # 在分好的组内进行汇总
n_country = n_distinct(country), # 当年包含了多少个国家/地区(去重计数)
pop_total = sum(pop), # 全球人口总和
lifeExp_mean = mean(lifeExp), # 当年平均寿命
.groups = "drop" # 取消分组-方便后续
) %>%
arrange(year) %>%
head() # A tibble: 6 × 4
year n_country pop_total lifeExp_mean
<int> <int> <dbl> <dbl>
1 1952 142 2406957150 49.1
2 1957 142 2664404580 51.5
3 1962 142 2899782974 53.6
4 1967 142 3217478384 55.7
5 1972 142 3576977158 57.6
6 1977 142 3930045807 59.6
重点
示例 3 #双重分组(国家 × 年份):构建“相对全球平均”的寿命指数
# 双重分组(国家 × 年份):构建“相对全球平均”的寿命指数
# 目标:对每一年,先算全球平均寿命;再算每个国家当年的平均寿命;
# 最后计算指数 = 国家平均 / 全球平均 × 100
# 1) 当年“全球平均寿命”
global_year <- test_data %>%
group_by(year) %>% # 按照年份分组
summarise( # 在分好的组内进行汇总
lifeExp_global = mean(lifeExp), # 计算每年全球的平均寿命
.groups = "drop" # 取消分组-方便后续
)
# 2) 当年“国家/地区平均寿命”(按 国家/地区×年份)
country_year <- test_data %>%
group_by(year, country) %>% # 按照年份 + 国家/地区分组
summarise( # 在分好的组内进行汇总
lifeExp_country = mean(lifeExp), # 计算每年+各国的平均寿命
.groups = "drop" # 取消分组-方便后续
)
# 3) 合并后计算指数(国家相对全球 = 100 为全球平均)
lifeExp_index <- country_year %>%
left_join(global_year, by = "year") %>% # 按照年份合并表 (后续会教)
mutate( # 创造新列,计算指数
lifeExp_index = lifeExp_country / lifeExp_global * 100
)
# 预览:以 2007 年为例,查看指数最高的前 10 个国家
lifeExp_index %>%
filter(year == 2007) %>%
arrange(desc(lifeExp_index)) %>%
select(year, country, lifeExp_country,
lifeExp_global, lifeExp_index) %>%
head(5) # 2007年各国/地区平均 vs 全球平均寿命前五名 # A tibble: 5 × 5
year country lifeExp_country lifeExp_global lifeExp_index
<int> <fct> <dbl> <dbl> <dbl>
1 2007 Japan 82.6 67.0 123.
2 2007 Hong Kong, China 82.2 67.0 123.
3 2007 Iceland 81.8 67.0 122.
4 2007 Switzerland 81.7 67.0 122.
5 2007 Australia 81.2 67.0 121.
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
separate()separate()(与 unite()
对照)在真实数据中,常见的一类结构问题是:多个信息被写在同一列中(需要拆分),或需要将多个列合并为一个更便于分析与呈现的字段(需要合并)。
在 tidyr 中,separate() 与
unite() 分别用于完成这两类操作。
separate()
Figure 9. separate() 基本概念图
下面构造一个简化的 messy data 示例:将“姓名-性别-年龄”在同一列里
# 构造示例数据(messy:多个变量挤在一列)
messy_people <- tibble::tibble(
id = 1:4,
profile = c("Li_M_23", "Wang_F_19", "Zhang_M_31", "Chen_F_26")
)
messy_people # A tibble: 4 × 2
id profile
<int> <chr>
1 1 Li_M_23
2 2 Wang_F_19
3 3 Zhang_M_31
4 4 Chen_F_26
# 使用 separate() 拆成三列:name、sex、age
clean_people <- messy_people %>% # 目标数据
tidyr::separate( # 拆分函数
col = profile, # 需要拆分的列 column
into = c("name", "sex", "age"), # 拆分成3列 into,名字-性别-年龄
sep = "_", # profile中的隔断特征是下划线‘_’
convert = TRUE # 自动转换数据类型,比如新的23从文本变成了数值
)
clean_people # 试着比较 convert = T 与 F的输出区别,注意看年龄列的数据类型 # A tibble: 4 × 4
id name sex age
<int> <chr> <chr> <int>
1 1 Li M 23
2 2 Wang F 19
3 3 Zhang M 31
4 4 Chen F 26
有时候 经纬度+高程
写在一起的情况,也可以将其拆分成3列
(经度,纬度,高度)
messy_geo <- tibble::tibble(
place = c("A", "B", "C"),
lon_lat_alt = c("121.47,31.23,15", "103.82,36.06,1520", "114.06,22.55,5")
)
messy_geo %>%
tidyr::separate(
col = lon_lat_alt,
into = c("lon", "lat", "alt"),
sep = ",",
convert = TRUE
) # A tibble: 3 × 4
place lon lat alt
<chr> <dbl> <dbl> <int>
1 A 121. 31.2 15
2 B 104. 36.1 1520
3 C 114. 22.6 5
unite()
Figure 10. unite() 基本概念图
当日期时间被拆成多列(year, month,
day, hour ,minute
,second)时,经常需要合并为一个 timestamp
字段用于排序、绘图或建模
time_parts <- tibble::tibble( # 构建一个示例数据,可通过输入 time_parts查看
year = c(2025, 2025),
month = c(4, 4),
day = c(29, 30),
hour = c(19, 8),
minute = c(3, 45),
second = c(52, 10)
)
time_parts %>% # 对示例数据进行整理
tidyr::unite( # 合并函数
col = "timestamp", # 需要合并成 timestamp列
year, month, day, # 原始数据中的列名
hour, minute, second,
sep = "-", # 可以以连字符连接
remove = FALSE # 不删除原始数据,可以对比结果
) # A tibble: 2 × 7
timestamp year month day hour minute second
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2025-4-29-19-3-52 2025 4 29 19 3 52
2 2025-4-30-8-45-10 2025 4 30 8 45 10
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
pivot_*()pivot_longer() 与
pivot_wider()在数据整理中,数据的“形状”往往直接影响后续处理与分析的效率。常见的整理需求之一,就是在两种表格形态之间进行转换:
宽数据(Wide
Data):
列名本身包含了变量取值(如
2019、2020 作为列名)
适合人类阅读与报表展示。
长数据(Long
Data):
更符合 Tidy Data(整洁数据)
的组织原则,即“一列变量、一行观测”。
更适合计算机处理与可视化。
在 tidyr 中,可通过“旋转”(Pivot)系列函数实现形态转换:
(pivot_longer() 与
pivot_wider())
“到底谁才是整洁数据?” 答案是:长数据(Long Data)是 Tidyverse 定义的标准“整洁数据”,但在实际工作中,我们需要根据下游用途灵活切换。
长数据 (Long Data):可视化
(ggplot2) 分组统计
这是 R 语言/计算机
最喜欢的格式。只要你需要画图(例如用不同颜色代表不同年份)或者分组汇总,就必须转为长数据。
宽数据 (Wide Data):报表展示 矩阵运算
这是 人类阅读(Excel
风格)最直观的格式。此外,后续学习某些数学模型(如聚类分析)时,也必须使用这种格式作为输入。
pivot_longer()
常用 这是数据清洗的第一步,通常用于把“列名里的信息”折叠进数据行里。
Figure 11. pivot_longer() 基本概念图
场景:列名是年份(1999,
2000),而不是变量名。我们需要把这些年份放到一列叫
year 的变量中
# 构造示例数据(宽数据:年份被写在列名上)
wide_data <- tibble::tibble(
country = c("A", "B", "C"),
`1999` = c(745, 300, 500), # 注意:数字作为列名需加反引号
`2000` = c(200, 400, 600)
)
wide_data # A tibble: 3 × 3
country `1999` `2000`
<chr> <dbl> <dbl>
1 A 745 200
2 B 300 400
3 C 500 600
# 使用 pivot_longer() 将宽表转换为长表
long_data <- wide_data %>%
tidyr::pivot_longer(
cols = c(`1999`, `2000`), # 1) 哪些列需要“旋转”?(也可以写 !country)
names_to = "year", # 2) 原来的列名(1999/2000)去哪里?-> 新变量 "year"
values_to = "cases", # 3) 原来的数值(745/200...)去哪里?-> 新变量 "cases"
names_transform = list(year = as.integer) # 可选:顺便把年份转为整数
)
long_data # A tibble: 6 × 3
country year cases
<chr> <int> <dbl>
1 A 1999 745
2 A 2000 200
3 B 1999 300
4 B 2000 400
5 C 1999 500
6 C 2000 600
注意:转换后的长表,行数增加了(3个国家 × 2个年份 = 6行),但结构满足了 Tidy Data 原则
常用
当我们需要制作便于阅读的统计表,或计算“行内差值”(如 type_A - type_B) 时,常需要将数据变宽
Figure 12. pivot_wider() 基本概念图
场景:所有的指标类型都挤在 size
列里,数值在 amount 列里。我们希望把每种 size
变成单独的一列
# 构造示例数据(长数据:指标混在一列)
pollution <- tibble::tibble(
city = c("New York", "New York", "London", "London"),
size = c("large", "small", "large", "small"),
amount = c(23, 14, 22, 16)
)
pollution # A tibble: 4 × 3
city size amount
<chr> <chr> <dbl>
1 New York large 23
2 New York small 14
3 London large 22
4 London small 16
# 使用 pivot_wider() 将长表转换为宽表
pollution %>%
tidyr::pivot_wider(
names_from = size, # 1) 新的列名来自哪一列?(这里的 large/small 将变成列名)
values_from = amount # 2) 新列的数据来自哪一列?
) # A tibble: 2 × 3
city large small
<chr> <dbl> <dbl>
1 New York 23 14
2 London 22 16
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
数据质量诊断与清洗
“整洁的数据结构”
并不等同于“高质量的数据内容”!
本节聚焦于解决真实数据中常见的缺失、重复、格式混乱与命名不规范等问题
4.1 缺失值处理
NA / is.na() / fill
4.2 重复值剔除
distinct()
4.3 格式修正
stringr / parse_number
4.4 列名规范
janitor::clean_names
💡
提示:数据清洗策略的选择往往取决于业务逻辑而非单纯的代码技巧
操作前请务必做好数据备份或使用新变量存储清洗结果
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
NANA
的识别与应对在 R 语言环境中,缺失值通常以
NA (Not Available)
标识。该标识在运算中具有传递性(propagation):在未加干预的情况下,涉及
NA 的算术运算或统计汇总通常会直接返回
NA,进而阻断正常的数值计算与结果解析。
执行缺失值诊断的基础函数主要包含 is.na() 与
anyNA():前者用于对数据对象执行逐元素的逻辑检验并返回布尔向量,后者用于执行宏观的整体判定(即查验目标对象中是否存在缺失值,并返回单一的 TRUE/FALSE
逻辑值)。
# 构造包含缺失与重复的示例数据
df_dirty <- tibble::tibble(
id = c(1, 2, 3, 3, 4),
value = c(10, NA, 30, 30, 50),
category = c("A", "B", NA, NA, "C")
)
# 1. 全局诊断:数据中是否存在任何缺失值?
anyNA(df_dirty) [1] TRUE
# 2. 定位缺失:计算各变量的缺失数量
df_dirty %>%
summarise(
na_count_value = sum(is.na(value)), # value变量/列中有多少个 NA值
na_count_cat = sum(is.na(category)) # category变量/列中有多少个 NA值
) # A tibble: 1 × 2
na_count_value na_count_cat
<int> <int>
1 1 2
警惕:计算函数的 NA 敏感性
在计算均值、总和等统计量时,NA
会导致结果无法计算。需要使用na.rm = TRUE来规避NA值。
缺失值处理通常有两条基本路径:删除(Deletion) 或 填补(Imputation)。
Figure 13. drop_na() 与 replace_na() 基本概念图
路径 A
丢弃法(Deletion):drop_na()。适用于数据量充足的场景,且缺失模式接近完全随机(Missing Completely At Random,
MCAR)时;在此条件下,删除缺失记录通常不会引入显著的样本偏差。
# A tibble: 2 × 3
id value category
<dbl> <dbl> <chr>
1 1 10 A
2 4 50 C
# A tibble: 4 × 3
id value category
<dbl> <dbl> <chr>
1 1 10 A
2 3 30 <NA>
3 3 30 <NA>
4 4 50 C
路径 B
填补法(Imputation):replace_na() /
fill()。适用于样本较为珍贵的场景,或缺失本身具有特定含义时。
(例如“未填写”可能代表“无”或“0”,需结合数据采集逻辑判断)
# 演示:对比不同的填补策略
df_dirty %>%
mutate(
# 策略 1: 填补为固定值 (如 0)
# [场景] 缺失代表“没有”或“数量为0”
val_zero = replace_na(value, 0),
# 策略 2: 填补为均值 (Mean)
# [场景] 数据分布比较均匀 (注意:必须加 na.rm = TRUE)
val_mean = replace_na(value, mean(value, na.rm = TRUE)),
# 策略 3: 填补为中位数 (Median)
# [场景] 数据有极端值/偏态分布 (中位数比均值更稳健)
val_median = replace_na(value, median(value, na.rm = TRUE)),
# 策略 4: 字符型填补 (固定标签)
# [场景] 缺失代表“未知”或“未录入”
cat_fixed = replace_na(category, "Unknown")
) # A tibble: 5 × 7
id value category val_zero val_mean val_median cat_fixed
<dbl> <dbl> <chr> <dbl> <dbl> <dbl> <chr>
1 1 10 A 10 10 10 A
2 2 NA B 0 30 30 B
3 3 30 <NA> 30 30 30 Unknown
4 3 30 <NA> 30 30 30 Unknown
5 4 50 C 50 50 50 C
进阶思路
当缺失变量与其他变量存在较强相关性时(例如:身高越高,体重通常越重),简单的均值填补可能会削弱变量之间的关系结构。此时可利用“观测完整的变量”作为自变量,对“存在缺失的变量”进行预测,从而实现基于模型的填补。
# 1. 构造示例数据 (假设身高170的人体重缺失)
df <- tibble::tibble(
height = c(160, 165, 170, 175, 180),
weight = c(52, 58, NA, 72, 78)
)
# 2. 建立模型:利用现有数据训练一个线性关系 (体重 ~ 身高)
# na.action = na.exclude 保证预测时自动处理缺失索引
model <- lm(weight ~ height, data = df, na.action = na.exclude)
# 3. 预测并填补
df %>%
mutate(
# 计算预测值 (基于模型和当前的身高)
pred = predict(model, newdata = .),
# 逻辑:如果 weight 是 NA,就填入预测值(pred);否则保留原值
weight_filled = ifelse(is.na(weight), pred, weight)
) # A tibble: 5 × 4
height weight pred weight_filled
<dbl> <dbl> <dbl> <dbl>
1 160 52 51.8 52
2 165 58 58.4 58
3 170 NA 65 65
4 175 72 71.6 72
5 180 78 78.2 78
注意:该方法相较均值填补通常更精细,但预测得到的填补值会完全落在回归拟合线上,从而可能人为高估变量间的相关性。
(原因在于填补过程削弱了原始数据的随机波动与离散性)
进阶思路,了解即可
对于随时间连续变化的数据(如气温、股价、空气质量),相邻观测往往存在时间依赖性。此时可利用“前后时刻的取值”来刻画局部趋势,并据此进行插值或平滑填补。下方示例使用
zoo 包完成时间序列处理(需先安装并加载)。
# 1. 构造模拟数据:10 天的空气质量指数 (AQI)
# 特征:数据有上升趋势,但中间第 4-5 天连续缺失,第 9 天单点缺失
ts_df <- tibble::tibble( # 时间序列数据
day = 1:10,
aqi = c(50, 52, 55, NA, NA, 68, 72, 75, NA, 82)
)
# 2. 核心操作:利用 zoo 包进行时序处理
# 若未安装,需运行 install.packages("zoo")
ts_df %>%
mutate(
# 方法 A: 线性插值 (Linear Interpolation)
# [原理] 连接缺失点前后的数值画一条直线 (局部线性回归)
# [结果] 55 -> 59.3 -> 63.6 -> 68 (平滑过渡)
aqi_interp = zoo::na.approx(aqi, na.rm = FALSE),
# 方法 B: 移动窗口均值 (Moving Average)
# [原理] 计算前后窗口(如3天)的平均值作为填补参考
# 这里先计算一个“3天滑动平均线”,再用它来填补缺失位
roll_mean = zoo::rollapply(aqi, width = 3, FUN = mean,
na.rm = TRUE, partial = TRUE, fill = NA),
aqi_ma = coalesce(aqi, roll_mean) # 仅在 aqi 为 NA 时,使用 roll_mean 填补
) # A tibble: 10 × 5
day aqi aqi_interp roll_mean aqi_ma
<int> <dbl> <dbl> <dbl> <dbl>
1 1 50 50 51 50
2 2 52 52 52.3 52
3 3 55 55 53.5 55
4 4 NA 59.3 55 55
5 5 NA 63.7 68 68
6 6 68 68 70 68
7 7 72 72 71.7 72
8 8 75 75 73.5 75
9 9 NA 78.5 78.5 78.5
10 10 82 82 82 82
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
distinct()distinct()数据采集偏误、多源表横向关联不当或数据集成冗余,常会导致数据集中出现重复观测记录(duplicate records)。
统计风险
重复数据会引发样本量的偏大估计(sample inflation),进而导致统计推断中的标准误(SE)被低估,增加一类错误(假阳性)的发生风险。因此,在执行统计描述或模型构建前,需明确分析粒度并进行唯一性校验。
在执行删除前,通常建议先量化重复记录的规模(重复行数/重复比例),以评估数据质量问题的严重程度,并定位可能的产生环节。
# 1. 构造示例数据
# 特征:第1行与第3行“完全重复”;第4行 id 重复但数值不同(部分重复)
df_dup <- tibble::tibble(
id = c("P01", "P02", "P01", "P01"),
date = c("2023-10-01", "2023-10-02", "2023-10-01", "2023-10-05"),
value = c(100, 200, 100, 150)
)
# 2. 诊断:有多少行是完全重复的?
# 逻辑:总行数 - 去重后的行数 nrow() 行数查询 输出是数字
n_dupes <- nrow(df_dup) - nrow(distinct(df_dup))
# 打印诊断结果
paste("发现完全重复的行数:", n_dupes) [1] "发现完全重复的行数: 1"
提示:若需要定位“具体哪些行发生重复”,可使用
group_by(所有变量) %>% filter(n() > 1)(找出完全重复的记录),或使用janitor::get_dupes(df_dup)(需提前安装janitor包)。
在 tidyverse 环境中,可使用 distinct()
进行重复记录的剔除(保留唯一记录)。
Figure 14. distinct() 基本概念图
根据“重复”的定义不同,distinct()
可对应两类常见处理策略:
策略 A
完全去重
适用于整行所有信息完全一致的冗余记录
# A tibble: 3 × 3
id date value
<chr> <chr> <dbl>
1 P01 2023-10-01 100
2 P02 2023-10-02 200
3 P01 2023-10-05 150
策略 B
按关键变量去重
适用于“同一主体存在多条记录,但当前分析要求每个主体唯一”的场景
# 逻辑:只根据 id 去重,保留第一次出现的行
# 参数 .keep_all = TRUE 至关重要,否则会丢弃除 id 外的其他列
df_dup %>%
distinct(id, .keep_all = TRUE) # A tibble: 2 × 3
id date value
<chr> <chr> <dbl>
1 P01 2023-10-01 100
2 P02 2023-10-02 200
警惕:隐性数据丢失
使用策略 B(.keep_all = TRUE)
时,distinct()
默认保留关键变量下第一条出现的记录。若数据原始顺序不确定,最终被保留的记录可能具有随机性,从而造成“隐性”的信息丢失。
建议:在按关键变量去重前,务必先使用
arrange()对数据进行明确排序(例如优先保留最新记录或最高质量记录)。
# A tibble: 2 × 3
id date value
<chr> <chr> <dbl>
1 P01 2023-10-01 100
2 P02 2023-10-02 200
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
stringr在多源城市数据中,非结构化文本常包含字符噪音 (如多余空格、大小写不统一);此外,数值型字段若包含特殊符号 (如货币符号、百分号),常被错误识别为字符型变量。此类格式性偏误会导致分组计算失效、关联匹配失败或算术运算报错,需在分析前执行规范化处理。
stringr常用
stringr
包提供了处理字符串的标准化工具集。针对文本噪音,常用的清洗步骤包括去除冗余空格与统一大小写规范。
去除首尾空格:str_trim()
用于清除字符串起始与末尾的不可见空格 (防止因空格导致完全相同的地名或标签无法匹配)。
转换字符大小写:str_to_lower() /
str_to_upper()
用于将文本统一转换为全小写或全大写 (确保如 “London”
与 “london” 在分组统计时被视作同一类别)。
# 1. 构造脏数据
# 问题:性别大小写不一、含空格;收入含符号,无法计算
df_text <- tibble::tibble(
name = c("Alice", " Bob", "Charlie "),
gender = c("Female", "male", "Male "),
income = c("$2,500", "3000", "$4,200")
)
# 2. 清洗前:直接分组计数,会发现 "Male " 和 "male" 被视作两类
df_text %>% count(gender) # A tibble: 3 × 2
gender n
<chr> <int>
1 "Female" 1
2 "Male " 1
3 "male" 1
# 3. 清洗操作
df_clean_text <- df_text %>%
mutate(
# 去除首尾空白字符 (Trim Whitespace)
name = str_trim(name),
# 统一转换为小写 (或 str_to_upper 转大写)
# [目的] 消除 "Male"/"male" 的差异
gender = str_to_lower(str_trim(gender))
)
# 验证清洗效果
df_clean_text %>% count(gender) # A tibble: 2 × 2
gender n
<chr> <int>
1 female 1
2 male 2
parse_number()常用
当数值列包含非数字字符(如
$、,、%)时,直接使用
as.numeric() 往往会产生大量 NA(强制转换失败)。
此时可使用 readr::parse_number()
提取其中可解析的数值部分,从而完成更稳健的数值转换。
df_clean_text %>%
mutate(
# 错误示范:as.numeric("$2,500") 会变成 NA
# 正确做法:提取数值核心
income_num = parse_number(income)
) # A tibble: 3 × 4
name gender income income_num
<chr> <chr> <chr> <dbl>
1 Alice female $2,500 2500
2 Bob male 3000 3000
3 Charlie male $4,200 4200
parse_number() 的原理
parse_number() 会自动忽略非数字字符(如货币符号、千分位逗号、空格等),仅提取字符串中第一个可解析的数字序列。
(例:parse_number("费用:$12,345") → 12345)
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
janitorjanitor::clean_names()可选辅助工具
原始数据的变量名通常存在空格、括号或特殊字符 (如
Customer Name 或 GDP (%))。在 R
语言环境中,引用非标准列名需频繁调用反引号 (`),这增加了代码维护成本与语法错误的潜在风险。
可利用 janitor
包提供的自动化工具对变量名进行规范化清洗。其中,clean_names()
函数能将原始变量名统一转换为小写且以底划线分隔的 snake_case
命名规范,从而提升后续代码编写与批量处理的效率。
一键清洗列名:janitor::clean_names()
janitor 包可以将所有列名统一转换为
snake_case(小写_下划线),这是 R 语言的最佳命名规范
# 构造列名不规范的数据
df_bad_names <- tibble::tibble(
`First Name` = "John",
`Annual Income ($)` = 50000,
`% Change` = 0.05
)
# 一键规范化
# 需先安装:install.packages("janitor")
df_bad_names %>%
janitor::clean_names() %>%
names() # 查看清洗后的列名 [1] "first_name" "annual_income" "percent_change"
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
重要
探索性数据分析 (Exploratory Data Analysis,简称 EDA) 由统计学家 John W. Tukey 系统提出。EDA 不宜仅被理解为若干可视化与描述统计方法的简单组合,更应被视为一种以理解数据本身为优先的分析理念。
作为正式统计推断 (如回归分析、假设检验) 与机器学习建模之前的重要环节,EDA 的核心任务是对原始数据进行系统性诊断:识别数据质量问题,刻画分布与变异特征,检视变量之间的关系,并定位异常值与潜在偏差。其方法论强调通过归纳性的观察与比较,发现数据中的规律、异常与不确定性,从而为后续研究提供更稳妥的假设基础与建模依据。
高质量的 EDA 是保障研究结论稳健性的重要基础,其价值主要体现在以下三个方面:
数据质量诊断:
用于检验前期数据处理的有效性,识别潜在的异常值、极端分布以及可能影响推断的缺失、重复或编码错误,从而提升分析样本的可信度。
单变量分布刻画:
系统描述变量的中心趋势 (如均值、中位数)、离散程度 (如标准差、四分位距)与分布形态 (如偏态、峰态),建立对研究对象属性的基础认识。
多变量关系探索:
初步识别变量之间的共变关系与潜在相关结构 (如线性或非线性趋势),为后续统计建模中的变量选择、函数形式设定与模型诊断提供依据。
在社会科学量化研究中,描述性统计 (Descriptive Statistics) 往往作为实证分析部分的起点,是支撑研究可信度的重要环节。
其主要功能包括:
评估样本代表性:
检视关键构成指标 (如性别、年龄、地区)
是否存在明显的抽样偏差或结构性缺口。
验证数据逻辑性:
核查核心变量的统计特征
(如均值、标准差与取值范围)
是否符合常识与理论预期,并识别不合理取值。
支撑处理决策:
为变量变换 (如对数变换)、样本筛选 (如处理离群值)
与模型设定提供必要的经验依据。
在量化研究实践中,EDA 的实施通常始于对数据的数值化概括。本节将聚焦于统计摘要 (Summary Statistics) 与分组汇总 (Grouped Summaries),旨在通过一组可复核的统计指标,建立对数据特征的基础认知。
需要说明的是,EDA 并不限于单变量或分组统计,也包括对变量之间关系的初步检视,例如相关性分析、散点关系观察与潜在共线结构识别。但为保持本节重点清晰,此处暂以描述性统计为主。
注:数据可视化是 EDA 的另一大核心支柱。鉴于其知识体系的独立性与丰富性,将在 下一章 单独进行系统讲授。
一个较为标准的 EDA 迭代循环通常包含以下三个步骤:
接下来,将介绍如何利用 tidyverse 生态中的现代化工具
(特别是 skimr
包),高效产出更接近学术写作规范的描述性统计与汇总结果。
探索性数据分析核心工具
本节围绕探索性数据分析的基础工具展开,主要包括全貌扫描、描述统计、分组汇总与相关性分析四个部分,用于建立对数据结构与变量关系的初步认识。
5.1.1 全貌扫描
skimr::skim()
5.1.2 描述统计
summarise()
5.1.3 分组汇总
group_by()
5.1.4 相关分析
cor() / cor.test()
💡 提示:本节主要聚焦于数值层面的概括与比较;与后续章节中的可视化方法结合,能够形成更完整的 EDA 分析框架。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
summary()
到 skimr在数据分析的初始阶段,建立对数据集的宏观统计认知是关键的起点。通常需要快速把握变量的集中趋势、离散程度与完整性(缺失情况),为后续的数据清洗、变量处理与建模决策提供依据。
1. 基础方案:Base R 的 summary()
在 Base R 中,summary()
是最通用的概览函数之一。对数值型变量,它提供经典的“六数概括”(Six-Number Summary),即:
最小值(Min.)、第一四分位数(1st
Qu.)、中位数(Median)、均值(Mean)、第三四分位数(3rd
Qu.)与最大值(Max.)。
# 以 gapminder 数据为例
# pop (人口) 可能会使用科学计数法显示,想要规避可以使用 options(scipen = 9999)
options(scipen = 9999)
summary(gapminder) country continent year lifeExp
Afghanistan: 12 Africa :624 Min. :1952 Min. :23.60
Albania : 12 Americas:300 1st Qu.:1966 1st Qu.:48.20
Algeria : 12 Asia :396 Median :1980 Median :60.71
Angola : 12 Europe :360 Mean :1980 Mean :59.47
Argentina : 12 Oceania : 24 3rd Qu.:1993 3rd Qu.:70.85
Australia : 12 Max. :2007 Max. :82.60
(Other) :1632
pop gdpPercap
Min. : 60011 Min. : 241.2
1st Qu.: 2793664 1st Qu.: 1202.1
Median : 7023596 Median : 3531.8
Mean : 29601212 Mean : 7215.3
3rd Qu.: 19585222 3rd Qu.: 9325.5
Max. :1318683096 Max. :113523.1
局限性:尽管
summary()能提供基础统计量,但其纯文本输出较为单一,难以直观呈现分布形态(如偏态、长尾或双峰);同时,对缺失值NA的呈现也较为有限,通常缺乏占比层面的汇总描述。
2. 进阶方案:Tidyverse 生态的
skimr::skim()
为弥补基础概览的不足,可引入 skimr 包。作为面向
tidyverse 工作流的 EDA 工具,skim()
能生成结构化的统计报告,并提供迷你可视化提示,以更直观地呈现变量分布与缺失情况。
skimr 并非 tidyverse
的核心包,需单独安装并加载。若尚未安装,可先运行
install.packages("skimr")。
# 以 gapminder 数据为例
# skimr::focus()的部分可以根据实际需要调整!
gapminder %>%
skimr::skim() %>% # 这一步会产生很多输出,可自行阅读,下方只展示了部分
skimr::focus(n_missing, numeric.mean, numeric.sd, numeric.hist)| Name | Piped data |
| Number of rows | 1704 |
| Number of columns | 6 |
| _______________________ | |
| Column type frequency: | |
| factor | 2 |
| numeric | 4 |
| ________________________ | |
| Group variables | None |
Variable type: factor
| skim_variable | n_missing |
|---|---|
| country | 0 |
| continent | 0 |
Variable type: numeric
| skim_variable | n_missing | mean | sd | hist |
|---|---|---|---|---|
| year | 0 | 1979.50 | 17.27 | ▇▅▅▅▇ |
| lifeExp | 0 | 59.47 | 12.92 | ▁▆▇▇▇ |
| pop | 0 | 29601212.32 | 106157896.74 | ▇▁▁▁▁ |
| gdpPercap | 0 | 7215.33 | 9857.45 | ▇▁▁▁▁ |
skim() 的优势:
factor、numeric)自动分块展示,便于快速定位不同类型变量的统计信息。n_missing(缺失数) 与 complete_rate(完整率)
量化数据的完整性,从而为数据质量评估提供直接依据。numeric
模块中,hist(迷你直方图)
可在不单独作图的情况下,对分布形态提供直观提示。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
在 3.2.7(分组汇总)中已初步使用
summarise()。与 skim()
的“全维度概览”不同,summarise()
允许根据具体研究问题对统计量进行定制化组合:围绕研究关注的变量与分组结构,计算更聚焦、更可解释的核心指标,从而生成更精简的描述性统计表。
1. 统计描述的核心指标体系
在实证研究中,连续变量的描述通常围绕集中趋势与离散程度展开。为更准确地选择统计量,需要理解其数学定义与适用场景。
一、集中趋势(Central Tendency):衡量数据分布的中心位置
均值(Mean,符号
\(\mu\),读作 mu)
\[\mu = \frac{1}{n}\sum_{i=1}^{n}
x_i\]
特性
使用全部样本信息,但对异常值(Outliers)敏感;更适用于近似对称分布。
中位数(Median,\(M\))
数据排序后的中心值(\(50\%\) 分位数)。
特性 具有鲁棒性(Robustness),能有效抵御极值干扰;对偏态分布(如收入)尤为常用。
二、离散程度(Dispersion):度量数据的变异度或波动范围
极差(Range,\(R\))
\[R = x_{\max} - x_{\min}\]
特性 由最小值(Min)与最大值(Max)决定,对异常值非常敏感。
标准差(Standard
Deviation,符号 \(\sigma\),读作
sigma)
\[\sigma = \sqrt{\frac{1}{n} \sum_{i=1}^{n}
(x_i - \mu)^2}\]
特性 量化数据点偏离均值的平均距离;\(\sigma\) 越大,分布越离散。
注:样本标准差常使用 \(n-1\)
作分母;此处给出总体形式便于理解
四分位距(Interquartile Range,\(IQR\))
\[IQR = Q_3 - Q_1\]
特性 聚焦中间 50% 的数据范围(\(Q_3\) 为 75%
分位数,\(Q_1\) 为 25%
分位数),对极端值更稳健。
2. 代码实现
利用 summarise() 配合上述统计函数,我们可以精准提取
gapminder 数据集中
lifeExp(预期寿命)的关键特征。
# 定制化计算:对比均值与中位数,评估数据偏态
gapminder %>%
summarise(
# 1. 集中趋势
mean_life = mean(lifeExp), # 均值
median_life = median(lifeExp), # 中位数
# 2. 离散程度
sd_life = sd(lifeExp), # 标准差
iqr_life = IQR(lifeExp), # 四分位距
# 3. 极值范围 (Range)
min_life = min(lifeExp),
max_life = max(lifeExp),
# 4. 任意分位数 (如 90% 分位数)
p90_life = quantile(lifeExp, 0.90)
) # A tibble: 1 × 7
mean_life median_life sd_life iqr_life min_life max_life p90_life
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 59.5 60.7 12.9 22.6 23.6 82.6 75.1
解读:若
mean与median差异显著(均值远大于中位数),通常暗示数据存在右偏分布
之前介绍过,基于 R 的运算逻辑,缺失值 (NA)
具有代数传染性:若输入向量中包含任意
NA,默认的聚合运算将直接返回 NA。
为确保统计结果的有效性,在执行聚合函数(如 mean,
sd, sum,
max)时,必须显式设定参数
na.rm = TRUE (NA
Remove),即在计算前先行剔除缺失样本。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
在 EDA 阶段,仅依赖全局统计量往往会掩盖数据内部的重要差异,这类现象可理解为“掩盖效应”。为在实证层面检视研究假设,分析视角需要从总体概览进一步转向分组比较。
分析逻辑:从全局(Global) 到条件(Conditional)
科学研究的核心之一在于比较(Comparison)。借助 group_by() 与
summarise()
的协同工作,可计算不同组别下的条件统计量,从而揭示数据的异质性(Heterogeneity)。
全局视角:“所有观测的平均寿命是多少?”
分组视角:“不同大洲、不同年份的平均寿命是否存在系统性差异?”
实战示例 1:构建“基线表”
常见
在量化研究中,常需要制作一张 Table 1(基线表) 用于呈现不同组别的特征差异。为避免将不同年份的数据混杂(时间效应),此处选取 2007 年的数据作为截面进行示例分析。
以下代码展示了如何快速生成分地区的统计摘要:
# 探究:2007年不同大洲的预期寿命是否存在结构性差异?
gapminder %>%
# 1. [关键步骤] 锁定时间截面,排除时间维度的干扰
filter(year == 2007) %>%
# 2. 设定比较维度
group_by(continent) %>%
# 3. 计算核心指标
summarise(
n_obs = n(), # 样本量 (N)
mean_life = mean(lifeExp, na.rm=TRUE), # 均值
sd_life = sd(lifeExp, na.rm=TRUE), # 标准差
# [进阶技巧] 学术格式化:构建符合期刊规范的 "Mean (SD)" 格式
# sprintf 是字符串格式化函数:
# "%.1f" 含义:保留1位小数的浮点数 (Float)
stats_fmt = sprintf("%.1f (%.1f)", mean_life, sd_life)
) %>%
# 4. 按均值降序排列,并仅保留汇报列
arrange(desc(mean_life)) %>%
select(continent, n_obs, stats_fmt) # A tibble: 5 × 3
continent n_obs stats_fmt
<fct> <int> <chr>
1 Oceania 2 80.7 (0.7)
2 Europe 30 77.6 (3.0)
3 Americas 25 73.6 (4.4)
4 Asia 33 70.7 (8.0)
5 Africa 52 54.8 (9.6)
分析:基于 2007 年的截面数据,
Oceania的平均寿命远高于Africa,且Africa组内的标准差较大(说明该地区内部国家间的发展差异最为显著)
实战示例 2:组内份额分析
进阶
在经济或人口学研究中,除了关注总体的均值差异,我们还经常需要考察数据的内部结构,即识别出哪些个体在群体中占据主导地位(Dominance)
与 summarise 的“降维”逻辑不同,这里我们需要保留微观个体 (如国家),并计算其在所属群体 (如大洲)
中的相对份额(Share)
以下代码展示了如何计算 2007 年各国的 GDP 占所在大洲总量的百分比:
# 探究:2007年,各国内部经济体量在所属大洲中的“权重”
gapminder %>%
filter(year == 2007) %>%
# 1. 预计算:算出每个国家的 GDP 总量 (单位:美元)
mutate(gdp_total = gdpPercap * pop) %>%
# 2. 分组:按大洲划分,为下一步“组内计算”做准备
group_by(continent) %>%
# 3. 组内占比计算 (Window Function)
# 逻辑:个体的 GDP / 该组(大洲)所有国家 GDP 之和
mutate(
gdp_share = gdp_total / sum(gdp_total),
# [格式构建] 目标格式:GDP总量 (占比%)
# 步骤 A: 将 GDP 换算为 "十亿 (Billion)" 单位,保留1位小数
gdp_val_fmt = round(gdp_total / 1e9, 1),
# 步骤 B: 使用 scales 包将小数转化为百分比字符串 (如 0.25 -> "25.0%")
share_pct_fmt = scales::percent(gdp_share, accuracy = 0.1),
# 步骤 C: 拼接字符串 -> "300.5B (25.0%)"
stats_fmt = sprintf("%sB (%s)", gdp_val_fmt, share_pct_fmt)
) %>%
# 4. 排序:按大洲和份额降序排列
# 这一步至关重要,决定了下一步截取的是哪几个国家
arrange(continent, desc(gdp_share)) %>%
# 5. 截取 Top N (Slicing)
# slice_head: 专门用于选取顶部的行
# n = 3: 因为数据处于 group_by 状态,所以是提取"每个大洲的前3名"
slice_head(n = 3) %>%
# 6. 清理:仅保留最终的汇报列
select(continent, country, stats_fmt) # A tibble: 14 × 3
# Groups: continent [5]
continent country stats_fmt
<fct> <fct> <chr>
1 Africa Egypt 448B (18.8%)
2 Africa South Africa 407.8B (17.1%)
3 Africa Nigeria 271.9B (11.4%)
4 Americas United States 12934.5B (66.6%)
5 Americas Brazil 1722.6B (8.9%)
6 Americas Mexico 1302B (6.7%)
7 Asia China 6539.5B (31.6%)
8 Asia Japan 4035.1B (19.5%)
9 Asia India 2722.9B (13.1%)
10 Europe Germany 2650.9B (17.9%)
11 Europe United Kingdom 2018B (13.6%)
12 Europe France 1861.2B (12.6%)
13 Oceania Australia 703.7B (87.2%)
14 Oceania New Zealand 103.7B (12.8%)
分析:数据揭示了极具差异的地缘经济结构:
- 单极主导:
Oceania和Americas呈现出典型的单极结构。澳大利亚 (87.2%) 和美国 (66.6%) 在各自区域内拥有绝对的经济统治力。- 多极均衡:相比之下,
Europe呈现出更为均衡的格局,德 (17.9%)、英 (13.6%)、法 (12.6%) 三强并立,无单一国家占据压倒性优势。- 亚洲格局:2007 年的
Asia已显现出中国 (31.6%) 的领先地位,但与日本 (19.5%) 和印度 (13.1%) 共同构成了梯度明显的“三强”态势。
注意
在构建基线特征表(Table 1)时,不同类型变量通常遵循不同的汇报格式:
连续变量(continuous)(如年龄、收入):通常汇报为 Mean
(SD)。
(代码实现:mean() 配合
sd())
分类变量(categorical)(如性别、地区):通常汇报为 Count
(%)。
(代码实现:n() 配合
n() / sum(n()))
实战示例 3:控制变量的EDA
EDA 不仅限于单一维度的比较。通过同时引入多个分组变量(如 continent +
year),我们要实现在控制 (Control) 某一变量(如时间)的情况下,观察另一变量(如地区)的净差异。
# 进阶探究:控制时间变量后,观察各大洲的经济增长趋势
gapminder %>%
# 1. 筛选关键时间点:仅选取首尾年份进行 "前后测" 对比
filter(year %in% c(1952, 2007)) %>%
# 2. 多维分组:同时按大洲和年份切片
group_by(continent, year) %>%
# 3. 计算统计量
summarise(
mean_gdp = mean(gdpPercap, na.rm=TRUE),
.groups = "drop" # [建议] 显式解除分组,避免后续操作报错
) %>%
# 4. 宽表重塑:将年份转为列,直观展示"增长幅度"
pivot_wider(
names_from = year,
values_from = mean_gdp,
names_prefix = "Year_" # [技巧] 添加前缀 prefix,避免列名变成纯数字
) # A tibble: 5 × 3
continent Year_1952 Year_2007
<fct> <dbl> <dbl>
1 Africa 1253. 3089.
2 Americas 4079. 11003.
3 Asia 5195. 12473.
4 Europe 5661. 25054.
5 Oceania 10298. 29810.
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
在 EDA 阶段,除对单变量分布进行概括外,还需要进一步考察变量之间是否存在稳定的共变关系。相关分析 (Correlation Analysis) 的核心作用,正是在于用一个可量化的指标,初步描述两个变量之间的关联方向与强弱。
需要注意的是,相关并不等于因果。相关分析只能回答“两个变量是否一起变化,以及变化是否同向或反向”,并不能单独说明“一个变量是否导致另一个变量变化”。因此,在研究流程中,相关分析更适合作为关系探索与建模前诊断的一部分。
1. 两类常用相关系数
在实证分析中,最常见的相关系数包括 Pearson 与
Spearman
两类。它们都用于描述变量之间的关联程度,但适用前提并不完全相同。
一、Pearson 相关系数 (Pearson’s
\(r\))
用于衡量两个连续变量之间的线性相关程度。其常见表达式为:
\[ r = \frac{\sum_{i=1}^{n}(x_i-\bar{x})(y_i-\bar{y})} {\sqrt{\sum_{i=1}^{n}(x_i-\bar{x})^2}\sqrt{\sum_{i=1}^{n}(y_i-\bar{y})^2}} \]
适用场景:变量近似连续、关系大致呈线性,且不希望完全忽略原始数值差异。
二、Spearman 等级相关系数 (Spearman’s \(\rho\))
用于衡量两个变量之间的单调关系,其本质是对变量先进行排序,再计算秩之间的相关。若无并列秩,其简化形式可写为:
\[ \rho = 1 - \frac{6\sum d_i^2}{n(n^2-1)} \]
其中,\(d_i\) 表示第 \(i\) 个观测在两个变量中的秩差。
适用场景:当变量分布偏态明显、含有极端值,或关系更接近单调变化而非严格线性时,Spearman
往往更稳健。
如何初步解读相关系数?
在课程学习阶段,可先作以下经验性理解:
注意:这些阈值仅用于初步描述,具体解释仍需结合研究问题、样本规模与变量性质。
2. 基础实现:cor() 与
cor.test()
在 R 中,cor() 用于计算相关系数,cor.test()
则可进一步提供检验结果 (如 p
值与置信区间)。为避免时间维度对关系判断造成干扰,以下示例仍以
2007 年 的 gapminder 截面数据为例。
library(tidyverse)
library(gapminder)
# 选取 2007 年截面,并构造 log GDP 变量
gap_2007 <- gapminder %>%
filter(year == 2007) %>%
mutate(log_gdpPercap = log10(gdpPercap))
# 计算 Pearson 与 Spearman 相关系数
gap_2007 %>%
summarise(
pearson_r = cor(log_gdpPercap, lifeExp, method = "pearson"),
spearman_r = cor(log_gdpPercap, lifeExp, method = "spearman")
) # A tibble: 1 × 2
pearson_r spearman_r
<dbl> <dbl>
1 0.809 0.857
Pearson's product-moment correlation
data: log_gdpPercap and lifeExp
t = 16.283, df = 140, p-value < 0.00000000000000022
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.7433069 0.8592085
sample estimates:
cor
0.8089803
Spearman's rank correlation rho
data: log_gdpPercap and lifeExp
S = 68434, p-value < 0.00000000000000022
alternative hypothesis: true rho is not equal to 0
sample estimates:
rho
0.8565899
说明:这里对
gdpPercap进行对数化处理,是因为其分布通常具有明显的右偏特征。对数变换后,更有助于观察其与lifeExp之间的总体关系。
3. 如何书写相关分析结果?
在研究写作中,相关分析通常需要同时交代方向、强度与显著性。
根据当前结果,log(gdpPercap) 与 lifeExp
的相关系数分别为:Pearson = 0.809,Spearman = 0.857。这表明两者之间存在较强的正相关关系,且这一关系在线性层面与等级排序层面都较为稳定。
例如,可写为:
在 2007 年的
gapminder截面数据中,log(gdpPercap)与lifeExp呈现较强的正相关。Pearson相关系数为 0.809,说明两者在线性层面存在明显的正向关系;Spearman相关系数为 0.857,且结果与Pearson相近,说明这一关系在排序意义上同样稳定。
若需要更精简的学术表达,也可写为:
lifeExpwas positively correlated withlog(gdpPercap)(Pearson’s \(r\) = 0.809; Spearman’s \(\rho\) = 0.857).
相关不等于因果:
即使两个变量高度相关,也不能直接得出因果结论。
显著不等于重要:
在样本量较大时,即使相关系数不高,也可能得到很小的 p
值。解释时仍应优先关注效应大小。
零相关不等于无关系:
当变量之间存在明显的非线性结构时,Pearson 可能接近
0,但这并不意味着两者完全无关。
4. 多变量情境:相关矩阵与 corrplot
当研究涉及多个数值变量时,往往需要先计算一个相关矩阵,以观察整体关系结构。此时可先使用
cor() 得到矩阵,再根据需要借助 corrplot
包进行初步展示。
# 构造数值变量子集
gap_num <- gap_2007 %>%
select(lifeExp, pop, gdpPercap, log_gdpPercap)
# 计算相关矩阵
cor_mat <- cor(gap_num, method = "pearson")
cor_mat lifeExp pop gdpPercap log_gdpPercap
lifeExp 1.00000000 0.04755312 0.6786624 0.80898025
pop 0.04755312 1.00000000 -0.0556756 -0.02353029
gdpPercap 0.67866240 -0.05567560 1.0000000 0.87510881
log_gdpPercap 0.80898025 -0.02353029 0.8751088 1.00000000
说明:
corrplot是相关矩阵展示中较常见的工具,但由于本章重点仍在数值摘要与关系识别,这里仅作方法引介。其更完整的图形表达与美化方式,可结合后续的数据可视化章节进一步理解。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
实战目标
本节练习将围绕真实的社会调查数据 (Survey Data) 展开。所使用的数据为
tidyverse 内置的美国综合社会调查 (General Social Survey, GSS) 示例数据
gss_cat。
通过本节练习,将进一步巩固以下几项核心技能:
全貌扫描 (Overview
Scan):
使用 skimr
快速把握变量分布与缺失情况。
分组洞察 (Grouped Summaries):
使用
group_by() 与 summarise()
比较不同社会群体之间的差异。
份额分析 (Share Analysis):
结合 mutate()
计算特定群体在总体中的相对占比。
结果解读 (Result Interpretation):
学习如何基于
tibble 输出提炼并表述基本的社会学发现。
本任务属于一个按步骤复现的练习。分析目标是考察:婚姻状况 (
marital) 是否与个体的日均看电视时长 (tvhours)存在差异。
请在你的 R Project 中新建一个 R Markdown
文档:
Lab2_Social_Survey.Rmdtidyverse 与 skimr;gss_cat 数据集 (无需额外下载;加载 tidyverse
后即可直接使用);marital)下,人们每日看电视时长 (tvhours)的平均水平;tvhours 中包含缺失值
(NA),计算时需考虑缺失值处理。Step 1
新建文档:创建新的 Rmd 文件,保留
YAML 头部,删除默认正文内容。
Step 2
复制模板:将下方提供的代码块完整复制到文档中。
Step 3
运行与观察:点击 Knit
生成输出结果。重点阅读 summarise()
所返回的结果表,并尝试解释不同婚姻状态之间的差异。
---
title: "GSS调查报告:婚姻与媒介消费"
author: "【你的名字】"
date: "`r Sys.Date()`"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
library(tidyverse)
library(skimr)
```
```{r}
# 1. 查看数据结构
# gss_cat 是 tidyverse 自带的数据集
glimpse(gss_cat)
# 2. 重点检查关键变量
# skim() 能让我们直观看到 tvhours 有多少 NA (n_missing)
gss_cat %>%
select(marital, age, tvhours) %>%
skim()
```
```{r}
# 计算统计量
result_table <- gss_cat %>%
# 1. 分组:按婚姻状况
group_by(marital) %>%
# 2. 统计汇总
summarise(
n_sample = n(), # 样本量
mean_tv = mean(tvhours, na.rm=TRUE), # 平均看电视时长 (注意 na.rm)
sd_tv = sd(tvhours, na.rm=TRUE) # 标准差
) %>%
# 3. 整理:保留1位小数,并按时长降序排列
mutate(
mean_tv = round(mean_tv, 1),
sd_tv = round(sd_tv, 1)
) %>%
arrange(desc(mean_tv))
# 直接打印结果 (Tibble)
result_table
```
>**简要分析**: 观察结果可以发现:
>
>丧偶 (`Widowed`) 群体的日均看电视时间最长(*3.9小时*),这可能与该群体平均年龄较大、居家时间较多有关。
>
>已婚 (`Married`) 群体的电视消费时间最短(2.7小时),显著低于 从不结婚 (`Never married`) 和 离婚 (`Divorced`) 群体。这可能反映了家庭互动或育儿责任挤占了个人媒介消费时间。这是一个不提供现成代码的练习。
你需要综合运用本章所学的分组统计、缺失值处理与组内计算技能,独立完成分析任务。
课题方向:
考察美国不同宗教信仰群体 (relig) 的年龄结构
(age)。
建议文件名:
Lab2_Religion_Age.Rmd
研究背景:
在人口社会学研究中,宗教信仰不仅体现个体的价值取向,也常与人口结构特征相关联。本练习假设,不同宗教群体在年龄结构上可能存在一定程度的异质性 (Structural
Heterogeneity)。例如,部分传统宗教群体可能呈现较明显的老龄化特征,而无信仰群体则可能相对更年轻。请使用
gss_cat 数据,并借助 EDA
方法对这一构想进行初步检验。
你的 R Markdown 报告需完整体现以下分析流程 (请自行编写代码):
1. 数据筛选与清洗 (Data
Preparation)
age
数据不包含缺失值 (NA)。
2. 变量重构 (Variable
Construction)
基于 age 构建新的分类变量
age_group。
分组标准:
结合 GSS 样本特征
(如 Min = 18,
Median = 47) 与常见人口学划分,可采用以下方案:
3. 多维结构分析 (Structural
Analysis)
核心要求:同时按宗教 (relig)与代际组别
(age_group)进行统计。
结果表中需包含以下指标:
4. 最终产出 (Output)
knit 输出整洁的
tibble 表格即可,无需额外使用 kable
等方式进行美化。gss_cat 数据包含 2000–2014 年
的时间信息。为减少不同群体原始数值差异带来的影响,本练习引入定基指数
(Index Number) 的思路,将 2000
年 设定为基期 (Base Year =
100),用于观察后续年份的相对变化幅度。
趋势分析:种族媒介消费指数
若将 2000 年的水平设为 100,那么 2008、2010 与 2014 年各群体的指标是上升至 120,还是下降至 80?
1. 分组与聚合
race 与 year 分组,计算各族群各年份的平均电视观看时长 (需使用变量 tvhours),结果命名为
mean_tv。2. 计算定基指数 (Calculate Index
in Long Format)
group_by(race) 与
mutate() 创建新变量
tv_index。\[ Index = \frac{\text{当年均值 }}{\text{2000 年均值}} \times 100 \]
mean_tv / mean_tv[year == 2000] * 100mean_tv / first(mean_tv) * 1003. 宽表展示
将计算后的 tv_index 转换为宽表。
raceyeartv_index (建议使用 round() 不保留小数)4. 结果解读 请重点观察 2014 年
对应的指数列,并回答以下问题:
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)