书稿纵轴题:正向竖排与单位外置
出版社 a–c、token 切词、单图/分面/翻转/热力四种布局
经验来自《中国旱区农业技术发展报告2026》第2章、第5章 5.2 配图(拍板 2026-09-02)。可拷贝源:
../R/ylab-stack.R、../R/theme-tech.R。交互终端print(ggplot)的字体/httpgd 问题见 另一篇笔记,与轴题几何无关。
出版社要求(a–c)
须同时满足:
- a. 自上而下:从轴顶读到轴底。
- b. 竖排:字形正向(
angle = 0),用换行把题拆成一列。不是整串axis.title.y的angle = -90(那是侧躺)。 - c. 单位外置:末尾全角或半角括号(
(家)、(亿元)、(%))不进竖排串,放在该图或该分面纵轴正上方。
无量纲指标不编造单位。热力图左轴已是类别名,保持 y = NULL。
为何不用整串旋转或分面条
| 做法 | 为何不用 |
|---|---|
theme_tech 的 axis.title.y = -90 |
整串侧躺,不满足 b |
右侧 facet_grid 条当轴题 |
轴值在左、题在右 |
switch = "y" + 换行 strip |
ggplot2 常把换行条裁掉 |
ggtext / 左侧 textGrob / 拼图 |
多依赖或多一层布局 |
切词:保护块优先于逐字
逐字 strsplit(s, "") 会把 RD资金投入 拆成 R / D。ylab-stack.R 用两个扁平函数,不包 ggplot:
split_ylab_unit(s):拆title/unit;无括号则unit = ""。stack_ylab(s):先匹配保护块ylab_keep(默认R&D、RD、AI、GDP、DEA、CARS、CCR,长的优先),再整段拉丁词或数字,其余按字;paste(..., collapse = "\n")。
例如 RD资金投入(亿元) → 题竖排 RD / 资 / 金 / 投 / 入,单位 (亿元) 顶置。项目自有缩写在 source() 之后追加:ylab_keep <- c(ylab_keep, "NDVI")。
各图自己组单位标注的 tibble 或 annotate()。不要再写 add_stacked_ylab(p, ...) 这类包一层的 helper。
字号、字体与间距
轴题必须与 theme_tech 的 axis.text.y 同一套:family = simsun、size = 11(pt)。常量在 ylab-stack.R:ylab_text_family、ylab_text_size_pt、theme_ylab_stack。
| 问题 | 根因 | 做法 |
|---|---|---|
| 轴题压住刻度 | geom_text(x = -Inf, hjust = 2.45) 的 hjust 以轴题自身宽度(约 1 汉字)为单位,挡不住 13000 |
单图改走 labs(y = stack_ylab(...)) + theme_ylab_stack(margin(r = ylab_title_gap_pt),由 ggplot 给轴题单独占位) |
| 字号不一致 | geom_text(size = 3.8) 默认单位是 mm;轴标签是 11pt |
单图用 element_text(size = 11);分面 geom_text 写 size = ylab_text_size_pt, size.unit = "pt" |
四种布局
单图(数值在左侧的柱/线)
labs(y = stack_ylab(u$title))。- 单位:
geom_text/annotate,x = -Inf, y = Inf,hjust = 0.5, vjust = -1.15,11pt / simsun,size.unit = "pt"。 coord_cartesian(clip = "off"),plot.margin顶部留约 18pt。+ theme_tech + theme_ylab_stack,顺序不能反,否则轴题被-90盖掉。
分面(每面题不同)
strip.text = element_blank(),ylab(""),避免与-90叠字。- 竖排题:
geom_text,x = -Inf,y取该面数据中位,hjust = ylab_stack_hjust,size.unit = "pt"。 - 单位:
geom_text,y = Inf。 clip = "off";左侧plot.margin用ylab_stack_margin_l。
翻转图:coord_flip() 后数值轴在图底、已经横排;左侧是类别。底轴保持横排,不要把数值题搬到左边再竖排。
热力图:labs(y = NULL)。图例承担量纲说明,不在左侧再加竖排题。
theme_tech 放在绘图脚本里
对象在分析脚本里就画完:末尾 + theme_tech,再加本图覆盖(theme_ylab_stack、关 strip、axis.text.x 角度、margin)。qmd 只写 p <- plot_*,不在章文件里再叠主题。
theme_tech 的 axis.title.y = -90 保留,服务未改完的旧图。迁移旧项目时一脚本一提交,未轮到的脚本不要提前改;改完每个脚本单独 source 并 ggsave(..., device = ragg::agg_png, bg = "white") 目视(theme_tech 置空背景,不写 bg 会是透明底),不要为轴题整章 render。
验收
source("scripts/book-plot/R/ylab-stack.R")
u <- split_ylab_unit("RD资金投入(亿元)")
# title 为 RD资金投入,unit 为 (亿元)
tok <- strsplit(stack_ylab(u$title), "\n", fixed = TRUE)[[1]]
# tok[1] 必须是 RD,不能是 Rharness 内的自动断言见 ../test/Rscript/test-ylab-stack.R。
不要做
- 多层嵌套 helper 改 plot。
- 为无量纲图编造
(无量纲)之类单位。 - 翻转图左侧再竖排数值题。
- 用
geom_text默认 mm 字号写轴题。