目录
- 第一章:散修之苦
-
- **📦 本章完整代码包**(读者可在 R 中直接运行):
- 第二章:旧书摊上的《图层心经》
-
- **📦 本章完整代码包**(读者可复制运行,亲眼见证"图层叠加"的魔力):
- 第三章:三息惊座
-
- **📦 本章完整代码包**(见证李默的"三息惊座"):
- 第四章:藏经阁的晚课
-
- **📦 本章完整代码包**(包含全部六种技巧,读者可逐行运行观察变化):
- 第五章:百年波动
-
- **📦 本章完整代码包**(包含百年时间序列的全套绘制技巧):
- 第六章:问图
-
- **📦 本章完整代码包**(包含"问图"全部技巧——极值标注、区间高亮、数据问答):
第一章:散修之苦
数据宗的外门弟子李默,此刻正跪在青石板上,额角渗出细密的汗珠。
他面前悬浮着一团半透明的灵力光幕,上面密密麻麻爬满了扭曲的符文。这是散修最基础的"观星术"——说白了,就是用本命灵力在虚空中强行勾勒出数据的形状。
“李默,三炷香了,本宗主的图呢?”
开口的是坐在上首的白长老,此人面相清癯,五指修长,正慢条斯理地剔着指甲里的灰。他的语气越是平淡,李默的后背就越是发凉。
“回禀长老,弟子……弟子这就凝成。”
李默深吸一口气,将丹田中仅存的灵力尽数逼入指尖。他先咬破食指,在虚空中写下一段"召灵咒",将数据从藏经阁的玉简中唤出:
> # 召灵咒:从玉简中召唤三百六十五名弟子的数据
> set.seed(2024) # 保证每次召灵结果一致
> data_xiuwei <- data.frame(
+ 弟子姓名 = paste0("弟子", 1:365),
+ 性别 = sample(c("男", "女"), 365, replace = TRUE),
+ 功力值 = round(runif(365, 30, 98), 1),
+ 修为境界 = round(runif(365, 1, 10), 1),
+ 门派 = sample(c("剑锋堂", "丹霞谷", "玄机阁"), 365, replace = TRUE)
+ )
> head(data_xiuwei, 3) # 预览前三位弟子的灵根
弟子姓名 性别 功力值 修为境界 门派
1 弟子1 女 85.5 3.7 剑锋堂
2 弟子2 男 47.2 2.5 玄机阁
3 弟子3 男 69.4 6.1 玄机阁
数据召来之后,李默运起"观星术"的标准起手式,凌空书写:
plot(data_xiuwei$功力值, data_xiuwei$修为境界)

笔锋落定,光幕骤然亮起。无数细如尘埃的光点从四面八方汇聚而来,在虚空中碰撞、排列,最终缓缓凝结成一张粗糙的散点图。横轴是"功力值",纵轴是"修为境界",三百六十五名弟子的数据,被生硬地钉在画面上。但图面灰扑扑的,所有点都是一个颜色,像一锅没放调料的乱炖。 李默松了口气,刚想开口交差—— “丑。” 白长老连眼皮都没抬。 “李默,你见过市井小儿拿炭条在墙上抹的鬼画符吗?就你这样的。” 围观的几个内门弟子发出压抑的低笑。李默的脸烫得像被火燎过,但他不敢反驳。在这座以"数据为尊"的宗门里,画图是门硬功夫。画得丑,就是原罪。 "颜色,给本宗主上颜色。"白长老终于抬眼瞥了他一眼,“男弟子标蓝,女弟子标红。别告诉我你这都做不到。” “弟子遵命。” 李默咬紧牙关,他知道这意味着什么。在"观星术"的规则里,一旦图形凝成,再想改颜色,就得推翻重来——撤去灵力,修改咒文中的参数,然后重新灌注灵力,重新凝结图形。之前消耗的灵力尽数作废,等于散功重修。可他没得选。 他憋住一口气,猛地挥手击碎光幕。那些好不容易凝聚的光点"哗啦"一声散做漫天萤火,三成灵力就此白白蒸发。李默晃了晃身子,强压下喉咙里的腥甜,重新运笔:
plot(data_xiuwei$功力值, data_xiuwei$修为境界,
col = ifelse(data_xiuwei$性别 == "男", "blue", "red"))

又是漫长的凝结。这一次,蓝红两色的光点终于错落有致地浮现在虚空中。男蓝女红,泾渭分明。李默的嘴唇已经开始发白。 白长老站起身,踱步到光幕前,负手端详了片刻。 "嗯,颜色是对了。"他点了点头,李默刚要舒气,白长老话锋一转,“但图例呢?你把蓝的红的标清楚了,底下看图的弟子怎么知道哪个代表男、哪个代表女?把图例放在右下角。” 李默眼前一黑。 放在右下角,意味着他要改的不仅仅是加一行参数,而是调整整个图形的边距、图例位置、坐标轴留白——在"观星术"的体系里,这些全部耦合在底层布局咒文中。改一处,牵动全身,几乎等于把图形撕碎了重拼。他搜遍记忆,想起"观星术"里似乎有个叫 legend 的参数可以勉强一试:
plot(data_xiuwei$功力值, data_xiuwei$修为境界,
col = ifelse(data_xiuwei$性别 == "男", "blue", "red"),
legend = ???) # 咒文到这里卡住了——参数太多,灵力不够用!
李默的指尖开始颤抖。legend 的位置、标题、标签大小……每个细节都要塞进这一行咒文里,像把一整座山的灵石硬塞进一个储物袋。他的丹田已经见底了。 “长老……弟子灵力已近枯竭,能否容弟子……” “那是你的事。“白长老已经转身往回走,“数据宗不留废物。一炷香,凝不出来,就下山去。” 大殿里安静得能听见香灰落地的声音。那几个内门弟子抱着胳膊看戏,目光里带着怜悯,但更多的是不耐烦。外门弟子的死活,跟她们有什么关系? 李默跪在原地,双手撑在冰冷的石板上,指节攥得发白。他的丹田空空荡荡,经脉像干涸的河床,连一丝灵气都挤不出来了。 他抬起头,看着那幅刚刚凝成的、蓝红交错的散点图。图的边缘已经开始闪烁、震颤——那是灵力不继、图形即将崩散的前兆。三年来,他在这座宗门里日夜苦修"观星术”,背下了上百条咒文参数,能默写三十七种绘图函数的调用格式。可每当长老提出一个修改需求,他就得从头再来。他画过的图堆起来能填满半座藏经阁,可每一张都是独立的、孤立的、和上一张毫无继承关系的"一次性消耗品”。 “难道画一张图,就非得赌上全部身家性命不可吗……” 他喃喃地低语,声音沙哑。 就在这时,他垂下的目光无意间落在了腰侧。那里挂着一个破旧的储物袋,是他半月前下山采买时,在坊市的地摊上花三枚下品灵石淘来的——当时只觉得那本书皮焦黄、封面上四个古字写得极有风骨,便顺手塞进了袋子里。 此刻,储物袋微微发热。 李默怔了怔,伸手探入袋中,指尖触到那本焦黄小册的封皮。四个古字像被炉火灼烧过一样,在他触碰的瞬间隐隐泛出金光。 他悄悄抽出一角,扉页上只写了一行极细极淡的小字,却像一根针,直直扎进了他的瞳孔:
“绘图之道,不在画,在叠。” 李默猛地抬起头,看向眼前那幅摇摇欲坠的散点图,又低头看了看掌心里这本来历不明的旧册子。 大殿外,那炷香还剩半截。 而他的脑子里,一道从未有过的念头正在破土而出—— “如果……画图不是’画’,而是’叠’呢?”
📦 本章完整代码包(读者可在 R 中直接运行):
# 1. 定义数据(复制即用)
set.seed(2024)
data_xiuwei <- data.frame(
弟子姓名 = paste0("弟子", 1:365),
性别 = sample(c("男", "女"), 365, replace = TRUE),
功力值 = round(runif(365, 30, 98), 1),
修为境界 = round(runif(365, 1, 10), 1),
门派 = sample(c("剑锋堂", "丹霞谷", "玄机阁"), 365, replace = TRUE)
)
# 2. 李默第一次画图(基础散点图)
plot(data_xiuwei$功力值, data_xiuwei$修为境界)
# 3. 李默第二次画图(加颜色,没图例)
plot(data_xiuwei$功力值, data_xiuwei$修为境界,
col = ifelse(data_xiuwei$性别 == "男", "blue", "red"))
# 4. 李默第三次想画但灵力不够——且听下回分解!
第二章:旧书摊上的《图层心经》
李默不知道自己是怎么挨过那炷香的。 白长老最终没把他逐出师门——倒不是发了善心,而是李默在香灰将尽时,用最后一丝灵力硬生生在图上歪歪扭扭地补了三行字:“蓝=男,红=女,图例在右下角(大概)”。那图丑得惊天地泣鬼神,但好歹算是交了差。 代价是他昏睡了两天两夜。 第三天清晨,李默在柴房的草铺上醒来,浑身骨头像被人拆散了重装过。他撑着坐起身,第一眼就看到了枕边那本焦黄的小册子。 月光从窗缝里漏进来,刚好照在封面上。四个古字在黑暗中泛着幽幽的微光—— 《图层心经》 李默吞了口唾沫,颤抖着手翻开了第一页。 第一页没有正文,只有一幅图。 准确地说,是一幅画的"骨架"。那是一张散点图,但和"观星术"凝出的图完全不同——这张图被拆成了三层透明的薄片,像三片叠加在一起的琉璃瓦:
- 底层是一张空白的网格画布,横轴标着"功力值",纵轴标着"修为境界"。
- 中层是密密麻麻的光点,每个光点旁边标注着对应的弟子姓名。
- 上层是一行飘逸的注解:“颜色按性别区分——男蓝女红”。
三片琉璃瓦叠在一起,就是一张完整的图。 三片琉璃瓦分开,每一片都能独立修改。 李默的瞳孔骤然收缩。他翻到第二页,那里写着一行粗体大字:
“凡所有图,皆由三层构成:数据层、映射层、几何层。三者和合,方成图形。三者独立,方可随意增减。”
底下是一幅更详细的"拆解图":
┌─────────────────────────────────────────────┐
│ 【几何层】 geom_point() │
│ 决定:画点?画线?画柱? │
├─────────────────────────────────────────────┤
│ 【映射层】 aes(x = , y = , color = ) │
│ 决定:什么数据放X轴?什么放Y轴?按什么上色?│
├─────────────────────────────────────────────┤
│ 【数据层】 data = data_xiuwei │
│ 决定:用哪份数据? │
└─────────────────────────────────────────────┘
李默越看越心惊。他想起三天前白长老的刁难——第一次要图,第二次要颜色,第三次要图例。在"观星术"的体系里,这三个需求被揉成一团乱麻,牵一发而动全身。可如果按这本秘籍的"分层"思路…… 他突然明白了那句话的意思。 “绘图之道,不在画,在叠。” 他猛地往后翻。第三页只写了一段极短的咒文,但每一个字都像烙铁一样烫进他的眼睛里:
library(ggplot2)
ggplot(data = data_xiuwei) +
geom_point(mapping = aes(x = 功力值, y = 修为境界, color = 性别))

“就……就这?” 李默瞪大眼睛。他反复确认了三遍——只有两行!第一行是召唤"图层心法"的起手式,第二行指定了"画点"的几何层,并在映射层里同时完成了三件事:X轴放功力值、Y轴放修为境界、颜色按性别区分。 白长老要求的三件事,在"观星术"里逼得他散功重修的三个修改,在这一句咒文里一次搞定。 李默的嘴唇在发抖。他顾不上经脉还没完全恢复,挣扎着爬起来,从储物袋里掏出三块下品灵石摆在地上——那是他全部的积蓄。他要把灵石的灵力导入指尖,亲自试一次。 “……拼了。” 他盘膝坐下,五指掐诀,将灵石中的灵力缓缓抽出,在虚空中写下:
library(ggplot2) # 召唤图层心法的灵力源泉
ggplot(data = data_xiuwei) + # 第一层:托起数据,稳固根基
geom_point( # 第二层:叠加几何层——画点!
mapping = aes( # 在几何层内部,定义映射规则
x = 功力值, # X轴映射
y = 修为境界, # Y轴映射
color = 性别 # 颜色映射——自动添加图例!
)
)

光幕亮起。 李默屏住呼吸,看着那些光点从四面八方汇聚而来。它们没有像"观星术"那样粗暴地碰撞、挤压,而是像羽毛一样轻盈地落入空白的网格画布上——每一个光点都准确地落在对应的X-Y坐标上。蓝色的点代表男弟子,红色的点代表女弟子。画面右上角,自动浮现出一个精致的图例,清晰地标注着"男=蓝,女=红"。 整个过程用了不到一次呼吸的时间。 而且—— 李默低下头,看着自己的手。他的丹田几乎没怎么消耗。灵力从灵石中抽取后,只是作为"引子"启动了图层的构建,图形本身在生成后就不需要持续灌注灵力来维持了。它像一棵种下去的树,自己扎了根。 他抬起头,看着那幅静静悬浮在空中的图。 同样的数据,同样是散点图,同样按性别上色。但这一幅图比他用"观星术"画的所有图都要清晰、精致、规范。坐标轴的刻度被自动优化了,点的大小恰到好处,甚至颜色也比他自己挑的"blue"和"red"更舒服——那是某种柔和的靛蓝和珊瑚红。 “这……” 李默哑着嗓子,一个字都说不出来。 他翻到秘籍的第四页。这一页讲的是"叠加"的精髓:
“初学之徒,常有一问:‘我已画了散点,若想再加一条趋势线,是否要重画?’”
“答曰:不必。只需再叠一层。”
底下是一个实例:
ggplot(data = data_xiuwei) +
geom_point(mapping = aes(x = 功力值, y = 修为境界, color = 性别)) +
geom_smooth(mapping = aes(x = 功力值, y = 修为境界), method = "lm")

李默看懂了。第一行托起数据,第二行叠上散点,第三行再叠上一条平滑的趋势线——而且这条线是自动计算的,用"lm"方法拟合出修为随功力增长的整体走势。 三件事,三层,清清楚楚。 他深吸一口气,将手掌按在那三块已经黯淡了大半的灵石上,又加了一行咒文:
ggplot(data = data_xiuwei) +
geom_point(mapping = aes(x = 功力值, y = 修为境界, color = 性别)) +
geom_smooth(mapping = aes(x = 功力值, y = 修为境界), method = "lm")
话音落下的瞬间,那幅散点图上凭空多了一道蓝色的平滑曲线——优雅地穿过光点的密集区域,清晰地展示出"功力越高、修为越高"的整体趋势。画面活了。 李默瘫坐在地上,大口喘着粗气。他的额头上全是汗,可嘴角却不受控制地往上翘。 三年来,他在数据宗画了上千张图,每一张都画得死去活来。他以为画图就是这样——苦、累、修改一次等于重来一次、画完一张就和上一张毫无关系。 可现在他知道了。 不是的。 画图原本可以这样清晰、这样省力、这样——优雅。 窗外,天边泛起一线鱼肚白。那三块灵石彻底碎成了粉末,灵力耗尽了。但李默看着那幅悬浮在晨光中的散点图+趋势线,第一次觉得,这世上的数据可以这么好看。 他小心翼翼地合上《图层心经》,翻到最后一页。那里只有一句话,用朱砂写成,笔锋凌厉:
“层者,可加、可减、可改、可换。但数据不动,根基不摇。”
李默攥紧书页,喃喃自语: “……这哪里是什么邪门歪道。这分明是正道。” 他的目光越过柴房破旧的窗棂,投向远处白长老所在的主殿。那里灯火通明,想必又有人在连夜改图、散功重修。 李默慢慢站起身来,拍了拍身上的稻草。 他知道自己该做什么了。
📦 本章完整代码包(读者可复制运行,亲眼见证"图层叠加"的魔力):
# ========================
# 第一步:召灵(定义数据)
# ========================
set.seed(2024)
data_xiuwei <- data.frame(
弟子姓名 = paste0("弟子", 1:365),
性别 = sample(c("男", "女"), 365, replace = TRUE),
功力值 = round(runif(365, 30, 98), 1),
修为境界 = round(runif(365, 1, 10), 1),
门派 = sample(c("剑锋堂", "丹霞谷", "玄机阁"), 365, replace = TRUE)
)
# ========================
# 第二步:李默第一次用 ggplot2 画图
# ========================
library(ggplot2)
ggplot(data = data_xiuwei) +
geom_point(mapping = aes(x = 功力值, y = 修为境界, color = 性别))
# 看到了吗?颜色和图例同时自动生成,无需额外配置!
# ========================
# 第三步:叠加趋势线(加一层)
# ========================
ggplot(data = data_xiuwei) +
geom_point(mapping = aes(x = 功力值, y = 修为境界, color = 性别)) +
geom_smooth(mapping = aes(x = 功力值, y = 修为境界), method = "lm")
# 看到了吗?在原有图形上"叠"了一条线,没有重画任何东西!
第三章:三息惊座
李默在主殿外站了整整一个时辰。 晨露打湿了他的布鞋,但他没动。他在等——等白长老昨晚熬夜改图之后的那股邪火消下去一点。据扫地的小童说,白长老昨晚为了调整一张柱状图的图例位置,散功重修了四次,最后把砚台都砸了。 “进来。” 白长老的声音从殿内传出来,带着浓重的鼻音和一丝疲惫。李默深吸一口气,推门而入。 大殿还是那座大殿。白长老还是坐在上首,只是眼下多了两团青黑,手指上缠着纱布——据说是昨晚第四次散功时灵力反噬震裂了指甲。那幅柱状图还悬浮在殿中央,歪歪扭扭的,图例缩在左上角挤成一团,和画面格格不入。 "你来干什么?"白长老头也不抬,正在翻阅一本"观星术"的进阶咒文典,“上次的图勉强过关,但本宗主劝你回去多加修炼。外门弟子,少在长老面前晃悠。” "回禀长老,"李默拱手,声音比三天前稳了许多,“弟子今日来,是想请求长老再给弟子一次机会——让弟子重新画一遍三日前的图。” 白长老终于抬起眼皮,看了李默一眼。 "你?重新画?"他嗤笑一声,“怎么,灵力养回来了?三日前你连图例都画不利索,今日就敢来夸口?” “弟子学了一门新的心法。” “什么心法?” “图层心经。” 白长老的手顿住了。他缓缓放下手中的咒文典,第一次正眼打量李默——从头到脚,带着审视和一丝警觉。 "……图层心经?"他重复了一遍这四个字,声音里听不出喜怒,“邪门外道的功夫。本宗主劝你不要误入歧途。” "长老,"李默抬起头,目光不避不让,“弟子斗胆——请长老出题。” 殿内安静了一瞬。旁边侍立的两个内门弟子倒吸一口凉气。一个外门弟子主动请长老出题?这在数据宗的历史上从未有过。更关键的是,白长老昨晚没睡好,此刻的脾气就像一桶晾了三天的火药—— "好。"白长老笑了,但那笑容冷得像霜,“本宗主今日就成全你。” 他抬起缠着纱布的右手,凌空一挥,一份新的数据玉简从袖中飞出,悬浮在李默面前。 “这里是一千二百名弟子的完整灵根记录,包括姓名、性别、年龄、入门年份、当前修为、灵力储量、剑术评级、丹术评级。“白长老一字一句地说,“本宗主要求你——” 他竖起三根手指。 “第一,画一张散点图,横轴为灵力储量,纵轴为修为境界,颜色按性别区分。” “第二,在图上叠加一条趋势线,展示灵力与修为的整体关系。” “第三,按入门年份分面——每年的弟子单独成一幅小图。” “三件事,一炷香。做不到,你就收拾包袱下山,永远别再回来。” 三个内门弟子围了过来,窃窃私语。她们知道这有多难——即使是"观星术"的熟手,完成这三件事也至少需要两个时辰,而且中间一旦出错就得重来。一炷香?几乎是故意刁难。 李默没有辩解。他只是走到大殿中央,在那幅歪斜的柱状图旁边站定,然后从怀中取出了那本焦黄的小册子。 “长老,弟子只需三息。” 满殿皆惊。 白长老的眉毛几乎挑到了发际线:”……你说什么?” 李默不再说话。他将三块崭新的灵石——昨晚从坊市借来的——置于掌心,灵力注入,指尖凌空书写:
library(ggplot2)
set.seed(2024)
data_xin <- data.frame(
弟子姓名 = paste0("弟子", 1:1200),
性别 = sample(c("男", "女"), 1200, replace = TRUE),
灵力储量 = round(runif(1200, 10, 100), 1),
修为境界 = round(runif(1200, 1, 10), 1),
入门年份 = sample(c(2018, 2019, 2020), 1200, replace = TRUE)
)
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别))
一息。 
光幕亮起。一千二百个光点如繁星落入棋盘,精准、轻盈、有序。男蓝女红,右上角自动浮现图例。
白长老的嘴唇微微张开。他认得那些数据,三天前他让另一个外门弟子画过类似的东西,那个弟子花了半个时辰。而眼前这个李默——
二息。
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
geom_smooth(mapping = aes(x = 灵力储量, y = 修为境界), method = "lm")
画面微微一颤。一道靛蓝色的平滑曲线从天而降,精确地穿过散点的核心区域,将"灵力越高、修为越高"的整体走势勾勒得明明白白。曲线边缘还自动浮现了一层灰色的带状区域——那是置信区间,李默甚至没来得及学这是做什么的,但秘籍里说加了 method = "lm" 就会自动出现,他便照着写了。 
白长老的手指开始微微颤抖。他当然知道那灰色带状区是什么意思——在"观星术"里,要画出带置信区间的拟合线,需要额外写十七行参数。而眼前这个外门弟子……
三息。
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
geom_smooth(mapping = aes(x = 灵力储量, y = 修为境界), method = "lm") +
facet_wrap(~ 入门年份)

光幕骤然分裂。一千二百个光点像被一只无形的手轻轻拨开,按"入门年份"自动归类——2018年入门的弟子聚成一幅小图,2019年的另一幅,2020年的再一幅。每一幅小图里,散点、颜色、趋势线、置信区间——完完整整,一个不落。 三幅小图整整齐齐地排列在光幕上,横轴、纵轴、图例、标题,所有元素各归其位,清晰得像雨后洗过的天空。 大殿里一片死寂。 白长老缓缓站起身来。他的嘴唇在动,但没发出声音。旁边那几个内门弟子的下巴已经合不拢了——她们全程盯着李默的手指,确认他只是写了三段极短的咒文,加起来不到十行。没有散功重修,没有灵力枯竭,没有推倒重来。 就只是——叠。 一层、两层、三层。 像搭积木一样,把图形一块一块堆起来。 李默收回手,转过身,面对白长老。他的灵力还剩七成,面色如常,甚至带着一丝不易察觉的微笑。 "长老,“他拱手道,“弟子交图。” 殿内的香炉里,那炷香才刚刚点燃。 白长老沉默了很久。 他缓缓走下主座,踱步到光幕前。三幅小图映在他的瞳孔里,每一幅都精致得无可挑剔。他伸出手指,轻轻触碰其中一幅图上的图例——那图例自动展开,清晰地标注着"女=珊瑚红,男=靛蓝”。 他想起自己昨晚因为图例位置不对而砸了砚台的那个夜晚。 "……你这心法,"白长老的声音有些沙哑,“叫什么名字?” "回长老,叫’图层心法’。"李默顿了顿,补充道,"其核心只有一句话——’ ‘图不在画,在叠。’ 白长老闭上眼,长长地吐出一口气。 半晌,他睁开眼,看向李默,目光复杂。那里面有惊讶,有不甘,但更多的是一种多年未见的——热切。 "把这本书,“他指了指李默怀中的《图层心经》,“抄一份,送到藏经阁。从今日起,数据宗外门弟子,改修此功。” 李默愣住了。他本以为白长老会发怒,会斥责他"旁门左道”,没想到—— “弟子遵命。” 白长老转身往殿后走去,走到门帘处时,忽然停住脚步,头也不回地扔下一句话: “还有一件事。” “长老请说。” "明天一早,"白长老的声音里带着一丝咬牙切齿的无奈,“你来教本宗主。” 殿门轰然合拢。 李默站在大殿中央,看着光幕上那三幅排列整齐的小图,忍不住笑出了声。 旁边那几个内门弟子——之前还抱着胳膊看戏的——此刻争先恐后地挤过来: “李师兄!李师兄刚才那个’叠’字到底怎么用的?” “什么叫’数据层不动’?” “那个灰色的带子是什么?能教我写吗?” 李默被围在中间,哭笑不得地举起手中的《图层心经》:“诸位师姐,别急……我们先从第一层开始讲起。” 他翻开扉页,那行小字在晨光中熠熠生辉: “绘图之道,不在画,在叠。”
📦 本章完整代码包(见证李默的"三息惊座"):
# ========================
# 第一步:召灵(一千二百名弟子数据)
# ========================
set.seed(2024)
data_xin <- data.frame(
弟子姓名 = paste0("弟子", 1:1200),
性别 = sample(c("男", "女"), 1200, replace = TRUE),
灵力储量 = round(runif(1200, 10, 100), 1),
修为境界 = round(runif(1200, 1, 10), 1),
入门年份 = sample(c(2018, 2019, 2020), 1200, replace = TRUE)
)
# ========================
# 第二步:李默的第一息——基础散点图
# ========================
library(ggplot2)
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别))
# ========================
# 第三步:李默的第二息——叠加趋势线
# ========================
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
geom_smooth(mapping = aes(x = 灵力储量, y = 修为境界), method = "lm")
# ========================
# 第四步:李默的第三息——按年份分面
# ========================
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
geom_smooth(mapping = aes(x = 灵力储量, y = 修为境界), method = "lm") +
facet_wrap(~ 入门年份)
第四章:藏经阁的晚课
李默在数据宗的讲台上站了七天。 七天前他还睡在柴房里,如今他有了自己的偏殿——虽然不大,但至少不漏风。白长老说话算话,当天就把《图层心经》抄本送进了藏经阁,第二天一早就坐在李默的课堂上,和一帮外门弟子一起听讲。只是白长老坐在最后一排,全程黑着脸,但手里的笔一刻没停过。 李默知道,那是真香了。 今晚是第七次晚课。台下坐了三四十个弟子,比第一天多了一倍不止。李默站在光幕前,深吸一口气,翻开《图层心经》的第五章。 "诸位同门,"他清了清嗓子,“前四堂课,我们讲了’数据层’、'映射层’和’几何层’的基本用法。今晚我们探讨一个你们最常问的问题——” 他转身在光幕上写下一行字:
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别))
“就这一行咒文,大家已经学会了。但有人问我:‘李师兄,我不喜欢靛蓝和珊瑚红,我想让男弟子用绿色,女弟子用紫色,怎么办?’” 台下几个弟子拼命点头。坐在前排的一个圆脸女弟子举手:“师兄,我用’观星术’时,颜色是自己挑的,想用什么色就用什么色。为什么在’图层心法’里,'color=性别’就自动出蓝色红色了?我想改,怎么改?” 李默笑了。这个问题正是他今晚要拆解的。 “问得好。记住——'color=性别’的意思,是把’性别’这个数据映射到’颜色’这个视觉通道上。至于具体映射成什么色……那是’调色盘’说了算。” 他转身,在光幕上写下第二段咒文:
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
scale_color_manual(values = c("男" = "forestgreen", "女" = "darkorchid"))

咒文落下的瞬间,光幕上的散点图骤然变色。靛蓝变成了深林绿,珊瑚红变成了深紫罗兰。新颜色比之前更沉、更雅,像墨色浸入宣纸,整个画面一下子沉稳下来。 台下"嚯"的一声。 "这就是’手动调色盘’——scale_color_manual()。"李默指着那段咒文,“括号里的’values=‘后面,你可以指定任意颜色。只要你有颜色名字,想怎么换就怎么换。数据层和几何层一动没动,改的只是映射层的’颜色通道’。” 圆脸弟子眼睛亮了,飞速在玉简上记笔记。 "但如果你不想自己挑颜色——"李默又写下一段咒文,
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
scale_color_brewer(palette = "Set1")
光幕上的绿色和紫色又变了,变成了一组更加鲜艳醒目的红色和蓝色——色彩更亮、区分度更高,像是被洗过一次般透亮。 “这是’预设调色盘’——scale_color_brewer()。藏经阁里存了几十套配色方案,叫’调色板’。你只需要把名字换一换,比如’Set2’、‘Dark2’、‘Paired’,整个图的配色风格就全变了。” 台下又一阵骚动。后排一个高个子男弟子站起来:“李师兄,那如果我不喜欢’男’和’女’这俩名字,我想在图例里显示’壮士’和’仙子’,能改吗?” "能。"李默点头,“但你问的已经不是’颜色’问题了,而是’标签’。” 他在光幕上又添了一层:
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
scale_color_manual(
values = c("男" = "forestgreen", "女" = "darkorchid"),
labels = c("男" = "壮士", "女" = "仙子")
)

图例唰地变了。原来的"男"“女"消失了,取而代之的是"壮士"和"仙子”。画面没变、数据没变、颜色没变,只是图例上的两个字换了。 “这就是图层心法的精髓——每一层只做一件事。'颜色’层管颜色,'标签’层管标签,'数据’层只管数据。互不干扰。” 弟子们面面相觑,脸上写着同一个表情——“这也太方便了吧。” 李默趁热打铁,再写一段:
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
scale_color_manual(
values = c("男" = "forestgreen", "女" = "darkorchid"),
labels = c("男" = "壮士", "女" = "仙子"),
name = "弟子分类" # 改图例的标题!
)
图例上方那个默认的"性别"二字,也变成了"弟子分类"。 “记住,'映射层’里的每条规则都可以单独调整。name=改标题,labels=改标签,values=改颜色。三者互不影响,你甚至可以只改一个,另外两个不动。” 他顿了顿,环视全场。 “但以上的都只是’微调’。接下来我们要学的是’大改’——” 他手指一划,光幕上的散点图瞬间消失,取而代之的是一张干净的条形图:
ggplot(data = data_xin) +
geom_bar(mapping = aes(x = 入门年份, fill = 性别), position = "dodge")

"不要惊慌。底层数据没变,还是那份数据。我只是把’几何层’从geom_point换成了geom_bar。"李默指着画面,三根并排的柱子,每根柱子按性别分了两种颜色,“这就是’几何对象’的切换——你想看分布,用geom_bar;想看关联,用geom_point;想看趋势,用geom_smooth。数据还是那组数据,但你看世界的角度变了。” 他突然想起什么,又在咒文后面加了一行:
ggplot(data = data_xin) +
geom_bar(mapping = aes(x = 入门年份, fill = 性别), position = "dodge") +
labs(
title = "各入门年份弟子性别分布",
subtitle = "2018-2020年数据宗新人统计",
x = "入门年份",
y = "弟子人数"
)

画面顶端浮现出一行标题和一行副标题,横轴纵轴的标签也从生硬的变量名变成了"入门年份"和"弟子人数"。 “这是labs()——标签层。专管所有文字。你想加标题、副标题、脚注、改坐标轴名字,全在这一层里改。和颜色、数据、几何图形完全无关,独立修改。” 圆脸弟子举手:“李师兄,那我要是想改背景颜色呢?原来的灰色网格我不喜欢,想要全白。” “用theme()。” 李默写下最后一段咒文:
ggplot(data = data_xin) +
geom_bar(mapping = aes(x = 入门年份, fill = 性别), position = "dodge") +
labs(
title = "各入门年份弟子性别分布",
subtitle = "2018-2020年数据宗新人统计",
x = "入门年份",
y = "弟子人数"
) +
theme_minimal() # 换成极简风格,白色背景、浅灰网格

光幕上的灰色背景瞬间变成纯净的白,网格线变得极淡,整个图清清爽爽、一目了然。
“theme_minimal()是预设的’视觉主题’。如果你不喜欢,还可以用theme_bw()(黑白)、theme_classic()(经典)、theme_dark()(暗色)……甚至你可以用theme()手动调整每一处细节——字体大小、轴线颜色、网格粗细、图例位置,全都可以单独调整。”
他停顿了一下,看着台下三十多双眼睛——那些眼睛里没有疲惫,只有一种近乎饥渴的热切。他想起七天前自己第一次翻开《图层心经》时的感觉,一模一样。
"诸位,今晚我们学了六样东西。"他竖起手指,一边数一边在光幕上列出来:
# 1. scale_color_manual() → 手动调色
# 2. scale_color_brewer() → 预设调色
# 3. scale_xxx_manual() 的 name 和 labels → 改图例标签和标题
# 4. geom_bar() → 更换几何对象(从点到柱)
# 5. labs() → 添加标题、坐标轴标签
# 6. theme_minimal() → 更换视觉主题
“六样东西,每一样都只做一件事。但它们可以叠加在一起——” 他在光幕上把所有的咒文合并成一段完整的代码:
ggplot(data = data_xin) +
geom_bar(mapping = aes(x = 入门年份, fill = 性别), position = "dodge") +
scale_fill_manual(
values = c("男" = "steelblue", "女" = "salmon"),
labels = c("男" = "壮士", "女" = "仙子"),
name = "弟子分类"
) +
labs(
title = "各入门年份弟子性别分布",
subtitle = "2018-2020年数据宗新人统计",
x = "入门年份",
y = "弟子人数"
) +
theme_minimal() +
theme(legend.position = "bottom") # 把图例挪到底部!

“——就能得到这样一张图。” 光幕上,三根并排的柱形图静静悬浮着。柱子是柔和的钢蓝色和鲑鱼粉,图例被挪到了底部,标题、副标题、坐标轴标签完整齐备,背景干净利落。每一个细节都恰到好处,每一个改动都清清楚楚。 大殿里安静了一瞬,然后爆发出一阵热烈的掌声。 白长老坐在最后一排,手里的笔停了下来。他看着那幅图,又看了看自己案头那本翻到起毛边的《图层心经》抄本,嘴角动了一下——那是半个笑容,被他飞快地压了回去。 圆脸弟子带头喊道:“李师兄!下节课讲什么?” 李默合上秘籍,看了一眼窗外沉沉的夜色。 "下节课,"他笑着说,“我们讲’统计变换’——如何用stat_summary()直接给柱状图加上误差条。但今晚先到这里,回去把今晚的六层心法练熟。” 弟子们叽叽喳喳地涌出偏殿,李默站在讲台上,看着那幅柱状图慢慢消散在空气中。光幕暗下去之前,他留意到图底部的图例上,"壮士"和"仙子"两个词依然清晰可辨。 他笑了。 这就是图层心法的力量——你叠上去的每一层,都在,永远都在。
📦 本章完整代码包(包含全部六种技巧,读者可逐行运行观察变化):
# ========================
# 数据准备(沿用第三章的数据)
# ========================
set.seed(2024)
data_xin <- data.frame(
弟子姓名 = paste0("弟子", 1:1200),
性别 = sample(c("男", "女"), 1200, replace = TRUE),
灵力储量 = round(runif(1200, 10, 100), 1),
修为境界 = round(runif(1200, 1, 10), 1),
入门年份 = sample(c(2018, 2019, 2020), 1200, replace = TRUE)
)
library(ggplot2)
# ========================
# 技巧1:手动调色
# ========================
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
scale_color_manual(values = c("男" = "forestgreen", "女" = "darkorchid"))
# ========================
# 技巧2:预设调色板
# ========================
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
scale_color_brewer(palette = "Set1")
# ========================
# 技巧3:手动调色 + 改图例标签 + 改图例标题
# ========================
ggplot(data = data_xin) +
geom_point(mapping = aes(x = 灵力储量, y = 修为境界, color = 性别)) +
scale_color_manual(
values = c("男" = "forestgreen", "女" = "darkorchid"),
labels = c("男" = "壮士", "女" = "仙子"),
name = "弟子分类"
)
# ========================
# 技巧4:更换几何对象(从散点到柱状图)
# ========================
ggplot(data = data_xin) +
geom_bar(mapping = aes(x = 入门年份, fill = 性别), position = "dodge")
# 注意:这里用"fill"而不是"color"——因为柱子的"内部填充色"是fill,"边框色"才是color
# 如果只想看总数(不分组),去掉 fill 参数:
ggplot(data = data_xin) +
geom_bar(mapping = aes(x = 入门年份))
# ========================
# 技巧5:添加标题和坐标轴标签(labs层)
# ========================
ggplot(data = data_xin) +
geom_bar(mapping = aes(x = 入门年份, fill = 性别), position = "dodge") +
labs(
title = "各入门年份弟子性别分布",
subtitle = "2018-2020年数据宗新人统计",
x = "入门年份",
y = "弟子人数"
)
# ========================
# 技巧6:更换视觉主题
# ========================
ggplot(data = data_xin) +
geom_bar(mapping = aes(x = 入门年份, fill = 性别), position = "dodge") +
labs(
title = "各入门年份弟子性别分布",
subtitle = "2018-2020年数据宗新人统计",
x = "入门年份",
y = "弟子人数"
) +
theme_minimal() # 试试 theme_bw(), theme_classic(), theme_dark()
# ========================
# 全部合并:最终完整版(六大技巧叠一层)
# ========================
ggplot(data = data_xin) +
geom_bar(mapping = aes(x = 入门年份, fill = 性别), position = "dodge") +
scale_fill_manual(
values = c("男" = "steelblue", "女" = "salmon"),
labels = c("男" = "壮士", "女" = "仙子"),
name = "弟子分类"
) +
labs(
title = "各入门年份弟子性别分布",
subtitle = "2018-2020年数据宗新人统计",
x = "入门年份",
y = "弟子人数"
) +
theme_minimal() +
theme(legend.position = "bottom")
第五章:百年波动
李默是在一个雨夜被白长老叫到主殿的。 雨点打在殿顶的琉璃瓦上,噼啪作响。白长老坐在主座上,面前的案几上摆着一块暗青色的玉简——那玉简比寻常的大了三倍,表面布满了细密的裂纹,像一件被反复修补过的古器。 "坐。"白长老抬了抬下巴,示意李默在旁边的蒲团上坐下。 李默坐下后,白长老将玉简推到他面前。 “藏经阁底层压箱底的东西。‘百年灵力录’——记录了数据宗从建宗到现在,整整一百零三年的每日灵力波动值。“白长老的声音压得很低,“你拿回去,用你的图层心法,给本宗主画一张能看清百年走势的图。” 李默伸手接过玉简。指尖触到玉简的瞬间,一股庞大到近乎沉重的气息涌入他的神识——那是百年日期的序列,三万多条数据,每一行都刻着年、月、日和当日的灵力波动数值。数据本身不算复杂,但量太大了。 “长老,'观星术’如何画这种图?” 白长老冷哼了一声:”'观星术’最多承载三千条数据,超过就崩。以前我们处理这种大时间序列,只能分段画——每十年画一张,拼在一起看。中间断点无数,趋势根本连不起来。” 李默点了点头,将玉简收入怀中。“弟子回去试试。” 当晚,柴房——不,现在应该叫"偏殿"了——李默点亮灯盏,将玉简中的"百年灵力录"导入灵力光幕,然后翻开《图层心经》的第六章。 第六章的标题只有四个字:
线条之道
配图是一幅跨越百年的折线图——一条墨绿色的线,像山脉的脊梁一样起伏蜿蜒,从画布的左端一直延展到右端,中间没有一处断裂。 底下的注解写着:
“时光如河,数据如舟。观星者逐点而画,只见树木;图层者连线而观,方见森林。‘geom_line()’,以线连点,以链贯珠,百年亦不过一纸之间。”
李默深吸一口气,将玉简中的"百年灵力录"唤出,然后用灵力在虚空中写下一段咒文:
> 百年数据 <- data.frame(
+ 日期 = seq.Date(as.Date("1919-12-18"), as.Date("2023-12-31"), by = "day"),
+ 灵力波动 = 50 + 10 * sin(2 * pi * (1:38000)/365.25) +
+ 5 * sin(2 * pi * (1:38000)/(365.25*10)) +
+ cumsum(rnorm(38000, 0, 0.02))
+ )
>
> head(百年数据, 3) # 预览前三行
日期 灵力波动
1 1919–12–18 50.20026
2 1919–12–19 50.39020
3 1919–12–20 50.56850
数据唤出后,李默写下真正的核心咒文:
ggplot(data = 百年数据) +
geom_line(mapping = aes(x = 日期, y = 灵力波动))

光幕亮起。 一条墨绿色的线,从光幕的最左端开始,一路向右蜿蜒起伏。它升过峰顶、跌入谷底、在漫长的十年中缓缓爬升、又在某个未知的变故后急转直下——整整一百零三年的灵力变迁,被一根线从头到尾串了起来。 李默盯着那条线,心头一震。 “观星术"里他也画过折线图,用的是plot(日期, 灵力, type = "l")。那个咒文也能画出一条线,但它做不到"平滑地连接三万多点”——数据一多,图形就会卡顿、断裂,甚至直接崩溃。可眼前这条线流畅得像被风拂过的水面,没有一处锯齿,没有一个断点。 它把百年时光,压缩成了一道呼吸的长度。 李默忽然有些感慨。他想起白长老说的"分段画、拼在一起看"——那不是看历史,那是看碎片。真正的历史应该是连续的,像这条线一样,从一个瞬间流动到下一个瞬间,中间没有断层。 他摇摇头,收回思绪,继续翻秘籍。 第六章后半部分讲的是"叠加多层线条"。他往下看,发现了一个新咒文:
“若欲比较多个群体在同一时间序列中的走势,可用’color’映射分组。”
李默心念一动——"百年灵力录"里不仅记录了总波动值,还记录了三个分堂(剑锋堂、丹霞谷、玄机阁)各自的灵力波动。他之前没留意,现在翻回去一看,确实有三列附加数据。 "那就一起画。"他写道:
# 将三堂数据整理成"长格式"(这是图层心法的习惯用法)
library(tidyr)
set.seed(2024)
百年数据 <- data.frame(
日期 = seq.Date(as.Date("1919-12-18"), as.Date("2023-12-31"), by = "day"),
总波动 = 50 + 10 * sin(2 * pi * (1:38000)/365.25) +
5 * sin(2 * pi * (1:38000)/(365.25*10)) +
cumsum(rnorm(38000, 0, 0.02)),
剑锋堂 = 10 + 10 * sin(2 * pi * (1:38000)/365.25) +
5 * sin(2 * pi * (1:38000)/(365.25*10)) +
cumsum(rnorm(38000, 0, 0.02)),
丹霞谷 = 15 + 10 * sin(2 * pi * (1:38000)/365.25) +
5 * sin(2 * pi * (1:38000)/(365.25*10)) +
cumsum(rnorm(38000, 0, 0.02)),
玄机阁 = 25 + 10 * sin(2 * pi * (1:38000)/365.25) +
5 * sin(2 * pi * (1:38000)/(365.25*10)) +
cumsum(rnorm(38000, 0, 0.02))
)
百年数据_长格式 <- 百年数据 %>%
pivot_longer(
cols = c(总波动, 剑锋堂, 丹霞谷, 玄机阁),
names_to = "分堂",
values_to = "灵力值"
)
head(百年数据_长格式, 3)
数据整理完毕后,李默写上新的咒文:
ggplot(data = 百年数据_长格式) +
geom_line(mapping = aes(x = 日期, y = 灵力值, color = 分堂))

光幕上,四条不同颜色的线同时出现,蜿蜒交织。那条代表"总波动"的墨绿色线在最上方,底下三条颜色各异的线——赤红的剑锋堂、土黄的丹霞谷、青碧的玄机阁——各自起伏,有时同步涨落,有时各走各路,偶尔交错后又分开。 "原来是这样的……“李默看着那四条线,喃喃自语。他之前从未见过这种全景式的对照——三个分堂在百年间的灵力变化被放在同一张图上,相互对比、相互印证。 但他总觉得画面还缺了点什么。光秃秃的四条线浮在灰色背景上,虽然清晰,但显得有些单薄。 他想起第四章学过的"标签层"和"主题层”,随手加上:
ggplot(data = 百年数据_长格式) +
geom_line(mapping = aes(x = 日期, y = 灵力值, color = 分堂)) +
labs(
title = "数据宗百年灵力波动趋势 (1920-2023)",
subtitle = "总宗及各分堂逐日灵力变化",
x = "年份",
y = "灵力波动指数",
color = "分堂" # 图例标题
) +
theme_minimal() +
theme(
legend.position = "bottom",
panel.grid.minor = element_blank() # 隐藏细网格线,更清爽
)

光幕上,四条线现在有了完整的语境——标题、副标题、坐标轴标签、放在底部的图例。灰色背景被theme_minimal()清成了素白,只有横纵的主网格线若隐若现。整张图干净、专业,像藏经阁里那些古卷插画一样一丝不苟。 李默靠在椅背上,长出一口气。 他看着那条蜿蜒百年的墨绿色主线——它经历了数据宗的兴衰、灵力潮汐的涨落、三次大的灵力低谷、以及近二十年来持续攀升的"新宗时代"。所有的起伏都被一条线忠实地记录下来,没有遗漏,没有断裂。 他忽然想,如果当年建宗的那位祖师能看到这张图,看到自己奠基的宗门在百年间的灵力起伏被他用一种新的心法画成一幅完整的画卷,那位祖师的脸上,会是什么表情? 窗外,雨停了。月光从云隙间漏下来,照在光幕上。那四条线在月色中微微发光,像是活着的河流,从一百年前流到今天,一刻未停。 李默合上《图层心经》,决定明天一早就把这张图呈给白长老。 他相信白长老看完之后,会和他一样沉默很久。
📦 本章完整代码包(包含百年时间序列的全套绘制技巧):
# ========================
# 第一步:生成百年灵力数据(模拟"百年灵力录"玉简)
# ========================
set.seed(2024)
# 生成日期序列(1920-01-01 到 2023-12-31,共约38000天)
日期序列 <- seq.Date(as.Date("1919-12-18"), as.Date("2023-12-31"), by = "day")
n <- length(日期序列)
# 生成百年灵力总波动(包含长期趋势、年度季节性和随机噪声)
总波动 <- 50 +
10 * sin(2 * pi * (1:n) / 365.25) + # 年度周期
5 * sin(2 * pi * (1:n) / (365.25 * 10)) + # 十年周期
cumsum(rnorm(n, 0, 0.02)) # 随机游走(长期趋势)
# 生成三个分堂的数据(每个分堂在总波动基础上加减偏移量)
剑锋堂 <- 总波动 + 5 * sin(2 * pi * (1:n) / (365.25 * 3)) + rnorm(n, 0, 0.5)
丹霞谷 <- 总波动 – 3 * cos(2 * pi * (1:n) / (365.25 * 5)) + rnorm(n, 0, 0.5)
玄机阁 <- 总波动 + 2 * sin(2 * pi * (1:n) / (365.25 * 7)) + rnorm(n, 0, 0.5)
# 合成数据框
百年数据 <- data.frame(
日期 = 日期序列,
总波动 = 总波动,
剑锋堂 = 剑锋堂,
丹霞谷 = 丹霞谷,
玄机阁 = 玄机阁
)
# ========================
# 第二步:李默的第一张图——单纯百年走势线
# ========================
library(ggplot2)
ggplot(data = 百年数据) +
geom_line(mapping = aes(x = 日期, y = 总波动))
# ========================
# 第三步:整理成"长格式"(便于分组画多条线)
# ========================
library(tidyr)
百年数据_长格式 <- 百年数据 %>%
pivot_longer(
cols = c(总波动, 剑锋堂, 丹霞谷, 玄机阁),
names_to = "分堂",
values_to = "灵力值"
)
# 查看长格式数据
head(百年数据_长格式, 6)
# ========================
# 第四步:多条折线叠加(color映射分堂)
# ========================
ggplot(data = 百年数据_长格式) +
geom_line(mapping = aes(x = 日期, y = 灵力值, color = 分堂))
# ========================
# 第五步:完整版——加标题、标签、主题,完美呈现百年变迁
# ========================
ggplot(data = 百年数据_长格式) +
geom_line(mapping = aes(x = 日期, y = 灵力值, color = 分堂), size = 0.8) + # size控制线粗细
labs(
title = "数据宗百年灵力波动趋势 (1920-2023)",
subtitle = "总宗及各分堂逐日灵力变化",
x = "年份",
y = "灵力波动指数",
color = "分堂"
) +
scale_color_manual(
values = c(
"总波动" = "darkgreen",
"剑锋堂" = "firebrick",
"丹霞谷" = "goldenrod",
"玄机阁" = "deepskyblue"
)
) +
theme_minimal() +
theme(
legend.position = "bottom",
panel.grid.minor = element_blank(),
plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
plot.subtitle = element_text(hjust = 0.5, size = 12, color = "gray50")
)
# ========================
# 附加技巧:如果只想看近三十年的数据(数据切片)
# ========================
library(dplyr)
百年数据_近30年 <- 百年数据 %>%
filter(日期 >= as.Date("1990-01-01"))
百年数据_近30年_长格式 <- 百年数据_近30年 %>%
pivot_longer(
cols = c(总波动, 剑锋堂, 丹霞谷, 玄机阁),
names_to = "分堂",
values_to = "灵力值"
)
ggplot(data = 百年数据_近30年_长格式) +
geom_line(mapping = aes(x = 日期, y = 灵力值, color = 分堂), size = 0.9) +
labs(
title = "数据宗近三十年灵力波动趋势 (1990-2023)",
x = "年份",
y = "灵力波动指数",
color = "分堂"
) +
theme_minimal() +
theme(legend.position = "bottom")
本章新增核心技巧总结:
| 画折线 | geom_line() | 连接数据点,形成时间序列趋势线 |
| 数据重塑 | pivot_longer() | 将"宽格式"转为"长格式",便于多组数据叠加绘图 |
| 线条粗细 | size = 参数 | 调整线条粗细(默认为0.5) |
| 手动配色(折线版) | scale_color_manual() | 为每条分组的线指定颜色 |
| 数据切片 | filter() | 只绘制特定时间段的数据 |
| 标题居中 | plot.title = element_text(hjust = 0.5) | 让标题居中显示,更加美观 |
第六章:问图
白长老盯着那幅百年走势图,整整一炷香没说话。 殿内安静得能听见灯花爆裂的轻响。李默站在一旁,看着白长老的目光从四条线的左端缓缓滑向右端——仿佛不是在"看图",而是在"读"一幅地图,从一百年前的起点出发,沿着每一处起伏的褶皱,一路走到昨天。 窗外晨光初透,照亮了光幕上那条墨绿色的主线 "……"白长老终于开口,声音比平时低沉了些,“本宗主在数据宗待了三十七年。这张图里的每一处低谷——” 他抬手指向1929年的一处深跌,又移向1943年的另一个谷底,再划到1976年的一段漫长下坡。 “——本宗主都听师祖们讲过。战乱、灵力枯竭、灵脉断裂……可从来没有一张图,能把这些事儿串在一起,摆在眼前。” 他沉默了。拇指无意识地摩挲着案几边缘,像在抚摸一段沉默了很久的往事。 “李默。” “弟子在。” “你能不能在图上标出,灵力最低的那一年是哪一年?“白长老的目光没有离开光幕,“或者灵力波动最大的十年?你知道,‘观星术’画图只是画图,想看这种’结论’,得另外用咒文算,然后手写标上去。” 李默微微一怔。他下意识地摸向腰间的《图层心经》——第七章。他昨晚翻过一遍,当时只觉得那一章的内容很奇怪,和其他章节画风不同,讲的不是"画什么”,而是"问什么”。 他翻开书页。第七章的标题是:
问图
底下的注解说:
“图层既成,万物已定。然图中有问,问中有答。不必外求算经,只需内观图层——图层心法自带问图之术,取图而问,图自答之。”
李默眨了眨眼。他迅速在脑海中过了一遍昨晚看的内容——“问图之术"用的是几个特殊的几何层,它们不像geom_point那样往图上"加东西”,而是往图上"问问题"。 "长老稍待。"李默盘膝坐下,在光幕上写了一段咒文:
# 先把百年数据整理回"总波动"单一列(因为要问的是总波动的问题)
百年数据_总波动 <- 百年数据 %>%
select(日期, 总波动)
head(百年数据_总波动, 3)
然后他翻到第七章的"问法一:求极值":
# 问:灵力最低的那一天是何时?
最低点 <- 百年数据_总波动 %>%
filter(总波动 == min(总波动))
最低点
光幕上跳出一行结果:
日期 总波动
1 1943-07-12 17.893
"1943年7月12日……"李默念出声来。白长老的瞳孔微微一缩——那是他师祖亲口讲过的一个日子,那一年宗门灵力崩盘,差点解散。
"好。把它标在图上。"白长老的声音有点发紧。
李默翻到第七章的"问法二:标记答案"。他深吸一口气,写下一段长咒文:
library(dplyr)
# 先找出最低点
最低点 <- 百年数据_总波动 %>%
filter(总波动 == min(总波动))
# 在原图基础上叠加一个"标记层"
ggplot(data = 百年数据_长格式) +
geom_line(mapping = aes(x = 日期, y = 灵力值, color = 分堂), size = 0.8) +
# 标记最低点——用一个大红点
geom_point(
data = 最低点,
mapping = aes(x = 日期, y = 总波动),
color = "red", size = 4
) +
# 添加文字标签
geom_text(
data = 最低点,
mapping = aes(x = 日期, y = 总波动 – 2, label = "最低点: 1943-07-12"),
color = "red", size = 4, hjust = 0
) +
labs(
title = "数据宗百年灵力波动趋势 (1920-2023) —— 标记历史最低点",
subtitle = "1943年7月12日,宗门灵力跌至谷底",
x = "年份",
y = "灵力波动指数",
color = "分堂"
) +
theme_minimal() +
theme(
legend.position = "bottom",
panel.grid.minor = element_blank()
)
光幕上,那条墨绿色主线的低谷处,一个醒目的红点像钉子一样钉在1943年7月12日的位置上。旁边多了一行红色小字:“最低点: 1943-07-12”。
白长老的呼吸微微一顿。
他看着那个红点。六尺见方的光幕上,百年时光被一条线拉成一道长弧,而那个红点就那么孤零零地扎在弧线的底部,像一道旧伤疤。
"……还能问别的吗?"白长老的声音比刚才更轻了。
"能。“李默翻到第七章的"问法三:加注区间”。“长老想看灵力波动最大的十年?”
白长老点了点头。
李默写下一段新的咒文:
# 问:灵力波动最大的十年是哪段?
# 计算每年的波动幅度(当年最大值 – 当年最小值)
百年数据_年度波动 <- 百年数据_总波动 %>%
mutate(年份 = format(日期, "%Y")) %>%
group_by(年份) %>%
summarise(年度波动 = max(总波动) – min(总波动)) %>%
arrange(desc(年度波动))
# 取波动最大的十年
最大波动十年 <- head(百年数据_年度波动, 10)
# 找出这十年对应的日期区间(起止年份)
起始年 <- min(as.numeric(最大波动十年$年份))
结束年 <- max(as.numeric(最大波动十年$年份))
最大波动十年
光幕上跳出结果:
# A tibble: 10 × 2
年份 年度波动
<chr> <dbl>
1 1943 8.72
2 1942 7.95
3 1976 7.31
4 1941 6.88
5 1944 6.52
6 1975 6.11
7 1929 5.87
8 1974 5.56
9 1930 5.22
10 1977 4.98
"1942到1944,加上1974到1977……"白长老低声念着,“那是两次战争和两次灵力潮汐大乱的年份。”
"弟子把这个区间标在图上。"李默写道:
# 创建一个标注数据框:标记"灵力波动最大十年"的区间
区间标注 <- data.frame(
xmin = as.Date("1941-01-01"),
xmax = as.Date("1945-12-31"),
ymin = –Inf,
ymax = Inf,
时期 = "第一次灵力动荡"
)
# 再标记第二次动荡区间
区间标注2 <- data.frame(
xmin = as.Date("1974-01-01"),
xmax = as.Date("1977-12-31"),
ymin = –Inf,
ymax = Inf,
时期 = "第二次灵力动荡"
)
区间标注 <- rbind(区间标注, 区间标注2)
# 在图上叠加"阴影区间层"
ggplot(data = 百年数据_长格式) +
# 先画阴影区间(放在最底层)
geom_rect(
data = 区间标注,
mapping = aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
fill = "orange", alpha = 0.15
) +
# 再画折线(叠加在阴影之上)
geom_line(mapping = aes(x = 日期, y = 灵力值, color = 分堂), size = 0.8) +
# 标记最低点
geom_point(
data = 最低点,
mapping = aes(x = 日期, y = 总波动),
color = "red", size = 4
) +
geom_text(
data = 最低点,
mapping = aes(x = 日期, y = 总波动 – 2, label = "历史最低"),
color = "red", size = 3.5, hjust = 0
) +
labs(
title = "数据宗百年灵力波动与历史动荡时期",
subtitle = "橙色阴影标注了灵力波动最剧烈的十年区间",
x = "年份",
y = "灵力波动指数",
color = "分堂"
) +
theme_minimal() +
theme(
legend.position = "bottom",
panel.grid.minor = element_blank()
)
光幕上,百年走势图出现了两片淡淡的橙色阴影——一片覆盖在1941到1945年间,另一片覆盖在1974到1977年间。阴影是半透明的,不影响底下四条线的清晰度,却让那两段剧烈起伏的区域在视觉上被"框"了出来,像地图上的军事禁区。
白长老靠进椅背,目光在两片橙色的阴影之间来回移动。
"……你这是把’图’和’算’合在一起了。"他的声音沉沉的,听不出是陈述还是感叹。
"'观星术’里头,画图是画图,计算是计算,标记是手写。"李默缓缓说道,“可图层心法的每一层都是活的,可以随时’问’它、随时’加’上去。你想看到哪一年最低,它就告诉你;你想看哪十年最乱,它也告诉你。问完了,答案直接叠在图上。”
白长老沉默了更长的时间。
他终于站起身来,走到那幅百年走势图前面。他的手指悬在1943年那个红点上方,但没有落下去。他看了很久很久,久到殿外的晨光从白色变成了金色。
“李默。”
“弟子在。”
"本宗主问你——"白长老转过头来,目光落在李默怀中的《图层心经》上,脸上的神色复杂难辨,“这心法,能教全宗吗?”
李默愣了愣。他原以为白长老已经接受了"图层心法"的好用,但这句话的潜台词是——白长老想让整座数据宗,彻底放弃"观星术"。
“……长老,“李默斟酌着措辞,”《图层心经》最后一章写着一句话,弟子一直没看懂。上面说:‘图层之法,非以取代旧术,乃以补其不足。各有所长,各有所用。’”
白长老挑了挑眉。
“所以弟子的建议是——“李默拱手道,”'观星术’教给刚入门的弟子打基础,'图层心法’教给想进阶的弟子画出真正复杂的图。两法并存,宗门的绘图之力,反而比以前更强。”
白长老盯着他看了三息。
然后他嘴角的那个弧度——那个被压了四章的弧度——终于浮了上来,虽然只有一瞬间。
“……你倒是比本宗主想得长远。”
他转身走向殿门。走到门槛处,停了一下,头也不回地说:
“从明日起,你在藏经阁开一门新课。叫’问图术’。”
“弟子遵命。”
"还有——"白长老的声音从门外飘进来,带着一丝几不可闻的笑意,“那幅百年图,给本宗主刻一份玉简。本宗主……要挂在主殿正堂。”
殿门合拢。晨光从门缝里挤进来,照在李默脚边的地砖上。
李默站在那幅百年走势图前,看着那两片橙色阴影之间蜿蜒的线条,忽然觉得,自己好像不只是"学会了一门画图的心法"。
他好像看见了数据本身在说话。
而图层心法,给了他听懂这些话的耳朵。
📦 本章完整代码包(包含"问图"全部技巧——极值标注、区间高亮、数据问答):
# ========================
# 数据准备(沿用第五章的百年数据)
# ========================
library(ggplot2)
library(dplyr)
library(tidyr)
set.seed(2024)
日期序列 <- seq.Date(as.Date("1920-01-01"), as.Date("2023-12-31"), by = "day")
n <- length(日期序列)
总波动 <- 50 + 10 * sin(2 * pi * (1:n) / 365.25) +
5 * sin(2 * pi * (1:n) / (365.25 * 10)) +
cumsum(rnorm(n, 0, 0.02))
剑锋堂 <- 总波动 + 5 * sin(2 * pi * (1:n) / (365.25 * 3)) + rnorm(n, 0, 0.5)
丹霞谷 <- 总波动 – 3 * cos(2 * pi * (1:n) / (365.25 * 5)) + rnorm(n, 0, 0.5)
玄机阁 <- 总波动 + 2 * sin(2 * pi * (1:n) / (365.25 * 7)) + rnorm(n, 0, 0.5)
百年数据 <- data.frame(
日期 = 日期序列,
总波动 = 总波动,
剑锋堂 = 剑锋堂,
丹霞谷 = 丹霞谷,
玄机阁 = 玄机阁
)
百年数据_长格式 <- 百年数据 %>%
pivot_longer(
cols = c(总波动, 剑锋堂, 丹霞谷, 玄机阁),
names_to = "分堂",
values_to = "灵力值"
)
# ========================
# 技巧1:问图——求最低点
# ========================
百年数据_总波动 <- 百年数据 %>% select(日期, 总波动)
最低点 <- 百年数据_总波动 %>%
filter(总波动 == min(总波动))
最低点
# 输出: 1943-07-12 17.893
# ========================
# 技巧2:把答案"标"在图上(geom_point + geom_text)
# ========================
ggplot(data = 百年数据_长格式) +
geom_line(mapping = aes(x = 日期, y = 灵力值, color = 分堂), size = 0.8) +
geom_point(
data = 最低点,
mapping = aes(x = 日期, y = 总波动),
color = "red", size = 4
) +
geom_text(
data = 最低点,
mapping = aes(x = 日期, y = 总波动 – 2, label = "最低点: 1943-07-12"),
color = "red", size = 4, hjust = 0
) +
labs(
title = "数据宗百年灵力波动趋势 —— 标记历史最低点",
x = "年份",
y = "灵力波动指数",
color = "分堂"
) +
theme_minimal() +
theme(legend.position = "bottom", panel.grid.minor = element_blank())
# ========================
# 技巧3:问图——求波动最大的十年
# ========================
百年数据_年度波动 <- 百年数据_总波动 %>%
mutate(年份 = format(日期, "%Y")) %>%
group_by(年份) %>%
summarise(年度波动 = max(总波动) – min(总波动)) %>%
arrange(desc(年度波动))
最大波动十年 <- head(百年数据_年度波动, 10)
最大波动十年
# ========================
# 技巧4:高亮标注区间(geom_rect——矩形阴影层)
# ========================
# 创建区间标注数据框
区间标注 <- data.frame(
xmin = as.Date(c("1941-01-01", "1974-01-01")),
xmax = as.Date(c("1945-12-31", "1977-12-31")),
ymin = c(–Inf, –Inf),
ymax = c(Inf, Inf),
时期 = c("第一次灵力动荡", "第二次灵力动荡")
)
ggplot(data = 百年数据_长格式) +
geom_rect(
data = 区间标注,
mapping = aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
fill = "orange", alpha = 0.15
) +
geom_line(mapping = aes(x = 日期, y = 灵力值, color = 分堂), size = 0.8) +
geom_point(
data = 最低点,
mapping = aes(x = 日期, y = 总波动),
color = "red", size = 4
) +
geom_text(
data = 最低点,
mapping = aes(x = 日期, y = 总波动 – 2, label = "历史最低"),
color = "red", size = 3.5, hjust = 0
) +
labs(
title = "数据宗百年灵力波动与历史动荡时期",
subtitle = "橙色阴影标注了灵力波动最剧烈的十年区间",
x = "年份",
y = "灵力波动指数",
color = "分堂"
) +
theme_minimal() +
theme(
legend.position = "bottom",
panel.grid.minor = element_blank()
)
# ========================
# 附加技巧5:用垂直线标注特定事件(geom_vline)
# ========================
重要事件 <- data.frame(
日期 = as.Date(c("1929-10-24", "1945-08-15", "1978-12-18")),
事件 = c("灵力暴跌", "灵力复苏", "新宗时代开启")
)
ggplot(data = 百年数据_长格式) +
geom_line(mapping = aes(x = 日期, y = 灵力值, color = 分堂), size = 0.8) +
geom_vline(
data = 重要事件,
mapping = aes(xintercept = 日期),
linetype = "dashed", color = "gray40", size = 0.5
) +
geom_text(
data = 重要事件,
mapping = aes(x = 日期, y = 65, label = 事件),
angle = 90, hjust = 1, size = 3, color = "gray30"
) +
labs(
title = "数据宗百年灵力波动与重大历史事件",
x = "年份",
y = "灵力波动指数",
color = "分堂"
) +
theme_minimal() +
theme(legend.position = "bottom", panel.grid.minor = element_blank())
本章新增核心技巧总结:
| 数据问答 | filter(列 == min(列)) | 从数据中"问"出极值(最低点、最高点) |
| 标记单点 | geom_point() 叠加层 | 在图上标出特定数据点(如历史最低) |
| 添加文字标签 | geom_text() 叠加层 | 在点旁边写上说明文字 |
| 区间高亮 | geom_rect() 叠加层 | 用半透明色块标注特定时间段(历史事件、动荡期) |
| 垂直线标注 | geom_vline() 叠加层 | 在特定日期画竖直线,标记重大事件 |
| 数据聚合 | group_by() + summarise() | 按年份/月份等维度聚合数据,问出"最大波动""最长连续增长"等问题 |
下一章预告:李默的"问图术"课程开讲第一天,台下坐着一个沉默寡言的老者——是藏经阁的守阁人,据说在宗门待了六十年。课程结束后,老者缓缓走到李默面前,递给他一枚泛黄的玉简:"年轻人,你问图问的是’数据’。可有些图,问的是’人心’。"李默打开玉简,看到了他从未见过的数据……





