R: R ggplot2 入门
最后更新:2026-08-26
上一课学了基础 R 绘图——快但丑。这一课进入 R 真正"杀手锏"——ggplot2。它由 Hadley Wickham 设计,Grammar of Graphics 语法让画图变成"搭积木",默认出版级美观,被 90% 的 R 学术论文使用。
读完这一课你就能用 ggplot2 画5 种核心图表,并理解"图层叠加"的核心理念。
1. 你将学到
- ggplot2 核心:Grammar of Graphics 语法
- ggplot() + aes() + geom_xxx() 三件套
- 5 种基础 geom:point / line / bar / histogram / boxplot
- 美学映射 aes(x, y, color, size, shape)
- labs() 标签 + theme_minimal() 主题
- ggplot2 vs 基础 R 的核心差异
2. 一个数据可视化的故事
(1) 痛点:基础 R 太丑
Bob用基础 R 画了 4 城市销售趋势图:
plot(1:4, sales, type = "b")
manager看了一眼:"这图能印到报告里吗?"
(2) ggplot2 的解法
library(ggplot2)
ggplot(sales_df, aes(x = quarter, y = sales, color = city, group = city)) +
geom_line(linewidth = 1) +
geom_point(size = 3) +
labs(title = "Q1-Q4 销售趋势", x = "季度", y = "销售额") +
theme_minimal()
4 行代码 + 默认出版级美观。这就是 ggplot2 的力量。
3. Grammar of Graphics 核心理念
(1) 什么是 Grammar of Graphics?
graph TB
A[ggplot2 图表] --> B[数据 Data<br/>data]
A --> C[美学映射 Aesthetics<br/>aes]
A --> D[几何对象 Geometries<br/>geom_xxx]
A --> E[统计变换 Statistics<br/>stat_xxx]
A --> F[坐标系 Coordinate<br/>coord_xxx]
A --> G[分面 Facet<br/>facet_xxx]
A --> H[主题 Theme<br/>theme_xxx]
style A fill:#fff3cd
style B fill:#cce5ff
style C fill:#d4edda
style D fill:#f8d7da
style E fill:#e1d4ff
style F fill:#ffe1e1
style G fill:#e1ffe1
style H fill:#ffe1d4
一句话:ggplot2 把图分解为 7 层,每层独立、可替换。
(2) ggplot2 三大金刚
| 函数 | 作用 | 例子 |
|---|---|---|
ggplot() |
初始化(指定数据) | ggplot(df) |
aes() |
美学映射(变量 → 视觉) | aes(x, y, color) |
geom_xxx() |
几何对象(图层) | geom_point() |
最小例子:
library(ggplot2)
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point()
4. 第一个 ggplot2 图
(1) 5 步画图流程
library(ggplot2)
# 1. Prepare data
df <- data.frame(
x = 1:10,
y = (1:10) ^ 2
)
# 2. 初始化(数据)
p <- ggplot(df, aes(x = x, y = y))
# 3. 加图层
p <- p + geom_point()
# 4. 加标签
p <- p + labs(title = "平方曲线", x = "X", y = "Y")
# 5. 加主题
p <- p + theme_minimal()
# 6. 打印
print(p)
或者用 + 一气呵成:
ggplot(df, aes(x = x, y = y)) +
geom_point() +
labs(title = "平方曲线", x = "X", y = "Y") +
theme_minimal()
+ 串联图层,类似 |> 串联数据操作。
(2) 输出到文件
# 保存为 PNG
ggsave("myplot.png", width = 8, height = 6, dpi = 300)
# 保存为 PDF(论文用)
ggsave("myplot.pdf", width = 8, height = 6)
# 自定义大小
ggsave("myplot.png", width = 10, height = 8, units = "cm", dpi = 300)
5. 美学映射 aes()
(1) 常用映射
| 映射 | 作用 | 例子 |
|---|---|---|
x, y |
X / Y 坐标 | aes(x = age, y = height) |
color / colour |
颜色(外置) | aes(color = gender) |
fill |
填充色(柱状/面积) | aes(fill = region) |
size |
点大小 | aes(size = amount) |
shape |
点形状 | aes(shape = category) |
alpha |
透明度 | aes(alpha = weight) |
linetype |
线型 | aes(linetype = type) |
(2) 实战:3 维映射
# 颜色 + 大小 + 形状 3 个维度
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl), size = hp)) +
geom_point(alpha = 0.7) +
labs(title = "汽车重量 vs 油耗", color = "气缸数", size = "马力")
(3) ⚠️ aes() 内 vs 外
# aes() 内:映射到变量(每点不同颜色)
ggplot(df, aes(x, y, color = group)) + geom_point()
# → 不同 group 显示不同颜色
# aes() 外:固定值(所有点同色)
ggplot(df, aes(x, y)) + geom_point(color = "red")
# → 所有点都是红色
color = "red" 在 aes() 内会被解释为"red 列"(如果数据有 red 列)。固定颜色必须放在 aes() 外。
6. 5 种基础 geom
(1) geom_point() 散点图
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
geom_point(size = 3) +
labs(title = "汽车重量 vs 油耗", x = "重量", y = "油耗")
(2) geom_line() 折线图
df <- data.frame(
x = 1:10,
y = c(2, 4, 3, 5, 4, 6, 5, 7, 6, 8),
group = "A"
)
ggplot(df, aes(x = x, y = y)) +
geom_line(color = "blue", linewidth = 1) +
geom_point(color = "blue", size = 3)
(3) geom_bar() 柱状图
# 数据
df <- data.frame(
city = c("北京", "上海", "广州", "深圳"),
sales = c(4800, 7000, 3900, 4500)
)
# 柱状图
ggplot(df, aes(x = city, y = sales, fill = city)) +
geom_bar(stat = "identity") + # identity = 用 y 值
labs(title = "各城市总销售", x = "城市", y = "销售额") +
theme_minimal()
geom_bar(stat = "identity") 用数据的 y 值;geom_bar() 默认统计计数。
(4) geom_histogram() 直方图
# 1000 个正态分布随机数
data <- data.frame(x = rnorm(1000, mean = 100, sd = 15))
ggplot(data, aes(x = x)) +
geom_histogram(bins = 30, fill = "skyblue", color = "white") +
labs(title = "正态分布", x = "值", y = "频次")
(5) geom_boxplot() 箱线图
# 4 班考试成绩
scores <- data.frame(
class = rep(c("1班", "2班", "3班", "4班"), each = 10),
score = c(rnorm(10, 80, 8), rnorm(10, 75, 10),
rnorm(10, 85, 7), rnorm(10, 70, 12))
)
ggplot(scores, aes(x = class, y = score, fill = class)) +
geom_boxplot() +
labs(title = "4 班成绩分布", x = "班级", y = "分数")
7. 标签与主题
(1) labs() 标签
ggplot(df, aes(x, y)) +
geom_point() +
labs(
title = "主标题",
subtitle = "副标题",
caption = "数据来源:xxx",
x = "X 轴",
y = "Y 轴",
color = "颜色映射",
fill = "填充映射"
)
(2) 主题 theme_xxx()
| 主题 | 风格 |
|---|---|
theme_minimal() |
极简(最常用) |
theme_bw() |
黑白 |
theme_classic() |
经典(学术) |
theme_void() |
空白 |
theme_light() |
浅色 |
theme_dark() |
深色 |
# 对比 4 种主题
p <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
(p + theme_minimal()) # 默认推荐
(p + theme_bw())
(p + theme_classic())
(p + theme_void())
(3) 主题微调
p + theme(
plot.title = element_text(size = 16, face = "bold"),
axis.text = element_text(size = 10),
legend.position = "bottom" # 图例位置
)
8. 颜色与映射
(1) 离散颜色
# 1. 内置调色板
p + scale_color_brewer(palette = "Set1") # 经典
p + scale_color_brewer(palette = "Dark2") # 深色
p + scale_color_brewer(palette = "Pastel1") # 浅色
# 2. 手动指定
p + scale_color_manual(values = c("red", "blue", "green"))
# 3. rainbow
p + scale_color_manual(values = rainbow(4))
(2) 连续颜色
# 渐变色(用于连续变量)
p + scale_color_gradient(low = "blue", high = "red")
p + scale_color_viridis_c() # viridis(推荐)
p + scale_color_distiller(palette = "RdYlBu")
9. 完整示例:4 城市销售多图
下面是一个完整工作流示例,把本课所有知识串起来。
▶ 示例:4 城市销售 5 图合集
# ============================================
# 4 城市销售 5 图合集
# 功能:用 ggplot2 画 5 张出版级图表
# ============================================
library(ggplot2)
library(dplyr)
library(tidyr)
# 1. Prepare data
sales_wide <- tibble(
city = c("北京", "上海", "广州", "深圳"),
Q1 = c(1000, 1500, 800, 1200),
Q2 = c(1200, 1800, 900, 1400),
Q3 = c(1100, 1700, 1000, 1300),
Q4 = c(1500, 2000, 1200, 1600)
)
sales_long <- sales_wide |>
pivot_longer(cols = -city, names_to = "quarter", values_to = "sales")
cat("=== 销售数据 ===\n")
print(sales_long)
# 2. 图 1:折线趋势(按城市)
p1 <- ggplot(sales_long, aes(x = quarter, y = sales, color = city, group = city)) +
geom_line(linewidth = 1) +
geom_point(size = 3) +
labs(title = "Q1-Q4 销售趋势", x = "季度", y = "销售额", color = "城市") +
scale_color_brewer(palette = "Set1") +
theme_minimal()
print(p1)
# 3. 图 2:总销售柱状图
total_sales <- sales_wide |>
mutate(total = Q1 + Q2 + Q3 + Q4) |>
select(city, total)
p2 <- ggplot(total_sales, aes(x = reorder(city, total), y = total, fill = city)) +
geom_bar(stat = "identity") +
coord_flip() + # 横向
labs(title = "各城市总销售", x = "城市", y = "总销售额") +
theme_minimal() +
theme(legend.position = "none")
print(p2)
# 4. 图 3:占比饼图(geom_bar + coord_polar)
p3 <- ggplot(total_sales, aes(x = "", y = total, fill = city)) +
geom_bar(stat = "identity", width = 1) +
coord_polar(theta = "y") + # 极坐标(饼图)
labs(title = "销售占比") +
theme_void() +
scale_fill_brewer(palette = "Set2")
print(p3)
# 5. 图 4:直方图(所有销售数据)
p4 <- ggplot(sales_long, aes(x = sales, fill = city)) +
geom_histogram(bins = 10, alpha = 0.7, position = "identity") +
labs(title = "销售额分布", x = "销售额", y = "频次") +
scale_fill_brewer(palette = "Set1") +
theme_minimal()
print(p4)
# 6. 图 5:箱线图(按城市)
p5 <- ggplot(sales_long, aes(x = city, y = sales, fill = city)) +
geom_boxplot() +
labs(title = "各城市销售分布", x = "城市", y = "销售额") +
theme_minimal() +
theme(legend.position = "none")
print(p5)
# 7. 保存所有图
ggsave("01_trend.png", p1, width = 8, height = 6, dpi = 300)
ggsave("02_total.png", p2, width = 8, height = 6, dpi = 300)
ggsave("03_pie.png", p3, width = 8, height = 6, dpi = 300)
ggsave("04_hist.png", p4, width = 8, height = 6, dpi = 300)
ggsave("05_box.png", p5, width = 8, height = 6, dpi = 300)
cat("\n=== 5 张图已保存 ===\n")
cat(" 01_trend.png - 折线趋势\n")
cat(" 02_total.png - 柱状对比\n")
cat(" 03_pie.png - 占比饼图\n")
cat(" 04_hist.png - 分布直方图\n")
cat(" 05_box.png - 城市箱线\n")
预期输出:5 张出版级图表(默认 theme_minimal 已够美)。
❓ 常见问题
geom_bar() 和 geom_col() 区别?geom_bar(stat = "identity") 用 y 值;geom_col() 是它的语法糖(更简洁)。geom_bar() 默认统计计数(不需要 y)。geom_text() / geom_label():ggplot(df, aes(x, y)) + geom_point() +
geom_text(aes(label = name), vjust = -0.5) # 数据点上方加标签
theme(legend.position = "none")。ggsave("plot.png", dpi = 300, width = 8, height = 6),dpi=300 是印刷标准。📖 小节
- ggplot2 基于 Grammar of Graphics,把图分解为 7 层(数据/美学/几何/统计/坐标/分面/主题)
- 三大金刚:
ggplot()(数据) +aes()(映射) +geom_xxx()(图层) - 5 种基础 geom:point(散点)/ line(折线)/ bar(柱状)/ histogram(直方图)/ boxplot(箱线)
- 美学映射:
colorfillsizeshapealphalinetype aes()内 = 变量映射;aes()外 = 固定值- 主题用
theme_minimal()theme_bw()等;调色板用scale_color_brewer(palette = "Set1") ggsave()保存图(dpi = 300 印刷标准)- ggplot2 默认出版级美观——所有专业 R 报告/论文都用它
📝 作业
-
基础题:构造 1 个数据框(10 行 2 列 x/y),用
ggplot + geom_point + labs + theme_minimal画散点图,验证aes()内color = "red"和外color = "red"的差异。 -
基础题:用
mtcars数据集画散点图(wtvsmpg),按cyl着色(color = factor(cyl)),加labs()标题和主题。 -
基础题:构造 1 个数据框(5 个城市 + 销售额),用
geom_col()画柱状图,按reorder(city, sales)排序,验证排序效果。 -
进阶题:模拟 4 班 × 30 学生的成绩,用
geom_boxplot()画 4 班成绩分布图,加上fill = class填充色 +theme_minimal()主题。 -
挑战题:用
mtcars综合画 6 图(par(mfrow)风格 + ggplot2):① wt vs mpg 散点 ② wt 分布直方图 ③ mpg 箱线图 ④ 按 cyl 分面的 wt vs mpg ⑤ 颜色映射的散点 ⑥ 完整主题美化。把 6 张图保存为 PDF。