R: R 综合数据分析报告:端到端完整项目
最后更新:2026-08-26
这是 R 教程的最后一课——把前面 29 课所有知识串起来,做一个端到端完整项目。从读数据到清洗、EDA、建模、可视化、输出报告,模拟真实数据分析师的一天。
读完这一课你就有能力独立完成完整 R 数据分析项目——数据科学求职面试作品集级别。
1. 你将学到
- 端到端项目 6 阶段(数据→清洗→EDA→建模→可视化→报告)
- 综合运用:readr + tidyr + dplyr + ggplot2 + broom + rmarkdown
- R Markdown 自动生成报告
- saveRDS 持久化与模型部署
- 输出 PDF/HTML 报告
- 实战:客户行为分析完整报告
2. 一个数据分析师一天的故事
(1) 痛点:从数据到报告要 3 天
Alice是数据分析师,接到任务:分析 2024 年客户行为,给manager一份完整报告。
传统流程:读 Excel → 清洗 → Excel 透视表 → 画图 → 复制到 PPT → 写分析 → manager 1 周后要看。
(2) R 的解法
R
# 1. 一行 R Markdown 报告
rmarkdown::render('customer_analysis.Rmd')
1 行代码 → 完整 HTML/PDF 报告(含代码、结果、图表、解读)。
3. 端到端项目 6 阶段
graph TB
A[1. 数据加载] --> B[2. 数据清洗]
B --> C[3. EDA 探索]
C --> D[4. 描述统计]
D --> E[5. 建模分析]
E --> F[6. 可视化与报告]
A --> A1[readr/DBI]
B --> B1[tidyr/stringr]
C --> C1[skimr/GGally]
D --> D1[dplyr summary]
E --> E1[lm/glm]
F --> F1[ggplot2/Rmd]
style A fill:#fff3cd
style B fill:#cce5ff
style C fill:#d4edda
style D fill:#f8d7da
style E fill:#e1d4ff
style F fill:#ffe1d4
(1) 6 阶段目标
| 阶段 | 目标 | 输出 |
|---|---|---|
| 1. 数据加载 | 读入数据 | tibble |
| 2. 数据清洗 | 处理缺失/异常/重复 | 干净 tibble |
| 3. EDA | 探索分布/关系 | 图表 + 统计 |
| 4. 描述统计 | 关键指标 | 汇总表 |
| 5. 建模 | 回归/分类/聚类 | 模型对象 |
| 6. 报告 | 整合 + 解读 | HTML/PDF |
4. 实战:客户行为分析端到端项目
(1) 完整 R 脚本
R
# ============================================
# 客户行为分析 - 端到端项目
# 阶段:1. 数据加载 → 6. 报告输出
# ============================================
library(readr)
library(dplyr)
library(tidyr)
library(stringr)
library(lubridate)
library(ggplot2)
library(broom)
library(skimr)
# ========== 阶段 1:数据加载 ==========
cat('=== 阶段 1:数据加载 ===\n')
set.seed(42)
n <- 2000
customers_raw <- tibble(
customer_id = 1:n,
age = round(rnorm(n, 35, 12)),
gender = sample(c('男', '女'), n, replace = TRUE),
city = sample(c('北京', '上海', '广州', '深圳', '杭州'), n, replace = TRUE),
register_date = sample(seq(as.Date('2023-01-01'),
as.Date('2024-06-30'), by = 'day'), n, replace = TRUE),
monthly_visits = round(rnorm(n, 10, 5)),
avg_order_value = round(rnorm(n, 200, 80)),
support_calls = sample(0:10, n, replace = TRUE),
plan = sample(c('基础', '高级', '企业'), n, replace = TRUE,
prob = c(0.5, 0.3, 0.2))
) |>
mutate(
logit_p = -2 + 0.02 * age - 0.05 * monthly_visits + 0.2 * support_calls,
p = 1 / (1 + exp(-logit_p)),
churn = rbinom(n, 1, p)
) |>
select(-logit_p, -p) |>
mutate(
age = ifelse(row_number() %in% sample(n, 10), NA, age),
avg_order_value = ifelse(row_number() %in% sample(n, 5), -999, avg_order_value),
register_date = ifelse(row_number() %in% sample(n, 8), NA, register_date)
)
write_csv(customers_raw, 'customer_raw.csv')
cat('已加载', nrow(customers_raw), '行原始数据\n\n')
# ========== 阶段 2:数据清洗 ==========
cat('=== 阶段 2:数据清洗 ===\n')
customers <- customers_raw |>
janitor::clean_names() |>
distinct() |>
mutate(
age = ifelse(is.na(age) | age < 0 | age > 150,
median(age[age > 0 & age < 150], na.rm = TRUE), age),
avg_order_value = ifelse(avg_order_value < 0, NA, avg_order_value)
) |>
drop_na(register_date) |>
filter(monthly_visits >= 0, support_calls >= 0)
cat('清洗后:', nrow(customers), '行\n')
cat(' - 重复:', nrow(customers_raw) - nrow(distinct(customers_raw)), '行已去\n')
cat(' - 年龄缺失/异常:已处理\n')
cat(' - 订单金额异常:已置 NA\n\n')
# ========== 阶段 3:EDA 探索 ==========
cat('=== 阶段 3:EDA 探索 ===\n')
cat('数据维度:', nrow(customers), '行 x', ncol(customers), '列\n')
cat('流失率:', round(mean(customers$churn) * 100, 2), '%\n')
churn_by_plan <- customers |>
group_by(plan) |>
summarise(
n = n(),
churn_rate = round(mean(churn) * 100, 2),
avg_age = round(mean(age), 1),
avg_visits = round(mean(monthly_visits), 1)
)
cat('\n按套餐类型的流失率:\n')
print(churn_by_plan)
p1 <- ggplot(customers, aes(x = monthly_visits, y = avg_order_value,
color = factor(churn))) +
geom_point(alpha = 0.5) +
geom_smooth(method = 'lm', se = FALSE) +
scale_color_brewer(palette = 'Set1', labels = c('未流失', '流失')) +
labs(title = '访问次数 vs 平均订单金额',
x = '月访问次数', y = '平均订单金额', color = '状态') +
theme_minimal()
cat('生成 2 张关键图表\n\n')
# ========== 阶段 4:描述统计 ==========
cat('=== 阶段 4:描述统计 ===\n')
key_metrics <- customers |>
summarise(
总客户数 = n(),
流失客户数 = sum(churn),
流失率 = round(mean(churn) * 100, 2),
平均年龄 = round(mean(age), 1),
平均月访问 = round(mean(monthly_visits), 1),
平均订单金额 = round(mean(avg_order_value, na.rm = TRUE), 2),
平均客服次数 = round(mean(support_calls), 1)
)
print(key_metrics)
by_city <- customers |>
group_by(city) |>
summarise(
客户数 = n(),
流失率 = round(mean(churn) * 100, 2),
平均订单 = round(mean(avg_order_value, na.rm = TRUE), 2)
) |>
arrange(desc(客户数))
cat('\n按城市统计:\n')
print(by_city)
cat('\n')
# ========== 阶段 5:建模分析 ==========
cat('=== 阶段 5:建模分析 ===\n')
# 5.1 逻辑回归:流失预测
churn_model <- glm(churn ~ age + gender + plan + monthly_visits +
avg_order_value + support_calls,
data = customers, family = binomial)
cat('逻辑回归模型:\n')
summary(churn_model)
cat('\nOdds Ratio 解读:\n')
or_df <- tidy(churn_model, conf.int = TRUE, exponentiate = TRUE) |>
filter(term != '(Intercept)')
print(or_df |> select(term, estimate, std.error, p.value, conf.low, conf.high))
# 5.3 线性回归:访问次数预测
visit_model <- lm(monthly_visits ~ age + plan + avg_order_value,
data = customers)
cat('\n访问次数预测模型:\n')
summary(visit_model)
# 5.4 模型评估
library(pROC)
customers$churn_prob <- predict(churn_model, type = 'response')
roc_obj <- roc(customers$churn, customers$churn_prob)
cat('\n流失模型 AUC:', round(auc(roc_obj), 3), '\n\n')
# ========== 阶段 6:可视化与报告 ==========
cat('=== 阶段 6:可视化与报告 ===\n')
p2 <- customers |>
group_by(plan, churn) |>
summarise(n = n(), .groups = 'drop') |>
ggplot(aes(x = plan, y = n, fill = factor(churn))) +
geom_col(position = 'fill') +
scale_y_continuous(labels = scales::percent) +
scale_fill_brewer(palette = 'Set1', labels = c('未流失', '流失')) +
labs(title = '各套餐类型流失占比', x = '套餐', y = '比例', fill = '状态') +
theme_minimal()
p3 <- customers |>
group_by(plan) |>
summarise(
n = n(),
churn_rate = mean(churn)
) |>
ggplot(aes(x = plan, y = churn_rate, fill = plan)) +
geom_col() +
geom_text(aes(label = paste0(round(churn_rate * 100, 1), '%')),
vjust = -0.5) +
scale_y_continuous(labels = scales::percent,
expand = expansion(mult = c(0, 0.15))) +
labs(title = '各套餐类型流失率', x = '套餐', y = '流失率') +
theme_minimal() +
theme(legend.position = 'none')
p4 <- ggplot(customers, aes(x = plan, y = monthly_visits, fill = plan)) +
geom_boxplot() +
labs(title = '各套餐月访问次数分布', x = '套餐', y = '月访问次数') +
theme_minimal() +
theme(legend.position = 'none')
# 6.2 保存图表
ggsave('chart_visits_vs_order.png', p1, width = 8, height = 6, dpi = 300)
ggsave('chart_churn_by_plan.png', p2, width = 8, height = 6, dpi = 300)
ggsave('chart_churn_rate.png', p3, width = 8, height = 6, dpi = 300)
ggsave('chart_visits_by_plan.png', p4, width = 8, height = 6, dpi = 300)
# 6.3 保存模型
saveRDS(churn_model, 'churn_model.rds')
saveRDS(visit_model, 'visit_model.rds')
# 6.4 保存处理后的数据
write_csv(customers, 'customer_clean.csv')
saveRDS(customers, 'customer_clean.rds')
# 6.5 业务洞察总结
cat('\n=== 业务洞察总结 ===\n')
cat('1. 整体流失率:', round(mean(customers$churn) * 100, 2), '%\n')
cat('2. 客服次数 > 5 的客户流失率:',
round(mean(customers$churn[customers$support_calls > 5]) * 100, 2), '%\n')
cat('3. 月访问 < 5 的客户流失率:',
round(mean(customers$churn[customers$monthly_visits < 5]) * 100, 2), '%\n')
cat('4. 基础套餐流失率最高(',
round(mean(customers$churn[customers$plan == '基础']) * 100, 2), '%)\n')
cat('5. 年龄越大、访问越少 → 流失概率越高\n')
cat('6. 建议:客服次数 > 5 的客户进入高风险名单,主动挽留\n')
cat('\n=== 全部完成 ===\n')
cat('输出文件:\n')
cat(' - customer_raw.csv(原始数据)\n')
cat(' - customer_clean.csv / .rds(清洗后数据)\n')
cat(' - chart_*.png(4 张图)\n')
cat(' - churn_model.rds(流失预测模型)\n')
cat(' - visit_model.rds(访问预测模型)\n')
(2) R Markdown 报告模板(简化版)
新建 customer_report.Rmd 文件,完整 Rmd 模板结构(YAML + 6 章节 + R 代码块 + 文字解读):
YAML 元数据(文件开头):定义 title / author / output format(html_document + theme: flatly + toc: true)。
6 大章节:
- 项目概述 - 一段背景介绍 + 3 个分析目标
- 数据概览 - 加载清洗后数据 + skimr 摘要
- 探索性分析 - 流失率 + 各套餐分布 + 散点图
- 统计模型 - 加载已保存的 churn_model.rds + tidy 展示 OR
- 关键发现 - bullet 列表,3-5 条 OR 解读
- 业务建议 - 3 条 actionable 建议 + 附录文件清单
每个章节用 R 代码块(```{r})嵌入分析代码,章节间用 ## 1. xxx / ## 2. xxx 标题分隔。
💡 提示:完整 Rmd 模板省略(可参考 RStudio 官方 R Markdown 模板生成)。核心是 YAML 元数据 + ```{r} 代码块 + #/## 标题 + 文字解读 4 件套。
(3) 生成报告
R
# 生成 HTML 报告
rmarkdown::render('customer_report.Rmd',
output_format = 'html_document',
output_file = 'customer_report.html')
# 生成 PDF 报告(需安装 LaTeX)
rmarkdown::render('customer_report.Rmd',
output_format = 'pdf_document',
output_file = 'customer_report.pdf')
预期输出:
- 1 份 10-15 页 HTML 报告(含代码、结果、图表、解读)
- 4 张 PNG 图表
- 2 个 .rds 模型
- 1 个干净数据 CSV
9. R 教程完整学习路径
(1) 5 个阶段
graph TB
A[Phase 1 基础 10 课] --> B[Phase 2 数据 7 课]
B --> C[Phase 3 可视化 5 课]
C --> D[Phase 4 统计 5 课]
D --> E[Phase 5 实战 3 课]
E --> F[完结 能独立做项目]
style A fill:#cce5ff
style B fill:#d4edda
style C fill:#f8d7da
style D fill:#e1d4ff
style E fill:#ffe1d4
style F fill:#fff3cd
(2) 30 课清单
| Phase | 课 | 主题 |
|---|---|---|
| 1 基础 | 01-10 | 认识R / 基础语法 / 数据类型 / 向量 / 运算符 / 控制流 / 函数 / 矩阵 / 列表 / 数据框 |
| 2 数据 | 11-17 | CSV / Excel / JSON / 数据库 / 字符串 / 正则 / 重塑 |
| 3 可视化 | 18-22 | 基础绘图 / ggplot2入门 / 进阶 / 主题 / 地图 |
| 4 统计 | 23-27 | 描述统计 / 概率分布 / 假设检验 / 线性回归 / 逻辑回归 |
| 5 实战 | 28-30 | EDA / 数据清洗 / 综合报告 |
10. 进一步学习建议
(1) 进阶 R 包
| 包 | 用途 |
|---|---|
| shiny | Web 应用(交互式仪表板) |
| plumber | REST API(机器学习部署) |
| tidymodels | 现代机器学习(caret 替代) |
| torch | 深度学习 |
| arrow | 大数据(Parquet 文件) |
(2) 必读资源
| 资源 | 说明 |
|---|---|
| R for Data Science (2e) | Hadley 大神官方书,hadley/r4ds |
| Advanced R (2e) | R 高级编程 |
| ggplot2: Elegant Graphics | ggplot2 原理书 |
| R Markdown Cookbook | 报告生成 |
| RStudio Cheatsheets | 速查表 |
(3) 实战项目
- Kaggle(kaggle.com)—— 真实数据科学竞赛
- TidyTuesday(tidytues.day)—— 每周免费数据集
- 自己的数据(工作 / 兴趣 / 投资)
❓ 常见问题
Q R Markdown 怎么用?
A 新建
.Rmd 文件(YAML 元数据 + Markdown 文本 + R 代码块)。rmarkdown::render() 生成 HTML/PDF。RStudio Knit 按钮一键出报告。Q .rds 和 .RData 区别?
A .rds 保存单个对象(如 1 个 model),.RData 保存多个对象(如整个工作空间)。新代码用 .rds 更规范。
Q 模型怎么部署到生产?
A 3 种方式:① plumber 包 写 REST API ② vetiver 包 标准化部署 ③ 导出为 PMML/Python 调用。
Q 项目怎么管理?
A 用 RStudio Project + renv 包(包管理)+ Git(版本控制)+ here 包(路径管理)。
Q 30 课学完能做什么?
A 能独立完成 80% 的数据分析任务——读数据、清洗、EDA、统计、建模、可视化、报告。剩余 20%(机器学习/深度学习/大数据)需要额外学习。
Q 下一步学什么?
A ① 机器学习(tidymodels) ② 文本挖掘(tidytext) ③ 时间序列(fable) ④ Shiny 仪表板 ⑤ R 高级编程(Advanced R)。选一个方向深入即可。
📖 小节
- 端到端项目 6 阶段:数据 → 清洗 → EDA → 描述 → 建模 → 报告
- R Markdown = 文档 + 代码 + 结果 + 图表合一的报告工具
- saveRDS 持久化单个对象,write_csv 持久化表格,ggsave 持久化图表
- RStudio Project + renv + Git = 数据科学项目三件套
- 30 课覆盖 R 数据分析 80% 工作量,剩余 20% 需要机器学习/深度学习扩展
- 下一步:tidymodels(机器学习)/ Shiny(仪表板)/ plumber(API)/ tidytext(文本)
- 核心思想:从数据到洞察的端到端工作流 = 数据科学家的基本功
- 教程完结——能独立完成 R 数据分析项目
📝 作业
毕业项目作业(完整端到端数据分析项目,R 教程 30 课结业项目):
- 数据选择:选 1 个公开数据集(推荐 Titanic / 鸢尾花 / mtcars / mpg / nycflights13)
- 完整流程:
- 阶段 1:读数据(readr 或 datasets)
- 阶段 2:清洗(缺失/异常/重复/类型)
- 阶段 3:EDA(summary + skimr + 可视化)
- 阶段 4:描述统计(按组汇总)
- 阶段 5:建模(lm 或 glm)
- 阶段 6:报告(R Markdown HTML)
- 交付物:
analysis.R主脚本(200+ 行)report.RmdR Markdown 报告- 5+ 张图表
- 1 段业务洞察总结(500 字)
- 评分标准:
- 代码可运行(20 分)
- 流程完整(20 分)
- 分析深度(20 分)
- 图表美观(20 分)
- 报告可读(20 分) 完成毕业项目 = R 教程结业!
▶ 示例:30 课学习总结
R
# ============================================
# R 教程 30 课学习总结脚本
# 运行后输出每课的主题
# ============================================
lessons <- c(
"01 认识R", "02 基础语法", "03 数据类型", "04 向量",
"05 运算符", "06 控制流", "07 函数", "08 矩阵与数组",
"09 列表", "10 数据框与因子",
"11 CSV", "12 Excel", "13 JSON与XML", "14 数据库",
"15 字符串", "16 正则", "17 数据重塑",
"18 基础绘图", "19 ggplot2入门", "20 ggplot2进阶",
"21 主题美化", "22 地图",
"23 描述统计", "24 概率分布", "25 假设检验",
"26 线性回归", "27 逻辑回归",
"28 EDA", "29 数据清洗", "30 综合报告"
)
cat("R 教程 30 课清单:\n")
for (i in seq_along(lessons)) {
cat(sprintf("%2d. %s\n", i, lessons[i]))
}
预期输出:
TEXT
📖 仅展示
R 教程 30 课清单:
1. 01 认识R
2. 02 基础语法
...
30. 30 综合报告
12. 恭喜完成 R 教程 30 课
你已经系统学完了 R 数据科学 30 课——从基础语法到端到端项目,能独立完成真实数据分析任务。接下来就是实战——找真实数据,做真实项目,积累作品集。祝你成为优秀的数据科学家!