什么时候用它
一次要看很多个效应量的时候:十个亚组各自的 OR、三个模型对同一个暴露的估计、二十项研究的 meta 分析。每行一个估计,一条线一个区间,竖着排开跟同一条无效线比。
只有一两个效应量就别用了——两行的森林图不如把数字直接写进正文。
怎么读
- 先找无效线。 比值型指标(OR / HR / RR)在 1,差值型(beta / MD)在 0。
- 看区间碰没碰到它。 碰到了就是"这份数据没能排除无效",不是"证明了无效"。
- 看区间的宽度。 宽是样本少或事件少,跟效应大小无关。一条又宽又偏的线,说的是"不知道",不是"很强"。
- 别横向比亚组之间的显著性。 A 组显著、B 组不显著,不等于两组效应不同——那要看交互作用检验,森林图上没有。
输入的形状
这是这张图唯一麻烦的地方。画的部分反而是机械的。
forestploter::forest() 要的不是一张表,而是拆开的四样东西:
| 要什么 | 说明 |
|---|---|
data |
只放文本列的 data.frame,逐列变成表格里的文字 |
| 占位列 | data 里必须有一列是全空格,CI 图就画在它的位置;空格数量决定 CI 面板的宽度 |
ci_column |
占位列是第几列 |
est / lower / upper |
三个独立的数值向量,按行和 data 对齐 |
还有两样是 forest() 不管、得你自己造的:OR (95% CI) 那一列文本(sprintf 拼),以及 p 值的两个版本——数值的用来判断要不要加粗,字符串的用来显示(带 <0.001 截断)。
plot_forest() 把后面这些全接管了:你只给文本列和三个向量,它自动插入占位列和 CI 文本列。所以它的 data 比 forest() 的 data 少两列,ci_column 指的也是插入之前的位置。这是两者最容易混的地方。
常见陷阱
占位列的空格数决定 CI 面板宽度。 它不是随便写的 strrep(" ", 20)——数字改了,图的比例就变了。行数变化时最容易忘掉调它,结果 CI 被挤成一小段。
缩进要用不断行空格 。 普通空格会被 grid 的文本渲染吃掉,亚组的层级就没了。plot_forest() 的 indent 参数内部用的就是它。
est 里的 NA 是分组标题行。 那一行不画 CI,但 data 里必须有这一行——三个数值向量和 data 的长度要严格一致。
不给 ticks_at 会得到 1.125 这种刻度。 默认是在 xlim 上五等分,c(0.5, 3) 就成了 0.5 / 1.125 / 1.75 / 2.375 / 3。无效线 1 甚至不在刻度上。
不给 header 会把列名当表头。 占位列会顶着 gap_ci、p 值列顶着 p_value 直接进图。
比值型指标该用对数轴。 线性轴上 OR = 0.5 和 OR = 2 是一对等效的相反效应,看起来却一个离 1 很近、一个很远。要对数轴得用 forest(x_trans = "log")——plot_forest() 没有暴露这个参数,这是它目前做不到的事。
画布小了会静默裁掉,别拍脑袋设尺寸。 森林图是个 gtable,行高列宽是按毫米写死的(plot_forest() 自动值:顶部 8 mm、表头 12 mm、每个数据行 10 mm、底部 15 mm)。画布比它小,超出的部分直接被切掉——不报错、不警告,页面上就是一张顶线贴边、第一行就是数据的图,除非你数得出表头应该在那儿,否则根本看不出少了东西。反过来画布太大就是一圈留白。
单位统一用厘米。 这一摊里同时躺着三种单位:gtable 内部是毫米,plot_forest(save_width / save_height) 是厘米,而 R 图形设备默认按英寸开。三种混着用,脑子里换算一次就错一次。既然 plot_forest 的存图参数是厘米,画布也一律用厘米,然后取整数——22 × 20 这种,不要 7.68 × 6.50。
尺寸不用猜,量得出来:
#| eval: false
p <- plot_forest(...) # 或 forest(...)
grid::convertWidth(sum(p$widths), "cm", valueOnly = TRUE) # 自然宽度
grid::convertHeight(sum(p$heights), "cm", valueOnly = TRUE) # 自然高度
量出来之后往上取整、再多给两三厘米当上下留白,这是常规做法:宁可四周空一圈,也不要贴着边——贴边一旦差个一两毫米就是截断,而截断是看不出来的。行数变了就重量一次。
配色
三个色,各管一件事:#B2182B 显著、#4575B4 不显著、#F0F0F0 斑马底纹。
按显著性给 CI 上色是有争议的——它把一个连续量(p 值)压成了两档,而 p = 0.049 和 p = 0.051 之间没有实质区别。用它的理由只有一个:图上行数多到一眼扫不完时,颜色帮读者先定位。行数少就别上色,黑色一种更干净。
斑马底纹的作用是横向对齐——行多的时候,眼睛从最左边的标签扫到最右边的 p 值容易串行。



