← 返回 Tessera
折线4 张图orange(Tessera Toy,5 棵树 × 7 个时间点)2026-08-11

multi_line

同一个连续指标随时间变化时,多条序列的轨迹是否一致,哪里开始分开?

需要的输入
时间或有序位置 + 连续数值;多序列时再加 1 个分组变量
示例数据
orange(Tessera Toy,5 棵树 × 7 个时间点)
依赖
ggplot2 · ggpubr · biopalette
配色
walter_white2
成图预览另有 3 张在配方里
multi_line

什么时候用它

折线图用来读沿一个有序轴的变化:时间、年龄、剂量、距离或步骤。横轴必须有真实顺序;如果 x 只是几个没有先后的类别,把点连起来会凭空制造“过程”。

单条线看变化方向和速度;多条线则看各组是否一起动、哪一组更快、何时开始分离,以及轨迹有没有交叉。

怎么读

  1. 先沿横轴追方向。 上升、下降、平台或拐点出现在哪里。
  2. 再比较斜率。 同一段时间内,哪条线变化更快。
  3. 看间距。 线之间逐渐拉开,说明组间差异随时间扩大;逐渐靠拢则相反。
  4. 找交叉。 两条线交换高低顺序时,单独比较起点或终点都会漏掉信息。
  5. 确认点的位置。 线段之间没有真实观测;折线只是连接相邻点,不代表中间过程真的线性。

常见陷阱

  • 横轴无序却连线。 A / B / C 三组如果没有先后关系,换一下排列就会得到另一条“趋势”;那应该画点图或柱图。
  • 排序错了。 geom_line() 会按 x 排序,但字符串日期和月份名称可能不是时间顺序。绘图前先把 x 转成 Date、数值或有序 factor。
  • 只画线不画点。 读者会把整条线当成连续测量。观测点不密时,叠一层小圆点能诚实地告诉人数据在哪里。
  • 把平滑线当成原始数据。 loess 曲线是局部估计,不经过每一个观测,也会在点与点之间给出看似连续的轨迹。它适合概括形态,不适合精确读数。
  • 点太抢。 点只负责标记观测位置,不应比线更醒目;尺寸通常比散点图小。
  • 线太细。 缩进论文双栏或导出后,低饱和颜色的细线很容易消失。按最终版面检查,不按 RStudio 的大预览窗口判断。
  • 五条线重复使用两种颜色。 同色线会交叉或靠近,读者无法持续追踪。色板容量不足时,应突出部分序列,而不是循环颜色。
  • 每条线再配一种点形或线型。 颜色、形状、虚实一起变化会让图例和图面同时过载。常规彩色图先只用颜色。
  • 忽略分组。 aes(group = Tree) 不能漏;否则 ggplot 可能把不同树的点按年龄连成一条折返线。

配色

五棵树是五个无序类别,完整分组版使用五色定性色板 walter_white2。颜色沿整条线保持不变,读者才能从左到右持续追踪同一棵树。

色板少于五色时,不插值、不循环,也不删掉其余轨迹:所有线先用浅灰画成上下文,再把与色板容量相同的重点序列覆盖在上面。浅灰表示“仍在图中,但当前不由色板编码”,不是第六个类别。

配方

方法绘图系统什么时候选它
Ageom_line() + geom_point()ggplot2单条序列;线表达相邻观测的连接,点保留真实测量位置
Baes(color = group, group = group)ggplot2组数不多、色板容量足够,每条序列都能得到独立颜色
C背景线 + 高亮线ggplot2序列数多于可用颜色,只突出重点序列,其余保留为灰色上下文
Dgeom_smooth(method = "loess")ggplot2只想展示总体变化形态,接受隐藏真实观测位置与局部波动

数据与公共样式

orange 已收进 Tessera Toy。五棵树在同样的七个年龄点测量树干周长,是标准的多序列纵向数据。

library(ggplot2)
library(ggpubr)
library(biopalette)

d <- read.csv("content/tessera/data/csv/orange.csv")
d$Tree <- factor(d$Tree, levels = sort(unique(d$Tree)))

tree_colors <- setNames(
  get_palette("walter_white2", type = "qualitative"),
  levels(d$Tree)
)

line_scales <- list(
  scale_x_continuous(
    breaks = c(0, 400, 800, 1200, 1600),
    limits = c(0, 1650),
    expand = expansion(mult = c(0, 0.01))
  ),
  scale_y_continuous(
    breaks = seq(25, 225, 50),
    limits = c(20, 225),
    expand = expansion(mult = c(0, 0))
  )
)

line_theme <- theme_pubr(base_size = 13, legend = "right") +
  theme(
    panel.grid.major = element_line(color = "#E5E3DC", linewidth = 0.35),
    panel.grid.minor = element_blank(),
    axis.line = element_line(color = "#333330", linewidth = 0.45),
    axis.ticks = element_line(color = "#333330", linewidth = 0.4),
    plot.title = element_text(face = "bold", size = 15, hjust = 0),
    plot.subtitle = element_text(color = "grey35", hjust = 0),
    legend.title = element_text(face = "bold"),
    legend.key.height = grid::unit(5, "mm"),
    plot.margin = margin(14, 16, 12, 12)
  )

A · 单条序列

线负责连接相邻年龄,点负责标出七次真实测量。两者颜色一致;点比散点图小,避免把线切成一串珠子。

#| fig: single
#| fig-width: 7.4
#| fig-height: 5.1
one_tree <- subset(d, Tree == "4")

ggplot(one_tree, aes(age, circumference)) +
  geom_line(linewidth = 1.05, color = "#5A5A56") +
  geom_point(size = 2.4, color = "#5A5A56") +
  line_scales +
  labs(
    title = "One tree, seven observations",
    x = "Age (days)",
    y = "Trunk circumference (mm)"
  ) +
  line_theme
multi_line — single
multi_line-single

B · 五条序列全部着色

colorgroup 都指向 Tree。点保持相同圆形,不再增加 shape;图例只需要解释颜色。

#| fig: grouped
#| fig-width: 7.8
#| fig-height: 5.2
ggplot(d, aes(age, circumference, color = Tree, group = Tree)) +
  geom_line(linewidth = 1.05, alpha = 0.92) +
  geom_point(size = 2.35, alpha = 0.95) +
  scale_color_manual(values = tree_colors) +
  line_scales +
  labs(
    title = "Orange tree growth",
    subtitle = "Five trees measured at the same seven ages",
    x = "Age (days)",
    y = "Trunk circumference (mm)",
    color = "Tree"
  ) +
  line_theme
multi_line — grouped
multi_line-grouped

C · 色板不足时突出部分序列

下面故意使用只有两色的 gene_red。Tree 2 与 Tree 4 是当前重点;其余三棵树仍以浅灰显示完整轨迹,但不进入颜色图例。

#| fig: highlighted
#| fig-width: 7.8
#| fig-height: 5.2
focus_levels <- c("2", "4")
context <- subset(d, !Tree %in% focus_levels)
focus <- subset(d, Tree %in% focus_levels)
focus$Tree <- factor(focus$Tree, levels = focus_levels)
focus_colors <- setNames(
  get_palette("gene_red", type = "qualitative"),
  focus_levels
)

ggplot() +
  geom_line(
    data = context,
    aes(age, circumference, group = Tree),
    color = "#D2D0C9",
    linewidth = 0.85
  ) +
  geom_point(
    data = context,
    aes(age, circumference),
    color = "#D2D0C9",
    size = 1.9
  ) +
  geom_line(
    data = focus,
    aes(age, circumference, color = Tree, group = Tree),
    linewidth = 1.2
  ) +
  geom_point(
    data = focus,
    aes(age, circumference, color = Tree),
    size = 2.55
  ) +
  scale_color_manual(values = focus_colors) +
  line_scales +
  labs(
    title = "Two highlighted, three kept as context",
    subtitle = "A two-color palette should encode two lines, not repeat across five",
    x = "Age (days)",
    y = "Trunk circumference (mm)",
    color = "Highlighted tree"
  ) +
  line_theme
multi_line — highlighted
multi_line-highlighted

灰线应当刚好看得见:太深会和重点线竞争,太浅则等于删掉数据。正式输出时要结合页面背景和最终缩放再检查一次。

D · 平滑曲线,不显示观测点

geom_smooth() 在每棵树内部独立拟合 loess。这里关闭置信带,也不叠观测点,让画面只保留五条连续轨迹;代价是读者看不出每棵树其实只有七次测量。

#| fig: smooth
#| fig-width: 7.8
#| fig-height: 5.2
ggplot(d, aes(age, circumference, color = Tree, group = Tree)) +
  geom_smooth(
    method = "loess",
    formula = y ~ x,
    se = FALSE,
    span = 0.85,
    linewidth = 1.15
  ) +
  scale_color_manual(values = tree_colors) +
  line_scales +
  labs(
    title = "Smoothed growth trajectories",
    subtitle = "Loess summarizes shape; the seven observation times are intentionally hidden",
    x = "Age (days)",
    y = "Trunk circumference (mm)",
    color = "Tree"
  ) +
  line_theme
multi_line — smooth
multi_line-smooth

这张图适合看整体形状,比折线版柔和;如果读者需要判断采样是否稀疏、某个拐点是不是由单个观测造成,仍应回到 B。

四种方法怎么选

  1. 只有一条序列,选 A。
  2. 序列数不超过定性色板容量,选 B。
  3. 序列数超过可用颜色,选 C:保留全部上下文,只突出当前真正需要比较的几条。
  4. 只强调宏观变化形态、不要求读出真实测量位置,才选 D。
运行环境4 个包 · 2026-08-11T16:23:51.405+0800

R version 4.5.1 (2025-06-13 ucrt) · x86_64-w64-mingw32

  • biopalette 0.1.0
  • ggplot2 4.0.3
  • ggpubr 0.6.2
  • grid 4.5.1