想象你要装修一套毛坯房。拿到房子的第一件事不是冲进家具城,而是看户型图,决定哪里是客厅、哪里是卧室,再定好整体装修风格。接下来你才去添置沙发、书架、窗帘,调节灯光色调,给每个房间挂上门牌,最后请专业摄影师拍一组高清照片,整套房子才算是“可以住人了”。
ggplot2就是帮你完成这套“从毛坯到美宅”全流程的装修施工队。它把画图拆成一道道工序:先用 ggplot() 拿到户型图(数据),用 aes() 标好每面墙的用途(美学映射),再用 geom_*() 一件件添置家具(几何对象),用 scale_*() 调节灯光(标度),用 theme_*() 统一风格(主题),用 facet_*() 给大空间做隔断(分面),用 labs() 挂上门牌(标签),最后用 ggsave() 拍出出版级高清大片(导出)。整个过程中你最常用的操作只有一个——加号 +,它像装修验收清单上的一个勾,每写一次,就有一层新东西被装进图里。
本文就用这套“装修比喻”带你走完 ggplot2 的全部核心流程,让零基础的你也能画出能投稿、能汇报、能发朋友圈的漂亮图表。
对 AI 从业者来说,数据可视化是把模型结果讲清楚的第一步。ggplot2 的图层语法能让你把任何分析结论变成一张出版级图表,是写论文、做报告、展示 AI 效果的必备硬技能。
可视化演示
对比五种常见时间复杂度O(1)、O(log n)、O(n)、O(n log n)、O(n^2)随输入规模n的增长曲线。
本节配有交互动效(进入视口后自动加载,约 3KB)。
时间复杂度对比 — n=1 O(1)常数:不随n变化 O(log n)对数:增长极慢 n=1 O(n^2)与O(1)差距随n增大急剧扩大
图层语法
装修一套房子,第一步绝不是冲进家具城搬沙发,而是先拿到户型图,定好“哪里是客厅、哪里是卧室、厨房卫生间各占多大”。ggplot2 的第一步也如此温柔:它让你先把数据框(户型图)和美学映射(设计方案)交给 ggplot(),这时画布上一片空白,但整张图的结构已经悄悄定好。
在 R 中,ggplot(data = df, aes(x = 变量1, y = 变量2)) 创建画布,aes() 把数据列映射到视觉通道——x 轴、y 轴、颜色、大小。这一步不画任何图形,就像设计师还没贴墙纸,但已经在图纸上标好了每面墙的颜色和功能分区。
ggplot2 的灵魂是“图层语法”(Grammar of Graphics):一张图表由一层层组件叠加而成,每一层用 + 连接。数据层、映射层、几何层、标度层、主题层,层层递进。这种设计让画图像搭积木一样,想加什么就加什么,想改哪层就改哪层。
验证清单上的每一项都是“打勾叠加”。ggplot2 的加号就是你手中的验收笔——每写一个 +,装好的那件东西就永远留在图纸上,直到最后整间“图表之屋”竣工入住。这种可拆分、可叠加的语法,正是 ggplot2 能画出出版级图表的最大底气。
- Plot最终生成的图表对象,是整间“装修好的房子”。
- Data输入的数据框,是一切绘图的原材料,相当于户型图。
- aes(·)美学映射函数,把数据列翻译成 x、y、颜色等视觉通道。
- geom(·)几何对象函数,决定数据用点、线还是柱来画。
- 加号(+)连接符,每加一层,就往图中“装配”一个新部件。
几何对象
客厅不能只有一张户型图。你得摆沙发、装搁板、挂窗帘——几何对象就是 ggplot2 的“家具”。同样是客厅,放中式红木沙发还是北欧布艺沙发,气质完全不同;同样一份数据,用散点还是用柱状,表达的信息也截然不同。
geom_point() 是散点图,适合看两个变量的相关关系,像茶几上的装饰盘,每盘对应一个观测值;geom_line() 是折线图,适合看时间趋势,像窗帘的波浪线条,把一个个点串成流动的方向;geom_col() 是柱状图,柱高表示数值大小,像书架每层的高度,一眼就能比出谁高谁低;geom_boxplot() 是箱线图,展示数据分布的“五数概括”,像收纳盒里整理好的物件,告诉你中位数在哪、离群值藏在哪。
每种几何对象都有自己默认的美学属性。point 需要 x、y;line 需要 x、y;col 需要 x 和 y(或权重)。几何层可以同时叠加多个,只要映射用得对。
家具可以混搭:客厅里沙发配茶几再加一盆绿植,ggplot2 里 geom_point() + geom_line() 一起上,散点给出每个观测的精确位置,折线串出整体趋势。但千万别把仓库里所有家具都搬进一个房间——图表也一样,信息过载到每个角落都堆满形状,读者就什么也看不出来了。
- geom几何对象,决定数据以什么“长相”出现。
- ∈属于符号,表示几何对象是下面集合中的一个。
- point散点,每个观测画一个点,适合看两变量关系。
- line折线,按顺序连接各点,适合看趋势变化。
- col柱状,柱高代表数值,适合类别间比较大小。
标度系统
同样的家具,在暖黄灯光下温馨,在冷白灯光下清爽——灯光决定了房间的“气质”。标度(scale)就是 ggplot2 里的灯光师,它控制坐标轴刻度怎么分、颜色从深到浅怎么变、点的大小范围多大,把数据的真实数值翻译成画布上协调的位置和色彩。
scale_x_continuous() 可以自定义 x 轴刻度与范围;scale_color_viridis() 控制颜色映射;scale_size() 控制点的大小。最常用也最关键的是对数变换:当数据从几十跨到几千万,横跨好几个数量级时,直接用原始值画图,小值全挤在一条线附近。scale_y_log10() 登场后,刻度变得均匀,小细节也看得清了。
灯光调得好,房间的层次感自然出来;标度调得好,数据的分布规律一目了然。尤其是画收入、流量这类“长尾数据”时,一个 scale_y_log10() 就能让你的图从“一团乱麻”变成“条理清晰”,这也是出版级图表和随手涂鸦的分水岭之一。
- x原始数据值,比如收入从 1000 到 10000000,跨越四个数量级。
- log₁₀以 10 为底的对数,把“相差十倍”变成“相差一格”。
- x′变换后的坐标值,用于映射到坐标轴的刻度位置。
主题美化
毛坯房装完家具,还是“现代混搭”得有点杂乱。装修公司这时会递上一本风格画册:现代简约、日式原木、复古欧式,任选一套就能让全屋风格统一。ggplot2 的主题(theme)就是这本画册。
theme_minimal() 一键去掉默认的灰色背景和网格线,只留下清爽的白底;theme_classic() 回归传统学术风格,适合论文插图;theme_bw() 黑白主题,适合印刷。如果这些还不够,theme() 函数允许你微调每一个细节:element_text() 改字体族和字号,element_rect() 改背景与边框,element_line() 改网格线粗细和颜色,legend.position 控制图例放哪。
出版级图表通常不只是套用一个主题,而是“基础主题 + 一屏微调”:去掉多余网格线、把字体放大到 12 号以上、加粗主标题、图例移到底部或隐藏。
装修最后要统一柜子把手、门框颜色,主题就是那个“让每一处细节都协调”的总设计师。一个 theme_minimal() 能让你的图表立刻从“学生作业”升级成“咨询报告”;再花两分钟微调字体和网格线,投稿、汇报都镇得住场子。
- theme主题函数,整体改变图表外观。
- style风格名,如 minimal、classic、bw。
- ↦映射符号,表示“把风格转换成一系列外观参数”。
- font字体与字号。
- background背景色与边框样式。
- grid网格线的有无与样式。
- legend图例的位置与外观。
分面技术
一个大客厅想同时给老人、孩子、客人三种人用怎么办?推拉门一拉,一个空间分成三个互不打扰的小空间。分面(facet)就是 ggplot2 的推拉门——按某个类别变量,把一张大图拆成多张并排的小图。
facet_wrap(~城市) 把一个分类变量的小图按顺序排列,放不下就自动换行,适合维度较少、想快速浏览每个类别的场景。facet_grid(行变量 ~ 列变量) 则生成一个二维矩阵,适合同时按两个变量分组,比如行放“性别”、列放“年龄段”。每个小图共享坐标轴,方便横向对比;如果数据各自量级差异太大,加上 scales = "free" 或 scales = "free_y",每个子图就能拥有自己的刻度范围。
推拉门拉开前,所有数据挤在一张图里是一团浆糊;拉开后,每个子集的规律清清楚楚。分面是“多类别对比”的终极武器:论文里按实验组/对照组分面,汇报里按门店/区域分面,一眼就能看出哪组表现好、哪组在恶化。
- f(x)每个子图内的图形关系,比如折线趋势。
- z分面变量,比如“城市”或“门店”,每个取值生成一个小图。
- for each level of z遍历 z 的每一个水平,逐个绘制。
标签注释
房门上挂名牌、墙上贴便利贴、地板画箭头——访客才知道每个房间是什么、重点该看哪里。ggplot2 里,labs() 和 annotate() 就是干这活的两双手。
labs(title = "主标题", subtitle = "副标题", x = "X轴名", y = "Y轴名", caption = "脚注") 一次性搞定所有“门牌”。主标题通常是图表想表达的核心结论,副标题补充范围和数据口径,脚注标注数据来源。annotate("text", x = 值, y = 值, label = "文字") 则在图内任意坐标写下注释;配合 annotate("segment", ...) 加 arrow() 可以画出带箭头的指向线,直接戳中那个“峰值点”或“异常值”。
一幅出版级图表的标签要克制:标题一句话讲清楚结论,正文里的标注只留给最重要的两三个点,脚注诚实交代数据来源。每一个字都要回答读者“这是什么、看哪、为什么”。
再美的房间,没有门牌和说明,访客也会迷路;图表没有标签,读者就不知道横轴是“月份”还是“收入”,更不知道那个箭头指的是什么。给图表起个好标题,比给它多撞十个图层更能提升专业感——审稿人和老板最先看的就是标题。
- T所有标签文本的集合。
- title主标题,表达核心结论或图名。
- subtitle副标题,补充说明范围与口径。
- xlabx 轴名称。
- ylaby 轴名称。
- caption脚注,标注数据来源与时间。
配色方案
装修选软装时,设计师会拿出一本“色卡”,保证沙发、窗帘、抱枕颜色之间有呼应。ggplot2 的配色方案就是现成的色卡库,拿过来直接用,永远比随手点的红黄蓝绿协调。
RColorBrewer 是最经典的选择:scale_fill_brewer(palette = "Set2") 提供分类色,适合离散类别;"Blues" 这类序列色,由浅到深表达数值递增,适合热力图;"RdBu" 这类发散色,中间浅两头深,适合表达正负偏离。近几年,viridis 色板异军突起,它基于感知均匀的色彩空间设计,颜色之间有相同的视觉距离,而且对色盲友好——scale_color_viridis_d() 一行代码就搞定。
选择配色有一条黄金法则:先想清楚数据是“分类”还是“连续”,再决定用分类色、序列色还是发散色。分类数据用五颜六色的分类色会显得杂乱,连续数据用彩虹色会误导对比。
调色盘选得好,家具之间有了呼应,房间自带高级感;配色选得好,数据分组一眼就能区分,连续变化清晰可感。一个团队如果统一用 viridis 或固定几套 RColorBrewer 色板,整套报告都会显得专业、整洁,这就是“出版级”的隐形门槛。
- palette调色板,一组预先设计好的颜色集合。
- qualitative分类色,类别之间差异明显、无顺序感。
- sequential序列色,由浅到深表示数值从小到大。
- diverging发散色,中间浅、两端深,表示偏离中点的程度。
高清导出
装修得再漂亮,最终也要拍一组“房源上线照”。用手机随便一拍,再好的设计也显得廉价;用单反在自然光下拍 300dpi 高清大片,马上身价翻倍。ggsave() 就是 ggplot2 的专业相机。
ggsave("output.png", plot = p, width = 8, height = 6, dpi = 300) 是标准用法:文件名决定格式(.png 为位图,.pdf 为矢量),width 和 height 控制物理尺寸,dpi 决定每英寸多少像素。投稿论文至少 300dpi,PPT 展示 150dpi 够用,屏幕预览 96dpi 就行。如果图表要放进论文或书籍排版,强烈建议导出 PDF 矢量格式——矢量图由数学曲线组成,放大一万倍都不会出现锯齿和马赛克。
装修得再好,照片拍糊了也白搭;图表做得再漂亮,导出分辨率太低,放进论文里就是一团马赛克,审稿人直接打回。记住这条公式,导出前先算一算够不够清晰,再决定用 PNG 还是 PDF——这是“出版级”的最后一公里。
- px最终图片的像素尺寸。
- inch图片物理宽度(英寸),8 英寸约等于 20.3 厘米。
- dpi每英寸像素点数,印刷标准 300,屏幕标准 96。
AI 应用场景
学术论文配图
阶段:实验结果可视化与交付
解决的问题:论文审稿人要求图表达到出版级清晰度。用 Excel 随手截图的图表分辨率不足、配色凌乱,反复被要求重做。
具体案例:某医学影像 AI 研究团队用 ggplot2 绘制模型评估图:geom_boxplot() 展示各算法在五个数据集上的 AUC 分布,facet_grid(数据集 ~ 模型类型) 分面排列,viridis 色板区分算法类别,最后 ggsave(..., dpi = 300) 导出 TIFF 与 PDF 双版本用于投稿。图上每个子集的差异一目了然,审稿意见中“图表质量差”的评论从此消失。
电商运营分析报告
阶段:业务数据分析与洞察呈现
解决的问题:每周经营分析会上,图表信息混杂、重点不突出,管理者在几十个维度里找不到关键变化。
具体案例:某电商平台的数据分析师用 geom_col() + geom_line() 组合展示“日 GMV 与订单量”双视角,用 annotate("segment") 加箭头标注“双11大促”和“大促后回落”两个关键节点,labs() 把标题写成结论式“大促后三天 GMV 回落 32%,需关注复购”,再用 theme_minimal() 统一风格。原本需要 10 分钟口头解释的数据,变成一张图讲 30 秒。
城市交通可视化大屏
阶段:数据洞察展示
解决的问题:交通数据量大、维度多,单张图信息过载,大屏上小流量路段被大数值淹没。
具体案例:某智能交通公司数据团队用 facet_wrap(~时段, scales = "free_y") 把全城路况按“早高峰/平峰/晚高峰/夜间”拆成四张子图,scale_y_log10() 处理从几百到几十万的车流量跨度,scale_color_viridis_d() 区分快速路、主干道和支路。四张子图在大屏轮播,值班人员一眼定位异常拥堵区域,再结合 AI 预测模型提前调流。
Python 演示
import pandas as pdimport numpy as npfrom plotnine import ( ggplot, aes, geom_point, geom_line, labs, theme_minimal, facet_wrap, scale_color_viridis_d, ggsave) np.random.seed(2024)n_per_store = 20stores = ["A店", "B店", "C店"]df_list = []for i, store in enumerate(stores): base = 50 + i * 25 trend = np.cumsum(np.random.normal(loc=1.5, scale=4.0, size=n_per_store)) df_list.append(pd.DataFrame({ "month": range(1, n_per_store + 1), "sales": base + trend, "store": store, }))df = pd.concat(df_list, ignore_index=True) p = ( ggplot(df, aes(x="month", y="sales", color="store")) + geom_point(size=2, alpha=0.7) + geom_line() + facet_wrap("~store", scales="free_y") + labs(title="三家门店月度销售额趋势", subtitle="模拟数据演示 ggplot2 图层语法", x="月份", y="销售额(万元)", caption="数据来源: 模拟数据集") + theme_minimal() + scale_color_viridis_d())print(p)ggsave(p, "sales_trend.png", width=8, height=6, dpi=300)print("已导出 sales_trend.png (300dpi)")
from plotnine import (...)导入 ggplot2 的 Python 移植版,函数名与 R 保持一致,学一遍两边通用。np.random.seed(2024)固定随机种子,保证每次运行生成的数据相同,便于复现和调试。np.cumsum(np.random.normal(...))生成带随机波动的累计趋势,模拟真实门店销售额的涨涨跌跌。ggplot(df, aes(x="month", y="sales", color="store"))创建画布并映射美学:月份上 x 轴、销售额上 y 轴、门店按颜色区分。+ geom_point(size=2, alpha=0.7)叠加散点层,半透明点避免重叠遮挡。+ geom_line()叠加折线层,串出趋势方向。+ facet_wrap("~store", scales="free_y")按门店拆成三张子图,且各自拥有独立的 y 轴刻度。+ labs(...)添加主标题、副标题、轴名和脚注,图表的“门牌”全部挂好。+ theme_minimal()一键极简主题,去掉默认灰底和网格线,立刻有出版级质感。+ scale_color_viridis_d()使用色盲友好的 viridis 配色,让三条折线既好看又易区分。ggsave(p, "sales_trend.png", width=8, height=6, dpi=300)以 300dpi 导出 8×6 英寸高清图,论文印刷级清晰度。
常见误区
误区“我明明在 ggplot() 里传了数据,为什么还要在 geom_point() 里再写一遍 x 和 y?能不能直接在 geom_point(x = 列1, y = 列2) 里指定?”
正解不能。数据列必须通过 aes() 声明映射,告诉 ggplot“哪一列对应 x、哪一列对应 y、哪一列对应颜色”。aes() 是设计蓝图,geom 只是施工队——施工队只负责按蓝图搬家具,不负责自己决定每个房间的功能。如果要在某个 geom 里单独指定映射,也必须写在 aes() 里。
为何常见:初学者习惯了 Excel 选两列直接出图的交互逻辑,不习惯 ggplot2“先映射、后绘制”的声明式思维,所以总想绕过 aes() 走捷径。
误区“ggplot2 里的 `+` 不就是 R 的加法运算符吗?为什么我把 ggsave() 也加进去就一直报错?”
正解在 ggplot 对象语境下,+ 被重载为“图层叠加符”,作用是在图表上增加一个组件或修改一个设置。它不像算术加法那样计算数值,而是像装修清单上的勾选:每写一个 +,就多一道工序。ggsave() 是拍照动作,不是装修工序,所以不能放进 + 链里。
为何常见:R 语言里 + 太常见了,很容易忘记它在面向对象绘图环境中被赋予了全新的语法意义。
误区“geom_point() 能画点,geom_line() 能连线,geom_col() 能画柱,我都画上去,读者肯定觉得信息量巨大、非常专业。”
正解图表不是信息仓库。散点+折线是经典组合,但散点+折线+柱状+箱线叠在一起,只会让画面像堆满所有家具的房间,转身都难。出版级图表的黄金法则是“一个图层突出一个重点”:趋势用折线,分布用箱线,比较用柱状,混搭只限于少数互补组合(如散点+趋势线)。
为何常见:新手觉得“画得越满越用心”,忽略了可视化的本质是降低读者的认知负担。
误区“我画出来的图灰底格纹、字小色杂,别人用 ggplot2 画得那么高级,是不是我下载的包有问题?”
正解默认主题确实不够好看,但这不是工具的错。出版级图表是“数据映射 + 几何选择 + 主题微调 + 配色方案 + 高分辨率导出”五步组合的产物。先套 theme_minimal() 或 theme_classic() 打底,再改字号、删网格线、换 viridis 色板,最后 300dpi 导出——每一步都做到,图不可能丑。
为何常见:默认设置和高级成品之间的差距需要经验补足,新手容易把“不会用”误判为“工具不行”。
误区“我截了 4K 屏幕的图放进论文还是模糊,是不是该换台更好的显示器?”
正解屏幕截图分辨率再高也是 96dpi 左右,而印刷需要 300dpi。直接用 ggsave(..., dpi = 300) 生成图片,或者导出 PDF 矢量格式,才符合出版要求。记住公式“像素 = 英寸 × dpi”:8 英寸宽的图,96dpi 只有 768 像素,300dpi 有 2400 像素,清晰度差两倍以上。
为何常见:屏幕显示清晰和纸面印刷清晰是两套标准,许多人从来不知道 dpi 这个概念,只能靠截图“硬扛”。
面试题
Q1. ggplot2 绘图的基本起点是什么?
- A.直接调用 geom_point() 画点
- B.ggplot(data, aes()) 建画布← 正确答案
- C.theme_minimal() 设置主题
- D.ggsave() 导出图片文件
答案:B。ggplot2 的一切从 ggplot(data, aes()) 开始,它创建画布并声明数据与美学映射;几何对象、主题、导出都是在这张画布上叠加的后置步骤。就像装修必须先拿到户型图才能摆家具。
Q2. ggplot2 中 aes() 函数的核心作用是什么?
- A.添加标题与副标题文本
- B.把数据列映射到视觉通道← 正确答案
- C.改变图表的背景颜色
- D.将图片保存到指定路径
答案:B。aes() 是“美学映射”,把数据框里的列翻译成 x 轴、y 轴、颜色、大小等视觉通道。它相当于装修方案里“这面墙刷什么色”的标注;标题、背景、导出分别由 labs()、theme()、ggsave() 负责。
Q3. 下列哪个是绘制柱状图的几何对象?
- A.geom_point()
- B.geom_line()
- C.geom_col()← 正确答案
- D.geom_boxplot()
答案:C。geom_col() 用柱高代表数值,适合类别间比较;point 是散点、line 是折线、boxplot 是箱线图。选择几何对象就像选择家具种类,先想清楚要表达什么故事再决定。
Q4. 数据范围从 100 到 10000000 跨四个数量级,最佳做法是?
- A.对 y 轴做对数变换 scale_y_log10()← 正确答案
- B.删除掉所有最大值所在的行
- C.把所有数据统一除以 1000000
- D.改用饼图展示占比关系
答案:A。scale_y_log10() 把“每十倍”压成等距刻度,小值不再挤成一条线。删数据、缩放数据都会扭曲信息,饼图更不适合连续变量。这就像把塞满大小杂物的房间按量级重新整理,每个尺寸都得到展示空间。
Q5. 想把一张大图按“城市”拆成多个并排小图,应使用?
- A.theme_classic() 经典主题
- B.facet_wrap(~城市)← 正确答案
- C.scale_color_manual() 手动配色
- D.annotate("text", ……) 文字标注
答案:B。分面函数负责“隔断”:facet_wrap(~城市) 按城市的每个取值各画一张子图并自动排列。主题、配色、标注都改变不了图的结构,只有 facet 系列能拆图。
Q6. ggsave 中 dpi=300 表示什么含义?
- A.图片文件的体积大小
- B.每英寸包含的像素点数← 正确答案
- C.图片物理宽度的计量单位
- D.颜色通道的位深数值
答案:B。dpi 是 Dots Per Inch,每英寸像素点。按公式“像素 = 英寸 × dpi”,300dpi 是印刷标准,96dpi 是屏幕标准。同样尺寸的图,dpi 越高细节越密,放大后越清晰。
Q7. 想让分面后的每个子图拥有独立坐标轴刻度,应如何设置?
- A.labs(scales = "free")
- B.theme(scales = "free")
- C.facet_wrap(……, scales = "free")← 正确答案
- D.ggsave(……, scales = "free")
答案:C。scales 是分面函数的专属参数,scales = "free" 表示每个子图根据自身数据范围独立定刻度,"free_y" 只放开 y 轴。其他函数不认识这个参数。就像每个独立小房间允许按自己的面积布置家具,不受大屋统一规格限制。
Q8. 要在图中画一条带箭头的线指向某个异常数据点,并配上文字,正确做法是?
- A.只用 annotate("text", ……) 就够了
- B.annotate("segment", ……) + arrow() 画箭头,再配 text 注释← 正确答案
- C.用 labs() 参数直接添加指向箭头
- D.用 theme() 的 legend.position 实现
答案:B。annotate("segment", ...) 画线段,配合 arrow() 加箭头,annotate("text", ...) 写文字,两者组合才能完成“箭头+文字”的指向标注。labs() 只添加标题类文本,theme() 只管外观,都无法画定向箭头。
Q9. 以下哪些做法能显著提升图表的“出版级”质感?
- A.套用 theme_minimal() 后微调字体与网格线← 正确答案
- B.使用 viridis 或 RColorBrewer 专业配色← 正确答案
- C.叠加尽可能多的几何对象让信息铺满全图
- D.导出时设置 dpi=300 或使用 PDF 矢量格式← 正确答案
答案:ABD。极简主题、专业配色、高清导出是出版级图表的三根支柱。而 C 恰恰相反——堆砌几何对象会造成信息过载,把图表变成“家具堆满的仓库”,读者反而抓不住重点。
Q10. 关于 ggplot2 的图层语法,下列说法正确的是?
- A.用加号 + 逐层叠加各组件← 正确答案
- B.aes() 只能在 ggplot() 中出现一次且不可覆盖
- C.标度和主题都是可以叠加的独立图层← 正确答案
- D.geom_point() 与 geom_line() 永远不能同时使用
答案:AC。+ 是图层叠加符,A 正确;aes() 可以在各 geom 中局部覆盖映射,B 错误;scale_* 和 theme_* 都以图层形式参与叠加,C 正确;散点+折线是经典组合,D 错误。就像装修清单上的每一道工序都可以独立追加,也都可以针对某个房间单独调整。