HOWTO · R

如何在 R 的 ggplot2 中修改坐标轴刻度标签

介绍如何修改 ggplot2 离散轴和连续轴的刻度标签、设置格式,并解决长标签重叠问题。

本页内容

对于因子或字符型刻度,使用 scale_x_discrete(labels = ...) 重命名;对于数值刻度,使用 scale_x_continuous(breaks = ..., labels = ...) 设置位置和格式。旋转等外观调整应放在 theme(axis.text.x = element_text(...)) 中。labs(x = ...) 修改的是坐标轴标题,而不是刻度标签。若要处理纵轴,把这些函数中的 x 换成 y

示例需要 R 和 ggplot2;连续轴示例还使用 scales 包格式化标签。代码已在 R 4.1.2、ggplot2 3.3.5 和 scales 1.1.1 中验证。这里的尺度参数也存在于当前 ggplot2 中,但不同版本的主题细节可能略有差异。

任务 ggplot2 设置
修改坐标轴标题 labs(x = "Title")labs(y = "Title")
重命名因子或字符型刻度 scale_x_discrete(labels = ...)
设置数值刻度位置和文本 scale_x_continuous(breaks = ..., labels = ...)
旋转或设置刻度文本样式 theme(axis.text.x = element_text(...))
格式化 Date scale_x_date(breaks = ..., labels = ...)

创建基准 ggplot2 图表

下面始终使用同一个小型数据框,以便清楚比较每项修改。plan 列是因子,因此 ggplot2 为它创建离散 x 轴;orders 列是数值,因此使用连续 y 轴。

library(ggplot2)

plan_sales <- data.frame(
  plan = factor(
    c("starter", "standard", "premium", "enterprise"),
    levels = c("starter", "standard", "premium", "enterprise")
  ),
  orders = c(180, 250, 210, 140)
)

base_plot <- ggplot(plan_sales, aes(x = plan, y = orders)) +
  geom_col(fill = "#2c7fb8", width = 0.7) +
  labs(x = "Plan", y = "Orders") +
  theme_minimal(base_size = 16)

base_plot
ggsave(
  "ggplot-default-axis-tick-labels.png",
  plot = base_plot, width = 7.4, height = 4.82, dpi = 100, bg = "white"
)

显示未经修改的 x 轴和 y 轴刻度标签的默认 ggplot2 图表。

基准图在 x 轴显示四个已存储的因子值,并在 y 轴显示自动选择的数值刻度。labs(x = "Plan") 设置 x 轴标题“Plan”,不会改写 starterstandard 等刻度文本。

选择尺度前,应检查映射变量,不要根据图形外观猜测。str(plan_sales$plan) 显示因子,str(plan_sales$orders) 显示数值向量。即使分类代码看起来像 "1""2",因子仍是离散型;即使当前只有几个唯一值,数值列也仍是连续型。只有当新类型符合数据的实际含义时才转换类型。

重命名离散轴刻度标签并调整顺序

在离散尺度中,应成对指定 breakslabelsbreaks 选择需要显示刻度的底层数据值,labels 提供这些位置上打印的文本。显式配对可以防止尺度顺序改变后标签错位。

下面还演示安全调整分类顺序:先在数据副本中更改因子水平,再把同一序列传给 breaks。命名标签向量让映射关系在运行前就清晰可见。

plan_order <- c("premium", "standard", "starter", "enterprise")
plan_names <- c(
  premium = "Premium",
  standard = "Standard",
  starter = "Starter",
  enterprise = "Enterprise"
)

ordered_sales <- plan_sales
ordered_sales$plan <- factor(ordered_sales$plan, levels = plan_order)

discrete_plot <- ggplot(ordered_sales, aes(x = plan, y = orders)) +
  geom_col(fill = "#2c7fb8", width = 0.7) +
  scale_x_discrete(
    breaks = plan_order,
    labels = plan_names[plan_order]
  ) +
  labs(x = "Plan", y = "Orders") +
  theme_minimal(base_size = 16)

discrete_plot
ggsave(
  "ggplot-discrete-axis-tick-labels.png",
  plot = discrete_plot, width = 7.4, height = 4.82, dpi = 100, bg = "white"
)

重新命名并调整离散 x 轴类别顺序的 ggplot2 图表。

显示顺序应为 Premium、Standard、Starter、Enterprise。小写数据值保持不变,改变的只是显示。若同一规则适用于所有刻度,可使用标签函数。例如,scale_x_discrete(labels = abbreviate) 会把全部刻度值传给 base R 的 abbreviate()。自定义 function(x) 必须返回与输入等长的字符向量。

如果只需改名并保留现有顺序,可省略 breaks,传入覆盖所有显示值的命名向量,例如 c(starter = "Starter", standard = "Standard", premium = "Premium", enterprise = "Enterprise")。如果显示集合和顺序都有明确要求,上面的显式配对更可靠。筛选数据后,从左到右读取 x 轴,与此处说明的预期顺序核对。

不要混用 breakslimits。离散尺度中的 breaks 只选择显示哪些刻度,不改变尺度接受的类别;limits 定义允许的值及顺序。范围外的值会变成缺失值,geom_col() 等图层随后可能在发出警告的同时删除对应行。如果顺序属于数据含义,调整因子水平更清楚;仅在确实要排除值时使用 limits

设置连续轴刻度位置和格式

数值轴需要连续尺度。把准确位置传给 breaks,再把等长文本或格式化函数传给 labels。如果数值可能变化,函数更安全,因为 ggplot2 会用最终的刻度位置调用它。

下面的散点图把马力刻度放在 100、200 和 300,并用 scales::label_number() 添加单位。绘图前直接调用格式化函数,还可以先检查实际标签文本。

hp_breaks <- c(100, 200, 300)
hp_formatter <- scales::label_number(suffix = " hp", accuracy = 1)
hp_formatter(hp_breaks)

continuous_plot <- ggplot(mtcars, aes(x = hp, y = mpg)) +
  geom_point(size = 3, color = "#d95f0e") +
  scale_x_continuous(
    breaks = hp_breaks,
    labels = hp_formatter
  ) +
  labs(x = "Engine power", y = "Fuel economy (mpg)") +
  theme_minimal(base_size = 16)

continuous_plot
ggsave(
  "ggplot-continuous-axis-tick-labels.png",
  plot = continuous_plot, width = 7.4, height = 4.82, dpi = 100, bg = "white"
)
[1] "100 hp" "200 hp" "300 hp"

自定义连续轴刻度位置和标签格式的 ggplot2 图表。

x 轴应恰好显示这三个位置和标签。数值 y 轴使用对应的 scale_y_continuous()。scales 中的其他格式化函数可显示千位分隔符、百分比、货币等;由于只改变标签文本,数据仍保持数值型。

当位置本身有意义时,应像这些马力值一样显式指定 breaks。如果只需控制格式,则省略 breaks,让 ggplot2 根据数据范围选择位置,只把格式化函数传给 labels。不要为了添加后缀而把数值列预先转成字符串,否则尺度类型会改变,并可能按字典顺序而不是数值间距排列。格式化函数不会改变计算或点的位置。

日期属于另一种尺度。映射列继承 Date 时,应使用 scale_x_date() 或对应的 y 轴函数,不要强行使用数值或离散尺度。它需要适用于日期的刻度位置与格式,但 breakslabels 的职责相同。

旋转重叠的刻度标签

修改标签内容与设置文本样式是两件事。文字正确但相互重叠时,保留原尺度并调整主题中的 axis.text.x。这里 angle = 35 旋转每个标签,hjust = 1 将右边缘对齐到刻度位置;较大的基础字号有助于窄图保持可读。

channel_sales <- data.frame(
  channel = c(
    "North America Online",
    "Europe Retail Stores",
    "Asia Pacific Partners",
    "Latin America Direct"
  ),
  orders = c(260, 220, 195, 150)
)

rotated_plot <- ggplot(channel_sales, aes(x = channel, y = orders)) +
  geom_col(fill = "#31a354", width = 0.65) +
  labs(x = "Sales channel", y = "Orders") +
  theme_minimal(base_size = 18) +
  theme(
    axis.text.x = element_text(angle = 35, hjust = 1, vjust = 1)
  )

rotated_plot
ggsave(
  "ggplot-rotated-axis-tick-labels.png",
  plot = rotated_plot, width = 7.4, height = 4.82, dpi = 100, bg = "white"
)

旋转并对齐较长的 x 轴刻度标签以避免重叠的 ggplot2 图表。

旋转会保留每个类别名称。如果仍然碰撞,缩短文字或加宽图表通常比继续增大角度更清晰。在已验证的 ggplot2 3.3.5 环境中,scale_x_discrete(guide = guide_axis(n.dodge = 2)) 可把离散标签错开为两行;当前 ggplot2 API 中也有该函数。它适合保留水平文字,但会占用更多垂直空间。更多选择参见在 base R 或 ggplot2 中旋转坐标轴标签

有意隐藏标签、刻度线或刻度位置

这些设置并非同义,应根据要移除的对象进行选择。

control_plot <- base_plot +
  theme_classic(base_size = 16)

labels_hidden <- control_plot +
  theme(axis.text.x = element_blank())

ticks_hidden <- control_plot +
  theme(axis.ticks.x = element_blank())

breaks_hidden <- control_plot +
  scale_x_discrete(breaks = NULL)

control_plot 使用会绘制短刻度线的 theme_classic(),因此每项变化都有可观察的基准。labels_hidden 只移除 x 轴文字;ticks_hidden 只移除短刻度线,类别名称仍保留;breaks_hidden 让尺度不生成主要 x 轴刻度位置,与其关联的标签和刻度线都会消失。这些表达式都不会筛掉 plan_sales 的行。

尺度级的 labels = NULL 会保留刻度位置但隐藏标签。若标签按尺度定义就不应显示,可选择它;若尺度仍有意义,只是不希望某个主题绘制文本,则使用 element_blank()。隐藏分类标签可能让读者无法识别柱或点,因此类别仍重要时要提供直接标注或其他图例。

这三个对象没有保存为图片,因为可选择的代码和精确说明更容易比较。可在 R 控制台打印 ticks_hidden,并与 control_plot 对照。三个结果中的柱和高度都应保持不变,只有坐标轴引导元素不同。若几何图形变化或出现删除行的警告,应检查图中其他位置的 limits 调用,而不是这里的主题设置。

排查错误或缺失的刻度标签

如果 ggplot2 报告向离散尺度提供了连续值,或反过来,应先用 str() 检查映射列。因子和字符列使用离散尺度,数值列使用连续尺度,Date 和日期时间对象使用相应日期尺度。不要只为消除错误而转换数据;只有新类型符合值的真实含义时才转换。

labels 使用字符向量时,务必与显式 breaks 配对并保持长度一致。否则筛选、重排或添加类别后标签可能错位。plan_names 这样的命名映射说明每个存储值对应的显示名称。如果所有刻度值都应用同一种变换,函数可以避免长度问题。

柱形缺失或删除行的警告往往指向 limits,而不是 labels。检查尺度范围是否遗漏数据中的值。若目标只是减少可见标签数量,应使用 breaks。关于有意缩放或控制范围,请参阅设置 ggplot2 坐标轴范围而不把它与刻度位置混淆

最后,确认要改的是否其实是坐标轴标题。labs(x = "Plan") 和 x 尺度的 name 参数设置标题,labels 参数控制刻度文本。区分这两个职责,可避免标题已改变而类别名或数字标签毫无变化的困惑。

对于大多数图表,应根据映射数据类型选择尺度,需要精确文本时成对设置 breakslabels,并只用 theme() 控制外观。这样既能让坐标轴修改可预测,也不会改变底层观测值。