R: R 主题与美化

最后更新:2026-08-26

ggplot2 默认 theme_gray() 主题已经够好,但要"出版级"图表,必须精修主题。这一课我们学 ggplot2 主题系统:从 8 种内置主题、theme() 细调、labs/scale、到 ggsave 高清输出。

读完这一课你能把任何 ggplot2 图调整到"发表级"——文字、颜色、图例、边距、坐标轴全可控。

1. 你将学到



2. 一个"专业图表"的故事

(1) 痛点:默认图太"学生气"

Bob用 ggplot2 画了图,manager说:"这是给投资人的报告,太朴素了。"

(2) ggplot2 美化方案

R
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) 实战对比

R
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

R
# 全局设置字体大小
p + theme_minimal(base_size = 14)

# 全局设置字体
p + theme_minimal(base_family = "SimHei")  # 中文


4. theme() 元素树

(1) 元素结构

100%
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() 文字参数

R
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() 线条参数

R
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() 矩形参数

R
theme(
  panel.background = element_rect(
    fill = "lightyellow",
    color = "black",
    linewidth = 0.5
  ),
  plot.background = element_rect(fill = "white")
)

(6) element_blank() 隐藏

R
theme(
  panel.grid = element_blank(),         # 隐藏所有网格
  axis.ticks = element_blank(),         # 隐藏刻度
  legend.position = "none"              # 隐藏图例(注意:不是 element_blank)
)


5. 常用主题微调

(1) 文字大小

R
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) 网格线

R
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) 图例位置

R
# 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) 坐标轴

R
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) 所有标签

R
p + labs(
  title = "主标题",
  subtitle = "副标题",
  caption = "数据来源",
  x = "X 轴",
  y = "Y 轴",
  color = "颜色映射",  # 对应 aes(color)
  fill = "填充映射",   # 对应 aes(fill)
  shape = "形状映射",
  size = "大小映射"
)

(2) 数学公式标签

R
# 公式用 quote() 或 expression
p + labs(
  x = quote(x[i]),
  y = expression(paste("浓度 (", mu, "g/mL)"))
)


7. scale_color / scale_fill 颜色

(1) 离散颜色(分类变量)

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

R
# 渐变
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) 实战:专业配色

R
# 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 种主题

R
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 商业主题

R
install.packages("hrbrthemes")
library(hrbrthemes)

p + theme_ipsum()         # ipsum 排版风格
p + theme_ft_rc()         # 金融时报

(3) 自定义主题(一次定义,全局使用)

R
# 自定义主题
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 个核心参数

R
ggsave(
  filename,            # 文件名
  plot = last_plot(),   # 图对象(默认最后一张)
  width = 8,            # 宽
  height = 6,           # 高
  units = "in",         # 单位 in/cm/mm
  dpi = 300             # 分辨率
)

(2) 实战

R
# 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 .pdf 论文(矢量)
SVG .svg 网络矢量图
JPEG .jpg 照片(不推荐用于图)
TIFF .tiff 印刷
R
# 矢量图(清晰无限放大)
ggsave("vector.svg", width = 8, height = 6)
ggsave("vector.pdf", width = 8, height = 6)


10. 完整示例:出版级专业图表

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

▶ 示例:4 城市销售出版级图表

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

预期输出:3 张出版级图组合(折线 + 柱状 + 堆叠),含中英文标题、专业配色、清晰标签。


❓ 常见问题

Q theme_minimal 和 theme_bw 区别?
A theme_minimal 极简(白底、淡灰网格),theme_bw 黑白(有边框、明显网格)。
Q 怎么去掉次要网格?
A theme(panel.grid.minor = element_blank())。让图更简洁。
Q 图例放哪好?
A 看图复杂度。1-2 个变量用 bottomtop;4+ 变量用 right;饼图内嵌可用 c(0.5, 0.5) 坐标定位。
Q 300 dpi 还是 150 dpi?
A 印刷 300 dpi,屏幕 96-150 dpi,PDF 矢量无需 dpi。ggsave 默认 300 dpi。
Q 怎么换中文字体?
A theme_minimal(base_family = "SimHei")(Windows)或 "PingFang SC"(macOS)或 "WenQuanYi Zen Hei"(Linux)。

📖 小节


📝 作业

  1. 基础题:用 mtcarswt vs mpg 散点图,分别用 theme_minimal theme_bw theme_classic theme_void 4 种主题,截图对比差异。

  2. 基础题:画 1 个柱状图,用 theme() 微调:① 标题加粗 16号 ② 隐藏次要网格 ③ 图例放底部 ④ Y 轴标题距离轴线 10px。

  3. 基础题:用 scale_color_brewer(palette = "Set1")scale_color_brewer(palette = "Set2") 画同一张图,截图对比颜色差异。

  4. 进阶题:模拟 4 城市销售数据,完整出版级图表定制:① 自定义主题 ② 完整 labs 标签 ③ 专业配色 ④ 高清保存。把过程和结果截图保存。

  5. 挑战题:用 ggthemestheme_economist()theme_wsj() 画 1 张图,对比默认 theme_minimal() 的差异。把图保存为 PNG 和 PDF 两种格式(PDF 用 Cairo PDF 设备以支持中文)。

Web-Tutorial.com

Web-Tutorial 技术团队

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

100%

🙏 帮我们做得更好

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

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