实验目标

本次实验,完成后你将掌握:

  1. 继续复用Rpractice总工程,读取上一次实验清洗完成的数据集
  2. 学会使用dplyr分组汇总,制作科研汇报用统计表
  3. 理解不同图表的适用场景,掌握9种常用统计图形的绘制
  4. 掌握ggplot2绘图语法,将图片输出保存至工程output文件夹
  5. 学会根据研究目的,选择合适图表,用图形讲清楚数据故事

预备知识

  1. ✅已完成上节课实验:成功生成data/disaster_data_cleaned.xlsx清洗数据集
  2. 熟悉tidyverse管道%>%、分组group_by、summarise汇总
  3. 已提前安装好tidyverse、ggplot2、knitr等包

Rpractice总工程说明

本次实验继续复用Rpractice总工程,不需要新建RStudio项目。

  • code/:保存本次Rmd脚本,命名labB_visualization.Rmd
  • data/:读取实验A产出的清洗后数据 disaster_data_cleaned.xlsx
  • output/:本次所有绘图,全部导出保存到这里

课堂操作步骤: 1. 双击打开 Rpractice.Rproj 2. File → New File → R Script,保存到code文件夹,命名labB_visualization.R 3. 读取上节课清洗好的数据:read_xlsx("data/disaster_data_cleaned.xlsx")

4 数据的表达

数据表达三大形式:文字表达、表格表达、图形表达。 表达的目的:简化信息、探索模式、辅助决策、讲述数据故事。

4.1 表格表达:生成统计汇总表

分组统计不同灾害的事件数量、伤亡合计、经济损失合计,生成汇报用表格。

library(tidyverse)
library(knitr)
disaster_clean <- readxl::read_xlsx("data/disaster_data_cleaned.xlsx")

simple_summary <- disaster_clean %>%
  # group_by:按照灾害类型分组;Tidy Data下分组只需要指定列名,非常方便
  group_by(disaster_type) %>%
  # summarise:分组汇总计算新指标
  summarise(
    事件次数 = n(),                      # n():统计每组观测行数
    伤亡人数 = sum(casualties, na.rm = TRUE), # sum求和,na.rm=TRUE:计算时忽略NA
    经济损失_百万美元 = round(sum(economic_loss, na.rm = TRUE), 1), # round保留1位小数
  ) %>%
  arrange(desc(伤亡人数)) %>% # arrange排序:desc代表降序
  mutate(
    伤亡人数 = format(伤亡人数, big.mark = ","), # 数字千分位分隔符,美化输出
    经济损失_百万美元 = format(经济损失_百万美元, big.mark = ",")
  )

kable(simple_summary,
      col.names = c("灾害类型", "事件次数", "伤亡人数", "经济损失(百万美元)"),
      caption = "2030年灾害事件影响分析",
      align = c("l", "r", "r", "r"),
      format = "simple")
2030年灾害事件影响分析
灾害类型 事件次数 伤亡人数 经济损失(百万美元)
earthquake 6 778 20,817.0
flood 8 215 11,960.0
drought 1 126 1,250.0
typhoon 6 106 14,088.2

4.2 图形表达:9种常用统计图实操

分为两大类:①基于原始观测;②基于统计汇总。所有图片导出保存至Rpractice工程output/。

4.2.1 茎叶图 stem(小样本,看原始数值+分布)

set.seed(123)
# 生成30个考试分数,均值75标准差10
exam_scores <- round(rnorm(30, mean = 75, sd = 10))
# pmax/pmin:限制分数在0~100之间,防止超出合理范围
exam_scores <- pmax(0, pmin(100, exam_scores))
# stem:绘制茎叶图,原生R函数,适合少量样本
stem(exam_scores)
## 
##   The decimal point is 1 digit(s) to the right of the |
## 
##   5 | 58
##   6 | 244
##   6 | 588999
##   7 | 0133
##   7 | 666799
##   8 | 0023
##   8 | 78
##   9 | 123

4.2.2 点图/云图 stripchart

set.seed(2026)
# 生成通勤时间数据
commute_time <- round(rlnorm(200, meanlog = 3.6, sdlog = 0.5))

stripchart(commute_time,
           method = "jitter", # jitter:点抖动,避免点重叠
           pch = 16,           # pch:点样式,16是实心圆点
           col = rgb(0,0,0,0.4),# 设置颜色和透明度
           xlab = "每日通勤时间 (分钟)", # X轴标签
           main = "城市白领通勤时间分布云图") # 图标题
grid(nx = 20, col = "gray", lty = "dotted") # 添加网格线

4.2.3 散点图(两个连续变量关系)

set.seed(2026)
work_years <- rep(1:15,2)       # 工作年限,重复1~15两遍
salary <- work_years*1 + runif(30,-3,3) # 月薪,增加随机扰动
salary[3] <- 12
df_scatter <- data.frame(work_years, salary)

library(ggplot2)
# ggplot绘图:第一个参数是数据集,aes()映射x,y等美学变量,Tidy Data直接映射列名
p_scatter <- ggplot(df_scatter,aes(x=work_years,y=salary))+
  geom_point(size=3,alpha=0.7,color="grey30")+ # 绘制点图层
  labs(x="工作年限(年)",y="月薪(千元)",title="工作年限与月薪的关系")+ # 设置坐标轴与标题
  theme_minimal() # 主题:简约白底风格
ggsave("output/scatter_work_salary.png", plot=p_scatter, width=7, height=4, dpi=300)
print(p_scatter) # 在html文档中输出图片

4.2.4 折线图(时间序列趋势)

set.seed(2026)
# 构造月度销售数据
df_line <- tibble(
  month = 1:12,
  sales = 200 + seq(0,50,length.out=12)+rnorm(12,0,5)
)

p_line <- ggplot(df_line, aes(x=month,y=sales))+
  geom_line(size=1,color="#2c7bb6")+ # 绘制折线图层
  geom_point(size=3,color="#2c7bb6")+# 叠加数据点
  scale_x_continuous(breaks=1:12)+  # X轴刻度1到12月
  labs(title="某公司月度销售额变化趋势",x="月份",y="销售额(万元)")+
  theme_minimal()+
  theme(plot.title = element_text(face="bold"))
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
ggsave("output/line_sales.png", plot=p_line, width=7, height=4, dpi=300)
print(p_line)

4.2.5 饼图(构成比例)

set.seed(2026)
df_edu <- tibble(
  education = sample(c("本科","硕士","高中及以下","大专"),
                     size=400, replace = TRUE,
                     prob=c(0.5,0.25,0.125,0.125))
) %>%
  count(education) %>% # count统计各类别数量,Tidy Data常用快速计数函数
  mutate(percentage = n/sum(n)) # 新增百分比列

p_pie <- ggplot(df_edu, aes(x="", y=percentage, fill=education))+
  geom_col(width=1,color="white")+
  coord_polar("y")+ # 极坐标转换,把条形图变成饼图
  scale_fill_brewer(palette = "Spectral")+
  labs(title="受访者学历构成调查", fill="学历层次")+
  theme_void() # 清空坐标轴,饼图不需要坐标轴
ggsave("output/pie_edu.png", plot=p_pie, width=6, height=6, dpi=300)
print(p_pie)

4.2.6 条形图(类别对比)

set.seed(2026)
df_community <- tibble(
  district = rep(c("A街道","B街道","C街道","D街道","E街道"),each=50),
  borrowed = c(rnorm(50,500,20), rnorm(50,450,20), rnorm(50,400,20),
               rnorm(50,350,20), rnorm(50,300,20))
) %>%
  group_by(district) %>%
  summarise(avg_borrowed = mean(borrowed)) # 分组求均值

p_bar <- ggplot(df_community, aes(x=district,y=avg_borrowed, fill=district))+
  geom_col(alpha=0.8, width=0.6)+
  scale_fill_brewer(palette="Blues")+
  labs(title="各街道社区平均图书借阅量",x="社区街道",y="平均借阅量(本)")+
  theme_minimal()+
  theme(legend.position="none") # 隐藏图例,x轴已经标注类别
ggsave("output/bar_community.png", plot=p_bar, width=7, height=4, dpi=300)
print(p_bar)

4.2.7 分组条形图(两个分类变量)

set.seed(2026)
df_policy <- tibble(
  type = c(rep("本地户籍",100), rep("非本地户籍",100)),
  attitude = c(rep("满意",80),rep("不满意",20),rep("满意",30),rep("不满意",70))
) %>%
  count(type, attitude) # 按两个变量交叉计数

p_gbar <- ggplot(df_policy, aes(x=type,y=n,fill=attitude))+
  geom_col(position = position_dodge(width=0.8), width=0.7)+ # position_dodge:分组并列
  scale_fill_manual(values = c("满意"="forestgreen","不满意"="firebrick"))+
  labs(title="不同户籍群体对政策的满意度分布",x="户籍类型",y="受访人数(人)",fill="态度")+
  theme_minimal()
ggsave("output/groupbar_policy.png", plot=p_gbar, width=7, height=4, dpi=300)
print(p_gbar)

4.2.8 箱线图(多组分布对比,识别异常值)

set.seed(2026)
df_commute <- tibble(
  group = rep(c("企业职员","公务人员","自由职业"),each=40),
  minutes = c(rnorm(40,40,10), rnorm(40,35,10), rnorm(40,40,20))
)
df_commute$minutes[df_commute$group=="企业职员"][1] <-120 # 手动添加一个极端异常点

p_box <- ggplot(df_commute, aes(x=group,y=minutes,fill=group))+
  geom_boxplot(alpha=0.6, outlier.color="red")+ # outlier.color:异常点标红
  scale_fill_brewer(palette="Set2")+
  labs(title="不同职业群体日均通勤时间",x="职业群体",y="时间(分钟)",fill="职业")+
  theme_minimal()+
  theme(legend.position="none")
ggsave("output/boxplot_commute.png", plot=p_box, width=7, height=4, dpi=300)
print(p_box)

4.2.9 直方图(连续变量分布)

set.seed(2026)
# 生成幸福感得分
df_happy <- tibble(score = rnorm(1000, mean=60, sd=15))

p_hist <- ggplot(df_happy, aes(x=score))+
  geom_histogram(aes(y=after_stat(density)), binwidth=5, fill="orange",color="white",alpha=0.8)+
  stat_function(fun=dnorm, args=list(mean=60,sd=15),color="darkblue",size=1)+ # 添加理论正态曲线
  labs(title="居民主观幸福感得分分布",x="幸福感量表得分(0‑100)",y="概率密度")+
  theme_minimal()
ggsave("output/hist_happy.png", plot=p_hist, width=7, height=4, dpi=300)
print(p_hist)

随堂练习:

  1. 运行全部绘图代码,查看output文件夹,确认所有图片成功导出;
  2. 思考:箱线图、直方图分别适合解决什么问题?

实验总结

  1. 延续Rpractice总工程,读取上节课清洗完成的数据,实现跨实验的可复现分析。
  2. dplyr分组+summarise()可以快速制作统计汇总表,kable输出美观表格用于报告。
  3. ggplot2采用图层化绘图思路:数据集→aes映射→几何图层→美化主题。
  4. 图表选择原则:
    • 构成比例:饼图(仅少量类别);类别对比优先条形图
    • 两个连续变量关系:散点图
    • 时间趋势:折线图
    • 多组分布对比、识别异常值:箱线图
    • 单一连续变量分布:直方图
  5. 项目规范:所有图片统一存入output文件夹,方便写报告直接引用。

课后思考

  1. 读取disaster_data_cleaned.xlsx,按disaster_type分组,计算每种灾害的平均经济损失,用kable输出汇总表。
  2. 绘制箱线图:对比不同severity_level严重等级下伤亡人数casualties。
  3. 思考题:
    • 1)饼图的局限性是什么?什么场景优先用条形图替代饼图?
    • 2)ggplot2图层化绘图是什么意思?