R: R 主题与美化
最后更新:2026-08-26
ggplot2 默认
theme_gray()主题已经够好,但要"出版级"图表,必须精修主题。这一课我们学 ggplot2 主题系统:从 8 种内置主题、theme() 细调、labs/scale、到 ggsave 高清输出。
读完这一课你能把任何 ggplot2 图调整到"发表级"——文字、颜色、图例、边距、坐标轴全可控。
1. 你将学到
- 8 种 ggplot2 内置主题
- theme() 元素树(element_text/line/rect)
- labs() 完整标签
- scale_color/fill 颜色映射
- 主题模板(ggthemes / hrbrthemes)
- ggsave() 高清保存
- 实战:出版级图表定制
2. 一个"专业图表"的故事
(1) 痛点:默认图太"学生气"
Bob用 ggplot2 画了图,manager说:"这是给投资人的报告,太朴素了。"
(2) ggplot2 美化方案
library(ggplot2)
library(hrbrthemes) # 专业主题包
p <- ggplot(sales, aes(x = quarter, y = sales, color = city, group = city)) +
geom_line(linewidth = 1.2) +
geom_point(size = 3) +
scale_color_brewer(palette = "Set1") +
labs(
title = "2024 年 Q1-Q4 销售趋势",
subtitle = "数据来源:销售系统 | 制图:分析师团队",
caption = "单位:万元",
x = "季度", y = "销售额(万元)",
color = "城市"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 16),
plot.subtitle = element_text(color = "gray50"),
legend.position = "top",
panel.grid.minor = element_blank()
)
ggsave("professional_report.png", p, width = 10, height = 6, dpi = 300)
3 行代码 → 出版级图表。
3. 8 种内置主题
(1) 主题对比
| 主题 | 风格 | 适用场景 |
|---|---|---|
theme_gray() |
默认(灰底) | 通用 |
theme_bw() |
黑白 | 学术论文 |
theme_minimal() |
极简(最常用) | 报告/演示 |
theme_classic() |
经典(无网格) | 学术 |
theme_void() |
空白 | 地图/示意图 |
theme_light() |
浅色背景 | 演示 |
theme_dark() |
深色背景 | 暗色主题 |
theme_test() |
测试用 | 调试 |
(2) 实战对比
p <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
p + theme_gray() # 默认
p + theme_minimal() # 极简
p + theme_bw() # 黑白
p + theme_classic() # 经典
p + theme_light() # 浅色
p + theme_dark() # 深色
p + theme_void() # 空白
(3) base_size / base_family
# 全局设置字体大小
p + theme_minimal(base_size = 14)
# 全局设置字体
p + theme_minimal(base_family = "SimHei") # 中文
4. theme() 元素树
(1) 元素结构
graph TB
A[theme 元素] --> B[plot 整图]
A --> C[axis 坐标轴]
A --> D[legend 图例]
A --> E[panel 面板]
A --> F[strip 分面标签]
A --> G[title 标题]
B --> H[plot.title]
B --> I[plot.background]
C --> J[axis.title]
C --> K[axis.text]
C --> L[axis.line]
D --> M[legend.title]
D --> N[legend.text]
D --> O[legend.position]
E --> P[panel.background]
E --> Q[panel.grid]
F --> R[strip.text]
F --> S[strip.background]
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
(2) 4 种 element_ 函数
| 函数 | 作用 | 用于 |
|---|---|---|
element_text() |
文字 | 标题、轴标签、刻度文字 |
element_line() |
线条 | 网格线、坐标轴线 |
element_rect() |
矩形 | 背景、边框、面板 |
element_blank() |
隐藏 | 任何元素 |
(3) element_text() 文字参数
theme(
plot.title = element_text(
size = 16, # 字号
face = "bold", # 加粗/斜体(plain/bold/italic/bold.italic)
color = "blue", # 颜色
family = "SimHei",# 字体
hjust = 0.5, # 水平对齐(0=左, 1=右, 0.5=中)
vjust = 0.5 # 垂直对齐
)
)
(4) element_line() 线条参数
theme(
panel.grid.major = element_line(
color = "gray80",
linewidth = 0.5,
linetype = "dashed" # solid/dashed/dotted
),
panel.grid.minor = element_blank(), # 隐藏次要网格
axis.line = element_line(color = "black", linewidth = 0.8)
)
(5) element_rect() 矩形参数
theme(
panel.background = element_rect(
fill = "lightyellow",
color = "black",
linewidth = 0.5
),
plot.background = element_rect(fill = "white")
)
(6) element_blank() 隐藏
theme(
panel.grid = element_blank(), # 隐藏所有网格
axis.ticks = element_blank(), # 隐藏刻度
legend.position = "none" # 隐藏图例(注意:不是 element_blank)
)
5. 常用主题微调
(1) 文字大小
p + theme(
plot.title = element_text(size = 18, face = "bold"),
plot.subtitle = element_text(size = 12, color = "gray50"),
axis.title = element_text(size = 12),
axis.text = element_text(size = 10),
legend.title = element_text(size = 11),
legend.text = element_text(size = 10)
)
(2) 网格线
p + theme(
panel.grid.major = element_line(color = "gray90"),
panel.grid.minor = element_blank(), # 隐藏次要网格(更简洁)
panel.border = element_rect(color = "black", fill = NA, linewidth = 0.5)
)
(3) 图例位置
# 4 个角落
p + theme(legend.position = "top")
p + theme(legend.position = "bottom")
p + theme(legend.position = "left")
p + theme(legend.position = "right")
# 隐藏
p + theme(legend.position = "none")
# 坐标定位(图中)
p + theme(legend.position = c(0.8, 0.2)) # 80% x, 20% y
(4) 坐标轴
p + theme(
axis.line = element_line(color = "black", linewidth = 0.8),
axis.ticks = element_line(color = "black"),
axis.title.x = element_text(margin = margin(t = 10)), # X 轴标题下移
axis.title.y = element_text(margin = margin(r = 10)) # Y 轴标题左移
)
6. labs() 完整标签
(1) 所有标签
p + labs(
title = "主标题",
subtitle = "副标题",
caption = "数据来源",
x = "X 轴",
y = "Y 轴",
color = "颜色映射", # 对应 aes(color)
fill = "填充映射", # 对应 aes(fill)
shape = "形状映射",
size = "大小映射"
)
(2) 数学公式标签
# 公式用 quote() 或 expression
p + labs(
x = quote(x[i]),
y = expression(paste("浓度 (", mu, "g/mL)"))
)
7. scale_color / scale_fill 颜色
(1) 离散颜色(分类变量)
# 1. 手动指定
p + scale_color_manual(values = c("red", "blue", "green", "orange"))
# 2. ColorBrewer 调色板
p + scale_color_brewer(palette = "Set1") # 经典 9 色
p + scale_color_brewer(palette = "Set2") # 柔和 8 色
p + scale_color_brewer(palette = "Dark2") # 深色 8 色
p + scale_color_brewer(palette = "Pastel1") # 浅色 9 色
# 3. viridis(色盲友好,推荐)
library(viridis)
p + scale_color_viridis_d() # 离散
p + scale_color_viridis_c() # 连续
(2) 连续颜色(连续变量)
# 渐变
p + scale_color_gradient(low = "blue", high = "red")
p + scale_color_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0)
p + scale_color_gradientn(colours = rainbow(7))
(3) 实战:专业配色
# 4 类分类
p + scale_color_brewer(palette = "Set1")
# 8 类分类
p + scale_color_brewer(palette = "Set2")
# 色盲友好
p + scale_color_viridis_d()
# 学术/严肃
p + scale_color_manual(values = c("#1f77b4", "#ff7f0e", "#2ca02c"))
8. 专业主题包
(1) ggthemes 8 种主题
install.packages("ggthemes")
library(ggthemes)
p + theme_economist() # 经济学人
p + theme_wsj() # 华尔街日报
p + theme_fivethirtyeight() # FiveThirtyEight
p + theme_hc() # Highcharts
p + theme_tufte() # Tufte 极简
p + theme_stata() # Stata
p + theme_solarized() # Solarized
p + theme_excel() # Excel 风格
(2) hrbrthemes 商业主题
install.packages("hrbrthemes")
library(hrbrthemes)
p + theme_ipsum() # ipsum 排版风格
p + theme_ft_rc() # 金融时报
(3) 自定义主题(一次定义,全局使用)
# 自定义主题
my_theme <- theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 14),
panel.grid.minor = element_blank(),
legend.position = "bottom"
)
# 全局使用
ggplot(df, aes(x, y)) + geom_point() + my_theme
9. ggsave() 高清保存
(1) 4 个核心参数
ggsave(
filename, # 文件名
plot = last_plot(), # 图对象(默认最后一张)
width = 8, # 宽
height = 6, # 高
units = "in", # 单位 in/cm/mm
dpi = 300 # 分辨率
)
(2) 实战
# PNG 印刷标准(300 dpi)
ggsave("report.png", width = 8, height = 6, dpi = 300)
# 高清屏幕(150 dpi)
ggsave("screen.png", width = 1920, height = 1080, units = "px", dpi = 150)
# PDF 论文用(矢量)
ggsave("paper.pdf", width = 8, height = 6)
# 中文标题(避免乱码)
ggsave("中文.png", width = 8, height = 6, dpi = 300)
(3) 各种格式
| 格式 | 后缀 | 用途 |
|---|---|---|
| PNG | .png |
网络/文档(最常用) |
.pdf |
论文(矢量) | |
| SVG | .svg |
网络矢量图 |
| JPEG | .jpg |
照片(不推荐用于图) |
| TIFF | .tiff |
印刷 |
# 矢量图(清晰无限放大)
ggsave("vector.svg", width = 8, height = 6)
ggsave("vector.pdf", width = 8, height = 6)
10. 完整示例:出版级专业图表
下面是一个完整工作流示例,把本课所有主题知识串起来。
▶ 示例:4 城市销售出版级图表
# ============================================
# 4 城市销售出版级图表
# 功能:从默认到专业级别的完整美化
# ============================================
library(ggplot2)
library(dplyr)
library(tidyr)
library(patchwork)
library(hrbrthemes)
# 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") |>
mutate(
total = sum(sales),
pct = round(sales / total * 100, 1)
)
# 2. 自定义主题
my_theme <- theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 16, hjust = 0,
margin = margin(b = 5)),
plot.subtitle = element_text(color = "gray40", size = 11,
margin = margin(b = 15)),
plot.caption = element_text(color = "gray50", size = 9,
hjust = 1, margin = margin(t = 10)),
axis.title = element_text(size = 11),
axis.text = element_text(size = 10, color = "gray30"),
panel.grid.major = element_line(color = "gray90", linewidth = 0.3),
panel.grid.minor = element_blank(),
legend.position = "top",
legend.title = element_text(size = 11),
legend.text = element_text(size = 10),
plot.background = element_rect(fill = "white", color = NA)
)
# 3. 图 1:折线趋势(专业版)
p1 <- ggplot(sales_long, aes(x = quarter, y = sales, color = city, group = city)) +
geom_line(linewidth = 1.2) +
geom_point(size = 3.5, fill = "white", shape = 21, stroke = 1.5) +
scale_color_brewer(palette = "Set1", name = "城市") +
scale_y_continuous(labels = scales::comma, expand = expansion(mult = 0.1)) +
labs(
title = "2024 年季度销售趋势",
subtitle = "4 个一线城市 4 季度销售数据",
caption = "数据来源:销售系统 | 制图:分析师团队",
x = NULL, y = "销售额(万元)"
) +
my_theme
# 4. 图 2:柱状对比(专业版)
total_sales <- sales_wide |>
mutate(total = Q1 + Q2 + Q3 + Q4) |>
arrange(desc(total)) |>
mutate(city = factor(city, levels = city))
p2 <- ggplot(total_sales, aes(x = city, y = total, fill = city)) +
geom_col(width = 0.7) +
geom_text(aes(label = scales::comma(total)), vjust = -0.5, size = 4) +
scale_fill_brewer(palette = "Set1", guide = "none") +
scale_y_continuous(labels = scales::comma,
expand = expansion(mult = c(0, 0.15))) +
labs(
title = "各城市年度总销售",
subtitle = "按总销售额降序排列",
x = NULL, y = "总销售额(万元)"
) +
my_theme
# 5. 图 3:堆叠柱状(专业版)
sales_long_ordered <- sales_long |>
left_join(total_sales |> select(city, total), by = "city") |>
mutate(city = factor(city, levels = total_sales$city))
p3 <- ggplot(sales_long_ordered, aes(x = city, y = sales, fill = quarter)) +
geom_col(position = "stack", width = 0.7) +
scale_fill_brewer(palette = "YlGnBu", name = "季度") +
labs(
title = "各城市季度销售构成",
subtitle = "堆叠柱状图展示季度贡献",
x = NULL, y = "销售额(万元)"
) +
my_theme
# 6. 多图组合
combined <- (p1 / (p2 + p3)) +
plot_annotation(
title = "2024 年 4 城市销售综合分析",
theme = theme(plot.title = element_text(face = "bold", size = 18, hjust = 0.5))
)
print(combined)
# 7. 保存出版级图
ggsave("professional_report.png", combined,
width = 14, height = 12, dpi = 300)
ggsave("professional_report.pdf", combined,
width = 14, height = 12)
ggsave("professional_report.svg", combined,
width = 14, height = 12)
cat("\n=== 出版级图表已保存 ===\n")
cat(" - professional_report.png(PNG 300dpi)\n")
cat(" - professional_report.pdf(PDF 矢量)\n")
cat(" - professional_report.svg(SVG 矢量)\n")
预期输出:3 张出版级图组合(折线 + 柱状 + 堆叠),含中英文标题、专业配色、清晰标签。
❓ 常见问题
theme_minimal 极简(白底、淡灰网格),theme_bw 黑白(有边框、明显网格)。theme(panel.grid.minor = element_blank())。让图更简洁。bottom 或 top;4+ 变量用 right;饼图内嵌可用 c(0.5, 0.5) 坐标定位。ggsave 默认 300 dpi。theme_minimal(base_family = "SimHei")(Windows)或 "PingFang SC"(macOS)或 "WenQuanYi Zen Hei"(Linux)。📖 小节
- ggplot2 有 8 种内置主题:
theme_graytheme_bwtheme_minimaltheme_classic等 theme()元素树分 7 个区域:plot/axis/legend/panel/strip/title- 4 种 element 函数:
element_text文字 /element_line线条 /element_rect矩形 /element_blank隐藏 - 主题微调核心:
plot.titleaxis.titlelegend.positionpanel.grid labs()设所有标签:titlesubtitlecaptionxycolorfill- 颜色:
scale_color_brewer/scale_color_viridis_d/scale_color_manual - 专业主题包:
ggthemes(经济学人/华尔街日报)/hrbrthemes(商业) ggsave("plot.png", dpi = 300)印刷标准;ggsave("plot.pdf")矢量- 出版级图表 = 主题 + 颜色 + 标签 + 字号 + ggsave 高分辨率
📝 作业
-
基础题:用
mtcars画wtvsmpg散点图,分别用theme_minimaltheme_bwtheme_classictheme_void4 种主题,截图对比差异。 -
基础题:画 1 个柱状图,用
theme()微调:① 标题加粗 16号 ② 隐藏次要网格 ③ 图例放底部 ④ Y 轴标题距离轴线 10px。 -
基础题:用
scale_color_brewer(palette = "Set1")和scale_color_brewer(palette = "Set2")画同一张图,截图对比颜色差异。 -
进阶题:模拟 4 城市销售数据,完整出版级图表定制:① 自定义主题 ② 完整 labs 标签 ③ 专业配色 ④ 高清保存。把过程和结果截图保存。
-
挑战题:用
ggthemes的theme_economist()或theme_wsj()画 1 张图,对比默认theme_minimal()的差异。把图保存为 PNG 和 PDF 两种格式(PDF 用 Cairo PDF 设备以支持中文)。