PAYATHON 2026

如何缩小 sjPlot 预测图中成对数据的间距并保留横轴标签?

数据小陈

先看空白出现在哪里:同一组内两个预测值的距离用 dodge 调整;不同组之间的实际显示距离主要通过调整图形宽度改变;横轴两端的留白则由 expand 控制。单独设置 expand = c(0, 0) 无法消除组与组之间的空白。

横轴标签消失可能与刻度类型有关:plot_model(type = "pred") 使用的预测绘图流程可能把分类变量放在连续横轴上。此时应使用 scale_x_continuous(),并在替换刻度时保留原来的 breaks 和 labels。

为什么修改 expand 没有效果?

示例中,terms = c("Demonstrator", "Shelter") 将 Demonstrator 放在横轴上,用 Shelter 区分预测值。图中涉及三种距离:

空白位置调整方式
同一个 Demonstrator 下两个 Shelter 预测值之间调整 dodge
相邻 Demonstrator 组之间调整绘图宽度或面板宽高比
最左、最右数据与面板边缘之间调整坐标轴 expand

expand 只控制坐标范围两端的扩展量,不改变各组中心的相对位置。缩小两端留白后,数据可能占据更大的面板宽度,组间距离反而看起来更宽。ggplot2 坐标轴文档

如果使用 scale_x_discrete() 后标签和网格线消失,也不能由此判断留白已经消除。ggeffects 的绘图文档说明,其分类预测变量也使用连续横轴;直接换成离散刻度会破坏原有的刻度设置。添加新的连续刻度也可能覆盖原来用于显示分类名称的标签配置。ggeffects 横轴自定义说明

先修正示例,再调整组内间距

模拟数据代码的最后一个参数后面多了一个逗号,删除即可:

Group = factor(rep(group, times = 3))
)

模型拟合成功后,使用示例中的模型对象 mm 绘图。dodge = 0.15 是一个用来尝试较小组内间距的示例值,可以根据点和误差条是否重叠再调整:

library(ggplot2)
library(sjPlot)
library(lme4)

p <- plot_model(
  mm,
  type = "pred",
  terms = c("Demonstrator", "Shelter"),
  title = "Predicted Time in Shelters",
  axis.title = c("", "Time Spent In Shelter"),
  dodge = 0.15
) +
  scale_color_manual(
    name = "Shelter Type",
    labels = c("Demonstrated", "Non Demonstrated"),
    values = c("blue", "red")
  ) +
  theme(
    legend.position = "bottom",
    axis.text.x = element_text(size = 14),
    axis.title.y = element_text(size = 14),
    legend.text = element_text(size = 14)
  )

p

plot_model() 支持将 dodge 传给边际效应绘图函数,用来错开同一横轴位置上的预测值和误差条。减小该值会让同组数据更靠近;设为零时可能造成重叠。sjPlot 参数说明

如果想缩小不同组之间的空白,继续减小 dodge 可能让空白更明显,因为每组本身会变窄。

缩小不同组之间的显示距离

如果海报上的图显得过宽,可以先减小导出宽度,并在海报排版中保留调整后的宽高比例:

ggsave(
  filename = "predicted_shelter_time.png",
  plot = p,
  width = 5.5,
  height = 5,
  units = "in",
  dpi = 300
)

这些尺寸可以作为排版起点。保持高度不变,逐步减小宽度,可以缩短各组之间的物理距离。如果横轴文字开始重叠,就需要停止缩窄或调整标签排版。ggsave() 的 width、height 和 units 控制输出尺寸。ggsave 文档

如果希望在显示窗口内约束面板形状,也可以尝试:

p + theme(aspect.ratio = 1)

这里的 1 表示面板高度与宽度之比为 1。调整后的效果仍需结合导出尺寸检查。

调整两端留白,同时保留横轴标签

替换横轴刻度之前,先从原图读取刻度位置和标签。下面的代码适用于示例中的单面板图,并根据实际横轴类型选择刻度函数:

sx <- ggplot_build(p)$layout$panel_scales_x[[1]]

if (sx$is_discrete()) {
  p_edges <- p +
    scale_x_discrete(
      expand = expansion(add = 0.4)
    )
} else {
  x_breaks <- sx$get_breaks()
  x_labels <- sx$get_labels(x_breaks)

  p_edges <- p +
    scale_x_continuous(
      breaks = x_breaks,
      labels = x_labels,
      expand = expansion(mult = 0, add = 0.2)
    )
}

p_edges

读取原图标签,可以避免假定分类名称一定对应 1:3,也能保留原图设置好的显示顺序。这里的 0.2 和 0.4 是可调整的边缘余量,并非适用于所有图的固定值。

如果要尝试完全取消连续横轴的额外扩展,可以把 expand 改成 expansion(mult = 0, add = 0),但需要检查最外侧经过 dodge 偏移的点和误差条是否仍完整显示。

示例中的其他注意事项

  • 初始绘图使用 model27,模拟数据拟合的是 mm。复现时要使用对应的模型对象。
  • 示例使用 lmer(),拟合的是线性混合模型;不能仅凭这段代码称其为 GLMM。
  • 模拟数据没有设置随机种子,每次生成的缺失位置和拟合结果可能不同。如果出现拟合警告,应先检查模型,再判断绘图问题。
  • 由于没有提供实际图像,无法确认最显眼的空白属于哪一种。可以先只调整一个参数,分别观察组内距离、组间距离和边缘留白的变化。

备注:内容仅供参考。