本次实验50分钟,完成实验后你将掌握:
Rpractice,理解R项目文件管理规范%>%基础# 安装实验需要的全部R包;eval=FALSE代表这份代码不会自动运行
# tidyverse:包含dplyr、ggplot2等,用于数据处理和绘图
# lubridate:专门处理日期时间
# readxl:读取Excel文件
# writexl:写入Excel文件
# knitr:制作美观表格
install.packages(c("tidyverse","lubridate","readxl","writexl","knitr")) #使用国内镜像,下载更快
【重要】我们第1次实验已经建立总工程 Rpractice,后续所有章节实验全部复用这个工程,不需要重复新建Project。
Rpractice固定目录结构:
code/:存放每一章R脚本(本章R脚本保存到这里)data/:存放所有章节原始数据、清洗后数据文件output/:存放所有章节导出的图表、报告
✅ 课堂操作步骤:
Rpractice.Rproj,双击打开RStudiocode/
文件夹,命名chapter3_data_process.Rdata/xxx.xlsx,图片自动输出到output/数据分为一手数据(直接获取)和二手数据(复用已有公开数据)。
一手数据:研究者为当前研究专门收集的数据。
注意:一手数据拿到之后,原始问卷导出的数据,几乎都是“脏数据”,必须经过数据清洗。
二手数据:他人已经收集完成,用于二次开发分析。
⚠️二手数据注意点:原始收集目的不一定匹配你的研究,需要校验适用性、准确性、时效性、合规伦理。
Tidy Data三条基本原则
- ✅每个变量独占一列
- ✅每条观测独占一行
- ✅每种观测单元构成独立表格
很多同学会问:只要数据能放进R里,脏数据不也能分析吗? 从R代码编写的角度,整洁数据可以大幅降低写代码的难度,减少bug:
dplyr、ggplot2这类最常用的包,设计逻辑就是“一列=变量”。只要数据是整洁格式,分组、汇总、绘图一行管道代码就能完成。如果数据是宽表、合并单元格、多变量挤在一列,代码会变得非常冗长。我们先运行代码,生成一份存在大量问题的原始脏数据,模拟现实上报的灾害记录,存在中英文混杂、日期格式混乱、异常值、缺失值、列名带空格中文。
# 加载需要的包
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.6
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.1 ✔ tibble 3.3.0
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.2.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)
library(writexl)
# set.seed():设定随机种子,保证每次运行生成一模一样的数据,方便复现结果
set.seed(2030)
# 生成2030全年日期字符串
datedata <- seq(ymd("2030-01-01"), ymd("2030-12-31"), by = "day") %>% as.character()
# tibble:tidy版本的数据框,比原生data.frame打印更友好,适合查看数据
disaster_messy_data <- tibble(
`事件ID` = c(paste0("DIS-2030-", 1001:1021)), # 灾害事件编号
`Event Date` = c( # 事件日期,多种格式混杂,脏数据典型特征
"2030-01-17", "19/03/2030", "20300707", "2030-08-07", "2030/06/18",
sample(datedata, 16, replace = FALSE)
),
`Disaster Type` = c("Flood", "flooding", "洪水", "Earthquake", "地震", "TYPHOON",
"洪水", "earthquake", "地震", "Flood", "flood", "飓风",
"Typhoon", "洪水", "Cyclone", "typhoon", "飓风",
"干旱", "Earthquake", "earthquake", NA), # 灾害类型中英文混用,存在缺失值NA
`Casualties` = c( # 伤亡人数,包含异常值99999
91, 28, 3, 699, 6, 77, 6, 20, 24, 4, 69, 12, 6, 99999, 8, 3, 0, 126, 27, 2, 5
),
`Economic Loss (million USD)` = c(5800, 740, 20, 15000, 255, 12000, 430, 2530,
1650, 250, 4120, 245, 800, NA, 32.2, 41, 970, 1250, 1300, 82, -100), # 经济损失,存在负数和NA
`Coastal Area` = sample(c("Y", "N", NA), 21, replace = TRUE, prob = c(0.4, 0.5, 0.1)), # 是否沿海,随机生成缺失值
`Severity Level` = c("severe", "moderate", "mild", "severe", "moderate", "severe",
"moderate", "severe", "severe", "mild", "severe", "moderate",
"moderate", "mild", "mild", "mild", "moderate", "severe",
"moderate", "mild", "mild") # 灾害严重等级
)
# 将脏数据保存到Rpractice工程下的data文件夹
write_xlsx(disaster_messy_data, "data/disaster_messy_data.xlsx")
随堂练习:运行完上面代码,打开Rpractice里的
data文件夹,可以看到disaster_messy_data.xlsx原始脏数据文件。
步骤:读入数据 →
glimpse()看结构 →summary()看统计、缺失、异常
# 加载读取Excel的包
library(readxl)
# 导入原始脏数据,使用相对路径(Rpractice项目自动定位根目录,换电脑只需要移动整个Rpractice文件夹)
disaster_raw <- read_excel("data/disaster_messy_data.xlsx")
# glimpse():快速查看数据结构,每一列的类型、前几条数据,tidyverse推荐查看方式
glimpse(disaster_raw)
## Rows: 21
## Columns: 7
## $ 事件ID <chr> "DIS-2030-1001", "DIS-2030-1002", "DIS-2…
## $ `Event Date` <chr> "2030-01-17", "19/03/2030", "20300707", …
## $ `Disaster Type` <chr> "Flood", "flooding", "洪水", "Earthquake",…
## $ Casualties <dbl> 91, 28, 3, 699, 6, 77, 6, 20, 24, 4, 69,…
## $ `Economic Loss (million USD)` <dbl> 5800.0, 740.0, 20.0, 15000.0, 255.0, 120…
## $ `Coastal Area` <chr> "N", "Y", "Y", "N", "Y", "Y", "Y", NA, "…
## $ `Severity Level` <chr> "severe", "moderate", "mild", "severe", …
# summary():基础统计摘要,数值列会输出均值最值,同时统计NA数量
summary(disaster_raw)
## 事件ID Event Date Disaster Type Casualties
## Length:21 Length:21 Length:21 Min. : 0
## Class :character Class :character Class :character 1st Qu.: 5
## Mode :character Mode :character Mode :character Median : 12
## Mean : 4820
## 3rd Qu.: 69
## Max. :99999
##
## Economic Loss (million USD) Coastal Area Severity Level
## Min. : -100.0 Length:21 Length:21
## 1st Qu.: 204.2 Class :character Class :character
## Median : 770.0 Mode :character Mode :character
## Mean : 2370.8
## 3rd Qu.: 1870.0
## Max. :15000.0
## NA's :1
观察问题清单
- 列名混杂中文、空格、括号;
- 日期列格式不统一;
- 灾害类型中英文混杂;
- casualties存在99999极端异常值;economic_loss存在负数;
- 多处NA缺失值。
原则:列名全部小写,空格替换下划线,去掉中文、括号、特殊符号。
rename():手动修改个别特殊列rename_with():批量统一处理全部列# %>% 管道符:把左边对象作为函数第一个参数传入,代码从左往右读,更符合阅读习惯
disaster_clean <- disaster_raw %>%
# 手动重命名指定列
rename(
event_id = `事件ID`,
economic_loss = `Economic Loss (million USD)`
) %>%
# 批量对所有列名做处理:转小写,空格替换下划线
rename_with(~ str_to_lower(str_replace_all(., " ", "_")))
# 查看修改后的列名和数据结构
glimpse(disaster_clean)
## Rows: 21
## Columns: 7
## $ event_id <chr> "DIS-2030-1001", "DIS-2030-1002", "DIS-2030-1003", "DIS…
## $ event_date <chr> "2030-01-17", "19/03/2030", "20300707", "2030-08-07", "…
## $ disaster_type <chr> "Flood", "flooding", "洪水", "Earthquake", "地震", "TYPHOON…
## $ casualties <dbl> 91, 28, 3, 699, 6, 77, 6, 20, 24, 4, 69, 12, 6, 99999, …
## $ economic_loss <dbl> 5800.0, 740.0, 20.0, 15000.0, 255.0, 12000.0, 430.0, 25…
## $ coastal_area <chr> "N", "Y", "Y", "N", "Y", "Y", "Y", NA, "N", "N", NA, "N…
## $ severity_level <chr> "severe", "moderate", "mild", "severe", "moderate", "se…
原始数据存在2030-01-17、19/03/2030、20300707多种格式,使用lubridate::parse_date_time()多格式解析。
disaster_clean <- disaster_clean %>%
mutate(
# mutate:新增/修改列,Tidy Data中,我们只修改整列,不修改单个单元格
event_date = coalesce(
parse_date_time(event_date, orders = c("ymd", "dmy", "mdy"))
),
event_date = format(event_date, "%Y-%m-%d") # 统一转换成 yyyy-mm-dd 文本格式
)
# 查看修改结果
glimpse(disaster_clean)
## Rows: 21
## Columns: 7
## $ event_id <chr> "DIS-2030-1001", "DIS-2030-1002", "DIS-2030-1003", "DIS…
## $ event_date <chr> "2030-01-17", "2030-03-19", "2030-07-07", "2030-08-07",…
## $ disaster_type <chr> "Flood", "flooding", "洪水", "Earthquake", "地震", "TYPHOON…
## $ casualties <dbl> 91, 28, 3, 699, 6, 77, 6, 20, 24, 4, 69, 12, 6, 99999, …
## $ economic_loss <dbl> 5800.0, 740.0, 20.0, 15000.0, 255.0, 12000.0, 430.0, 25…
## $ coastal_area <chr> "N", "Y", "Y", "N", "Y", "Y", "Y", NA, "N", "N", NA, "N…
## $ severity_level <chr> "severe", "moderate", "mild", "severe", "moderate", "se…
中英文混杂,使用case_when()做匹配替换,转换因子;严重程度转为有序因子ordered()。
disaster_clean <- disaster_clean %>%
mutate(
# case_when:多条件判断,依次匹配条件,适合批量文本替换
disaster_type = case_when(
str_detect(disaster_type, "Flood|洪水|flooding") ~ "flood",
str_detect(disaster_type, "Earthquake|地震") ~ "earthquake",
str_detect(disaster_type, "cyclone|Cyclone|飓风|TYPHOON|Typhoon") ~ "typhoon",
str_detect(disaster_type, "wildfire|野火") ~ "wild fire",
str_detect(disaster_type, "干旱") ~ "drought",
TRUE ~ tolower(disaster_type) # 其余全部转为小写
) %>% factor(), # factor:因子,分类变量,用于分组统计绘图
# ordered:有序因子,有大小顺序:mild < moderate < severe
severity_level = ordered(severity_level, levels = c("mild", "moderate", "severe"))
)
glimpse(disaster_clean)
## Rows: 21
## Columns: 7
## $ event_id <chr> "DIS-2030-1001", "DIS-2030-1002", "DIS-2030-1003", "DIS…
## $ event_date <chr> "2030-01-17", "2030-03-19", "2030-07-07", "2030-08-07",…
## $ disaster_type <fct> flood, flood, flood, earthquake, earthquake, typhoon, f…
## $ casualties <dbl> 91, 28, 3, 699, 6, 77, 6, 20, 24, 4, 69, 12, 6, 99999, …
## $ economic_loss <dbl> 5800.0, 740.0, 20.0, 15000.0, 255.0, 12000.0, 430.0, 25…
## $ coastal_area <chr> "N", "Y", "Y", "N", "Y", "Y", "Y", NA, "N", "N", NA, "N…
## $ severity_level <ord> severe, moderate, mild, severe, moderate, severe, moder…
业务常识:
disaster_clean <- disaster_clean %>%
mutate(
# replace(向量, 条件, 替换值):批量修改满足条件的元素,R向量化操作,不用循环
casualties = replace(casualties, casualties > 1e4, NA),
economic_loss = replace(economic_loss, economic_loss < 0, NA)
)
glimpse(disaster_clean)
## Rows: 21
## Columns: 7
## $ event_id <chr> "DIS-2030-1001", "DIS-2030-1002", "DIS-2030-1003", "DIS…
## $ event_date <chr> "2030-01-17", "2030-03-19", "2030-07-07", "2030-08-07",…
## $ disaster_type <fct> flood, flood, flood, earthquake, earthquake, typhoon, f…
## $ casualties <dbl> 91, 28, 3, 699, 6, 77, 6, 20, 24, 4, 69, 12, 6, NA, 8, …
## $ economic_loss <dbl> 5800.0, 740.0, 20.0, 15000.0, 255.0, 12000.0, 430.0, 25…
## $ coastal_area <chr> "N", "Y", "Y", "N", "Y", "Y", "Y", NA, "N", "N", NA, "N…
## $ severity_level <ord> severe, moderate, mild, severe, moderate, severe, moder…
计算每一列缺失率,快速评估数据质量。
library(knitr)
# map_dbl:对每一列运行函数,返回数值向量,计算每一列缺失比例
na_info <- map_dbl(disaster_clean, ~mean(is.na(.))) %>%
tibble(variable = names(.), na_rate = .)
# kable:输出美观表格
kable(na_info, caption = "各变量缺失率", format = "simple")
| variable | na_rate |
|---|---|
| event_id | 0.0000000 |
| event_date | 0.0000000 |
| disaster_type | 0.0476190 |
| casualties | 0.0476190 |
| economic_loss | 0.0952381 |
| coastal_area | 0.1428571 |
| severity_level | 0.0000000 |
真实项目中,缺失值处理分删除、填充等,本实验演示统计缺失率。 本示例代码演示人为补全缺失(仅教学演示!真实科研必须溯源核实原始记录,不能盲目填充)
# which(is.na()):找到缺失值所在行号
disaster_clean$disaster_type[which(is.na(disaster_clean$disaster_type))] <-c("flood")
disaster_clean$casualties[which(is.na(disaster_clean$casualties))] <-c(9)
disaster_clean$economic_loss[which(is.na(disaster_clean$economic_loss))] <-c(500,100)
disaster_clean$coastal_area[which(is.na(disaster_clean$coastal_area))] <-c("Y")
保存到Rpractice工程
data/文件夹,原始脏数据不动,新生成清洗版本。
# 将清洗完毕整洁数据写入Excel
write_xlsx(disaster_clean, "data/disaster_data_cleaned.xlsx")
记录每一个变量:变量名、中文标签、数据类型、取值范围、来源、完整性。
disaster_clean_note <- tibble(
变量名 = names(disaster_clean),
中文标签 = c("事件ID", "事件日期", "灾害类型", "伤亡人数", "经济损失(百万美元)",
"是否沿海地区", "灾害严重程度"),
数据类型 = c("字符型", "字符型", "因子型", "数值型", "数值型",
"字符型", "有序因子型"),
取值范围 = c("DIS-2030-XXXX", "2030-01-01至2030-12-31",
"flood/earthquake/typhoon/drought", "0-10000",
">=0", "Y/N", "mild/moderate/severe"),
数据来源 = c("系统生成", "事件报告", "分类系统", "伤亡统计", "经济损失评估",
"地理信息系统", "灾害评估"),
完整性 = paste0(round(100 - sapply(disaster_clean, function(x) sum(is.na(x))/length(x)*100), 1), "%")
)
kable(disaster_clean_note,
caption = "清洗后灾害数据的数据字典",
align = c("l", "l", "c", "l", "l", "c"),
format = "simple")
| 变量名 | 中文标签 | 数据类型 | 取值范围 | 数据来源 | 完整性 |
|---|---|---|---|---|---|
| event_id | 事件ID | 字符型 | DIS-2030-XXXX | 系统生成 | 100% |
| event_date | 事件日期 | 字符型 | 2030-01-01至2030-12-31 | 事件报告 | 100% |
| disaster_type | 灾害类型 | 因子型 | flood/earthquake/typhoon/drought | 分类系统 | 100% |
| casualties | 伤亡人数 | 数值型 | 0-10000 | 伤亡统计 | 100% |
| economic_loss | 经济损失(百万美元) | 数值型 | >=0 | 经济损失评估 | 100% |
| coastal_area | 是否沿海地区 | 字符型 | Y/N | 地理信息系统 | 100% |
| severity_level | 灾害严重程度 | 有序因子型 | mild/moderate/severe | 灾害评估 | 100% |
随堂练习: