R: R ggplot2 入门

最后更新:2026-08-26

上一课学了基础 R 绘图——快但丑。这一课进入 R 真正"杀手锏"——ggplot2。它由 Hadley Wickham 设计,Grammar of Graphics 语法让画图变成"搭积木",默认出版级美观,被 90% 的 R 学术论文使用。

读完这一课你就能用 ggplot2 画5 种核心图表,并理解"图层叠加"的核心理念。

1. 你将学到



2. 一个数据可视化的故事

(1) 痛点:基础 R 太丑

Bob用基础 R 画了 4 城市销售趋势图:

R
plot(1:4, sales, type = "b")

manager看了一眼:"这图能印到报告里吗?"

(2) ggplot2 的解法

R
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?

100%
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()

最小例子

R
library(ggplot2)
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point()


4. 第一个 ggplot2 图

(1) 5 步画图流程

R
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)

或者用 + 一气呵成:

R
ggplot(df, aes(x = x, y = y)) +
  geom_point() +
  labs(title = "平方曲线", x = "X", y = "Y") +
  theme_minimal()
💡 提示ggplot2 用 + 串联图层,类似 |> 串联数据操作。

(2) 输出到文件

R
# 保存为 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 维映射

R
# 颜色 + 大小 + 形状 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 外

R
# 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() 散点图

R
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  labs(title = "汽车重量 vs 油耗", x = "重量", y = "油耗")

(2) geom_line() 折线图

R
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() 柱状图

R
# 数据
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() 直方图

R
# 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() 箱线图

R
# 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() 标签

R
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() 深色
R
# 对比 4 种主题
p <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
(p + theme_minimal())  # 默认推荐
(p + theme_bw())
(p + theme_classic())
(p + theme_void())

(3) 主题微调

R
p + theme(
  plot.title = element_text(size = 16, face = "bold"),
  axis.text = element_text(size = 10),
  legend.position = "bottom"  # 图例位置
)


8. 颜色与映射

(1) 离散颜色

R
# 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) 连续颜色

R
# 渐变色(用于连续变量)
p + scale_color_gradient(low = "blue", high = "red")
p + scale_color_viridis_c()   # viridis(推荐)
p + scale_color_distiller(palette = "RdYlBu")


9. 完整示例:4 城市销售多图

下面是一个完整工作流示例,把本课所有知识串起来。

▶ 示例:4 城市销售 5 图合集

R 📖 仅展示
# ============================================
# 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")
逻辑代码 61 行(超过 40 行限制,仅展示)

预期输出:5 张出版级图表(默认 theme_minimal 已够美)。


❓ 常见问题

Q geom_bar()geom_col() 区别?
A geom_bar(stat = "identity") 用 y 值;geom_col() 是它的语法糖(更简洁)。geom_bar() 默认统计计数(不需要 y)。
Q 怎么加注释?
Ageom_text() / geom_label()
R
ggplot(df, aes(x, y)) + geom_point() +
  geom_text(aes(label = name), vjust = -0.5)  # 数据点上方加标签
Q 图例怎么去掉?
A theme(legend.position = "none")
Q 怎么保存高清图?
A ggsave("plot.png", dpi = 300, width = 8, height = 6),dpi=300 是印刷标准。

📖 小节


📝 作业

  1. 基础题:构造 1 个数据框(10 行 2 列 x/y),用 ggplot + geom_point + labs + theme_minimal 画散点图,验证 aes()color = "red" 和外 color = "red" 的差异。

  2. 基础题:用 mtcars 数据集画散点图(wt vs mpg),按 cyl 着色(color = factor(cyl)),加 labs() 标题和主题。

  3. 基础题:构造 1 个数据框(5 个城市 + 销售额),用 geom_col() 画柱状图,按 reorder(city, sales) 排序,验证排序效果。

  4. 进阶题:模拟 4 班 × 30 学生的成绩,用 geom_boxplot() 画 4 班成绩分布图,加上 fill = class 填充色 + theme_minimal() 主题。

  5. 挑战题:用 mtcars 综合画 6 图(par(mfrow) 风格 + ggplot2):① wt vs mpg 散点 ② wt 分布直方图 ③ mpg 箱线图 ④ 按 cyl 分面的 wt vs mpg ⑤ 颜色映射的散点 ⑥ 完整主题美化。把 6 张图保存为 PDF。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏