未记录事实核验 · 状态:维护中
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 适用边界与维护说明
本文适用于具备R基础的读者,使用随机模拟数据练习10种图表、主题定制、分面、多图布局及交互式输出;ERP来源和2024年报告期均为假设情境。当前facet_grid示例只按季度分行,并未演示注释所称的两个变量行列分面;箱线图的“五数概括”表述也需注意,默认须端不必等于样本最小值和最大值。工具对比表仍含“完全可重复”“版本管理差”“需要额外工具”等未提供条件或依据的概括,不宜用于正式选型、容量规划或采购判断。中文输出依赖字体配置、网络访问及输出设备,设计原则文档仅为尚未提供的可选延伸。本次仅作结构及正文一致性审阅,未执行代码或进行外部事实核验。 已取消无法确定目标的可点击引用「待创建:数据可视化设计原则」,保留原提及;Missing link target: 待创建:数据可视化设计原则
R语言数据可视化——ggplot2深度实战
abstract 学习目标
- 深入理解ggplot2的图层语法(Grammar of Graphics)
- 掌握10种常见业务图表的实现方式
- 学会颜色方案与主题定制,输出商业可用图表
- 了解多图组合与交互式图表的实现路径
quote 核心洞察
ggplot2最重要的思想是:图表是数据属性到视觉属性的映射。当你想改一张图时,不要问"怎么改这个函数",而要问"我想把哪个数据属性映射到哪个视觉通道"。这个问题想清楚了,代码自然就写出来了。
一、图层语法:ggplot2的核心哲学
ggplot2基于Leland Wilkinson的《The Grammar of Graphics》,把图表分解为可组合的图层:
一张图 = 数据(data)
+ 映射(aes) — 数据变量 → 视觉通道(x轴/y轴/颜色/形状/大小)
+ 几何对象(geom) — 点/线/柱/面等视觉元素
+ 统计变换(stat) — 计数/平滑/分箱等数据变换
+ 坐标系(coord) — 直角/极坐标/地图投影
+ 标度(scale) — 颜色/大小/坐标轴的刻度控制
+ 分面(facet) — 按变量分成多个子图
+ 主题(theme) — 非数据元素的视觉风格
最小可行的ggplot2代码:
library(ggplot2)
library(dplyr)
# 生成示例数据
set.seed(42)
sales_data <- tibble(
month = rep(1:12, 3),
city = rep(c("北京","上海","广州"), each = 12),
sales = c(
rnorm(12, 100, 15),
rnorm(12, 120, 20),
rnorm(12, 80, 10)
) %>% abs() %>% round()
)
# 三行画出一张折线图
ggplot(sales_data, aes(x = month, y = sales, color = city)) +
geom_line() +
geom_point()
注意+号:ggplot2用+叠加图层,而不是tidyverse的%>%管道符。
二、10种常见业务图表实现
2.1 分布类图表
直方图:查看数值分布
ggplot(sales_data, aes(x = sales)) +
geom_histogram(bins = 20, fill = "#2196F3", color = "white", alpha = 0.8) +
geom_vline(aes(xintercept = mean(sales)), color = "red", linetype = "dashed", size = 1) +
labs(title = "月度销售额分布", x = "销售额(万元)", y = "频次") +
theme_minimal()
密度图:平滑分布曲线(适合多组对比)
ggplot(sales_data, aes(x = sales, fill = city)) +
geom_density(alpha = 0.4) +
scale_fill_manual(values = c("#E91E63","#2196F3","#4CAF50")) +
labs(title = "各城市销售额分布对比", fill = "城市") +
theme_minimal()
箱线图:查看分布的五数概括
ggplot(sales_data, aes(x = city, y = sales, fill = city)) +
geom_boxplot(alpha = 0.7, outlier.color = "red") +
geom_jitter(width = 0.2, alpha = 0.3, size = 1) + # 叠加散点显示原始数据
stat_summary(fun = mean, geom = "point", shape = 23, size = 3, fill = "white") + # 均值点
scale_fill_brewer(palette = "Set2") +
labs(title = "各城市销售额箱线图", x = "城市", y = "销售额") +
theme_minimal() +
theme(legend.position = "none")
2.2 比较类图表
柱状图:类别对比
# 汇总数据
city_total <- sales_data %>%
group_by(city) %>%
summarise(total = sum(sales))
# 基础柱状图
ggplot(city_total, aes(x = reorder(city, total), y = total, fill = city)) +
geom_col(width = 0.6) +
geom_text(aes(label = total), hjust = -0.2, size = 4) + # 标签
coord_flip() + # 横向柱状图
scale_fill_brewer(palette = "Blues", direction = -1) +
labs(title = "各城市年度销售总额", x = NULL, y = "销售额") +
theme_minimal() +
theme(legend.position = "none")
分组柱状图:多维对比
# 按季度分组
sales_data %>%
mutate(quarter = paste0("Q", ceiling(month / 3))) %>%
group_by(city, quarter) %>%
summarise(total = sum(sales), .groups = "drop") %>%
ggplot(aes(x = quarter, y = total, fill = city)) +
geom_col(position = "dodge", width = 0.7) +
scale_fill_brewer(palette = "Set1") +
labs(title = "各城市季度销售对比", x = "季度", y = "销售额", fill = "城市") +
theme_minimal()
堆叠柱状图:展示构成
sales_data %>%
mutate(quarter = paste0("Q", ceiling(month / 3))) %>%
group_by(city, quarter) %>%
summarise(total = sum(sales), .groups = "drop") %>%
ggplot(aes(x = quarter, y = total, fill = city)) +
geom_col(position = "fill") + # position="fill"变为100%堆叠
scale_y_continuous(labels = scales::percent) +
scale_fill_brewer(palette = "Set2") +
labs(title = "各季度城市销售占比", y = "占比") +
theme_minimal()
2.3 趋势类图表
折线图:时间序列趋势
ggplot(sales_data, aes(x = month, y = sales, color = city, group = city)) +
geom_line(size = 1.2) +
geom_point(size = 3) +
geom_smooth(method = "loess", se = TRUE, alpha = 0.15) + # 添加趋势线
scale_x_continuous(breaks = 1:12, labels = paste0(1:12, "月")) +
scale_color_manual(values = c("#E91E63","#2196F3","#FF9800")) +
labs(title = "各城市月度销售趋势", x = "月份", y = "销售额", color = "城市") +
theme_minimal()
堆叠面积图:展示各月总量与城市构成(非跨月累计)
ggplot(sales_data, aes(x = month, y = sales, fill = city)) +
geom_area(alpha = 0.6, position = "stack") +
scale_fill_brewer(palette = "Set2") +
labs(title = "各城市月度销售构成与合计", fill = "城市") +
theme_minimal()
2.4 相关类图表
散点图:两变量关系
# 生成用户行为数据
set.seed(100)
user_behavior <- tibble(
session_duration = rnorm(500, 8, 3) %>% abs(),
pages_viewed = round(rnorm(500, 5, 2) %>% abs()),
converted = rbinom(500, 1, prob = plogis(-1 + 0.2 * session_duration + 0.1 * pages_viewed)),
user_type = sample(c("新用户","老用户"), 500, replace = TRUE)
)
ggplot(user_behavior, aes(x = session_duration, y = pages_viewed,
color = factor(converted), size = converted)) +
geom_point(alpha = 0.5) +
geom_smooth(aes(group = 1), method = "lm", color = "gray40", se = FALSE) +
scale_color_manual(values = c("#9E9E9E","#E91E63"), labels = c("未转化","已转化")) +
scale_size_continuous(range = c(1, 4)) +
labs(title = "会话时长 vs 浏览页数", x = "会话时长(分钟)",
y = "浏览页数", color = "转化状态") +
theme_minimal() +
guides(size = "none")
热力图:矩阵数据
# 用户行为热力图
expand.grid(hour = 0:23, weekday = c("周一","周二","周三","周四","周五","周六","周日")) %>%
as_tibble() %>%
mutate(
activity = case_when(
weekday %in% c("周六","周日") & hour %in% 10:22 ~ rnorm(n(), 80, 15),
weekday %in% c("周一","周五") & hour %in% c(8,9,18,19) ~ rnorm(n(), 70, 10),
TRUE ~ rnorm(n(), 30, 10)
) %>% abs() %>% round()
) %>%
ggplot(aes(x = hour, y = weekday, fill = activity)) +
geom_tile(color = "white", size = 0.3) +
scale_fill_gradient2(low = "#FFFFFF", mid = "#FFC107", high = "#E91E63",
midpoint = 50, name = "活跃度") +
scale_x_continuous(breaks = seq(0, 23, 3), labels = paste0(seq(0, 23, 3), "时")) +
labs(title = "用户活跃时间热力图", x = "小时", y = NULL) +
theme_minimal() +
theme(panel.grid = element_blank())
三、颜色方案与主题定制
3.1 内置颜色体系
# RColorBrewer:经典配色;并非所有色板都色盲友好,应按色板及使用场景选择
library(RColorBrewer)
display.brewer.all() # 查看所有配色
# 定性色板(适合类别)
scale_color_brewer(palette = "Set1") # 最多9种鲜亮颜色
scale_color_brewer(palette = "Set2") # 8种柔和颜色
scale_color_brewer(palette = "Dark2") # 8种深色
# 序列色板(适合渐变数值)
scale_fill_distiller(palette = "Blues") # 蓝色系
scale_fill_distiller(palette = "YlOrRd") # 黄橙红渐变
# viridis:感知均匀,色盲友好,印刷友好
scale_fill_viridis_c() # 连续变量
scale_fill_viridis_d() # 离散变量
3.2 自定义品牌配色
# 定义公司品牌色
brand_colors <- c(
"主色" = "#1A73E8",
"辅色1" = "#34A853",
"辅色2" = "#FBBC04",
"辅色3" = "#EA4335"
)
# 在图中使用
scale_color_manual(values = brand_colors)
scale_fill_manual(values = unname(brand_colors))
3.3 主题定制
# 自定义商业报告主题
theme_business <- function() {
theme_minimal(base_family = "Noto Sans SC", base_size = 12) +
theme(
# 标题样式
plot.title = element_text(face = "bold", size = 14, color = "#1A1A2E"),
plot.subtitle = element_text(size = 11, color = "#666666", margin = margin(b = 10)),
plot.caption = element_text(size = 9, color = "#999999", hjust = 0),
# 坐标轴
axis.text = element_text(color = "#444444"),
axis.title = element_text(color = "#444444", size = 11),
axis.line = element_line(color = "#E0E0E0"),
# 网格线
panel.grid.major = element_line(color = "#F0F0F0"),
panel.grid.minor = element_blank(),
# 图例
legend.position = "bottom",
legend.title = element_text(face = "bold"),
# 背景
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "white", color = NA),
# 外边距
plot.margin = margin(20, 20, 20, 20)
)
}
# 应用自定义主题
ggplot(sales_data, aes(x = month, y = sales, color = city)) +
geom_line(size = 1.2) +
labs(
title = "月度销售趋势报告",
subtitle = "模拟情境:2024年各城市销售额月度对比",
caption = "数据来源:随机生成的模拟数据;假设业务场景为内部ERP销售报告",
x = "月份", y = "销售额(万元)", color = "城市"
) +
theme_business()
四、分面:一条命令出多张子图
# facet_wrap:按单个变量分面
ggplot(sales_data, aes(x = month, y = sales)) +
geom_line(color = "#2196F3", size = 1) +
geom_area(fill = "#2196F3", alpha = 0.15) +
facet_wrap(~ city, ncol = 3) + # 按城市分面,每行3图
scale_x_continuous(breaks = c(3, 6, 9, 12)) +
labs(title = "各城市月度销售趋势") +
theme_minimal()
# facet_grid:按两个变量分面(行列矩阵)
sales_data %>%
mutate(quarter = paste0("Q", ceiling(month / 3))) %>%
ggplot(aes(x = city, y = sales, fill = city)) +
geom_boxplot() +
facet_grid(rows = vars(quarter)) + # 按季度分行
theme_minimal() +
theme(legend.position = "none")
五、多图组合
library(patchwork)
p1 <- ggplot(city_total, aes(x = reorder(city, total), y = total)) +
geom_col(fill = "#2196F3") +
coord_flip() +
labs(title = "A) 城市总销售额") +
theme_minimal()
p2 <- ggplot(sales_data, aes(x = month, y = sales, color = city)) +
geom_line(size = 1) +
labs(title = "B) 月度趋势") +
theme_minimal()
p3 <- ggplot(sales_data, aes(x = sales, fill = city)) +
geom_density(alpha = 0.5) +
labs(title = "C) 分布对比") +
theme_minimal()
# patchwork的布局语法
(p1 | p2) / p3 # 上两图并排,下一图全宽
# 更精细的布局控制
p1 + p2 + p3 + plot_layout(widths = c(1, 2, 1), ncol = 3) +
plot_annotation(
title = "销售数据综合分析报告",
caption = "模拟数据;假设报告期截至2024年12月"
)
六、交互式图表
6.1 plotly:一键转为交互图
library(plotly)
# 方法1:把ggplot对象转为plotly
p <- ggplot(sales_data, aes(x = month, y = sales, color = city,
text = paste0("城市:", city, "<br>月份:", month, "月<br>销售额:", sales))) +
geom_line() +
geom_point()
ggplotly(p, tooltip = "text") # tooltip指定悬停显示内容
# 方法2:直接用plotly语法(更精细控制)
plot_ly(sales_data,
x = ~month, y = ~sales, color = ~city,
type = "scatter", mode = "lines+markers",
hovertemplate = "月份: %{x}月<br>销售额: %{y}万<extra></extra>"
) %>%
layout(
title = "各城市月度销售趋势",
xaxis = list(title = "月份"),
yaxis = list(title = "销售额(万元)"),
hovermode = "x unified"
)
6.2 echarts4r:ECharts的R接口
library(echarts4r)
sales_data %>%
group_by(city) %>%
e_charts(x = month) %>%
e_line(sales, name = city) %>%
e_tooltip(trigger = "axis") %>%
e_title("各城市月度销售趋势") %>%
e_legend(bottom = 0) %>%
e_x_axis(name = "月份") %>%
e_y_axis(name = "销售额")
七、保存与导出
# 保存为高分辨率图片(用于印刷或PPT)
ggsave(
filename = "sales_trend.png",
plot = last_plot(), # 或指定plot变量
width = 12,
height = 7,
dpi = 300, # 印刷用300dpi,网页用72-96dpi
units = "in",
bg = "white"
)
# 保存为SVG(可缩放,适合网页)
ggsave("sales_trend.svg", width = 12, height = 7)
# 保存为PDF(向量图,最适合报告)
ggsave("sales_trend.pdf", width = 12, height = 7, device = cairo_pdf)
tip 中文字体显示的一种处理方案
在macOS/Linux上,中文显示取决于可用字体、字体配置和输出设备;默认配置可能出现缺字。可尝试以下方案,Google字体下载需要网络访问,也可加载本地字体:
library(showtext)
showtext_auto() # 启用showtext自动处理
font_add_google("Noto Sans SC", "noto") # 或加载本地字体
# 之后使用theme_minimal(base_family = "noto"),或theme(text = element_text(family = "noto"))
八、ggplot2与Tableau/PowerBI的对比
| 维度 | ggplot2 | Tableau | PowerBI |
|---|---|---|---|
| 学习曲线 | 陡峭,需要编程基础 | 平缓,拖拽操作 | 中等 |
| 定制程度 | 极高,像素级控制 | 中等 | 中等 |
| 可重复性 | 完全可重复(代码即文档) | 工作簿,版本管理差 | 同左 |
| 自动化 | 天然支持(脚本化) | 需要额外工具 | 需要额外工具 |
| 数据量 | 需结合可用内存、数据处理方式与绘图复杂度评估 | 假设比较百万行场景,仍需按数据源、硬件与图表复杂度测试,不能保证流畅 | 是否需要高级许可应按容量、功能及部署需求判断,不能仅按数据量断定 |
| 适合场景 | 统计报告、学术发表、自动化报告 | 业务BI看板 | 企业报表 |
建议:两者不是对立关系。用Tableau/PowerBI做实时BI看板,用ggplot2做深度统计分析报告。
相关文档
- 08-tidyverse数据处理全流程 — 数据处理后传入ggplot2绘图
- 11-R-Markdown数据报告自动化 — 把ggplot2图表嵌入自动化报告
- 12-AB测试与实验分析 — A/B测试结果的标准可视化方案
- 待创建:数据可视化设计原则 — 超越代码的图表设计哲学
tip 立即可用的技巧
在任何ggplot2图表末尾加上+ theme_minimal(),可以立刻去掉灰色背景,让图表更干净。这是提升图表专业感成本最低的一步。