Menu

R 语言条形图:用 barplot() 绘制计数和频数表

如何在 R 中用 barplot() 画条形图:从命名向量或 table() 出发,用矩阵画分组和堆叠条形图,调整样式,以及 geom_col 与 geom_bar 的区别。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

条形图用来比较类别

条形图回答的是“每一类各有多少?”:每个类别一根条形,条形的长度表示该类别的值。在 R 中,基础函数是 barplot(),它的输入就是一个表示高度的向量。给向量加上名字,这些名字就会成为条形下方的标签:

visits <- c(Mon = 42, Tue = 51, Wed = 47, Thu = 60, Fri = 78)

barplot(visits,
        main = "Store visits by day",
        ylab = "Visits")

五根灰色条形,按星期几标注,从不算高的周一一路升到周五的峰值,几乎是周一的两倍高。什么都不用安装,什么都不用配置:只要你能构建一个命名向量,就能把它画成图。

本页的在线编辑器只显示文本输出,所以这里的 barplot() 调用都是静态代码片段,需要粘贴到本地的 R 会话中运行。不过画图之前的计数步骤可以直接运行,而这恰恰是最关键的一步。

实际工作流程:先 table(),再 barplot()

实际工作中,你很少能直接拿到整理好、计好数的高度。你手上的通常是一列原始的类别标签:每份问卷回答、每个订单、每个用户各占一行,而计数才是真正要做的工作。table() 一次调用就能完成计数,它的输出是一个命名的计数向量,正是 barplot() 想要的:

然后只需再加一行就能画图:

barplot(sort(t, decreasing = TRUE),
        main = "Survey responses",
        ylab = "Count")

先 table() 再 barplot(),这两步是 R 中最常见的条形图流程。先对频数表排序是一个几乎零成本的改进:按高度排列的条形比按字母排列的更容易读,除非类别本身有自然的顺序(星期、月份、剂量等级)。这种情况下,用水平顺序设置正确的因子,就能防止 table() 把它们按字母排序。如果条形需要真正的聚合,也就是每组的总和或均值,而不是行数,就先做分组汇总,再把结果交给 barplot()。

标签与样式

标签通常来自向量的名字,但 names.arg 可以覆盖它们。当数据里的编码不适合直接给读者看时,这很有用:

barplot(visits,
        names.arg = c("Monday", "Tuesday", "Wednesday",
                      "Thursday", "Friday"),
        col    = "steelblue",
        border = NA,
        main   = "Store visits by day",
        las    = 2)
  • col 设置条形的填充色;传入一个向量可以给每根条形单独上色,但别动不动就用彩虹色:颜色应该有含义,比如突出你正在讨论的那根条形。
  • border = NA 去掉条形的边框,让图看起来更简洁。
  • las = 2 把坐标轴标签旋转成与坐标轴垂直,用来解决类别名太长、互相重叠的问题。如果名字确实很长,再配合加大底部边距(par(mar = c(8, 4, 4, 2)))。

用矩阵画分组和堆叠条形图

要同时展示两个分类维度,比如每个季度每个渠道的销售额,就把一个矩阵交给 barplot()。每一列对应类别轴上的一个位置,各行则成为其中的子条形:

sales <- matrix(c(12, 18,   15, 22,   20, 19,   24, 27),
                nrow = 2,
                dimnames = list(c("Online", "Retail"),
                                c("Q1", "Q2", "Q3", "Q4")))

# Grouped: rows stand next to each other within each quarter
barplot(sales, beside = TRUE,
        col = c("steelblue", "orange"),
        legend.text = rownames(sales),
        main = "Sales by channel and quarter")

# Stacked: rows pile up into one total bar per quarter (the default)
barplot(sales, beside = FALSE,
        col = c("steelblue", "orange"),
        legend.text = rownames(sales))

beside = TRUE 让两个渠道在每个季度内并排站立,最适合直接比较各渠道。默认的堆叠方式强调的是每个季度的总量,各部分的构成显示在每根条形内部;它如实反映了总量,但由于内部各段的起点不同,很难在不同条形之间比较它们。legend.text = rownames(sales) 添加的图例,能让两种版本都清晰易读。

还有一个开关:horiz = TRUE 让条形沿水平方向排列。类别很多、名字又很长时,这是最友好的布局:每个标签都单独占一行,紧挨着自己的条形,不需要旋转。

条形图还是直方图?

搜索的人经常把这两者搞混,花三十秒分清它们很值得:条形图比较的是互不相同的类别,坐标轴是一组标签,条形之间有间隔,调换顺序完全没问题。直方图把一个数值变量分箱,坐标轴是连续的刻度,条形紧挨着,因为各个箱是连续的区间,调换顺序毫无意义。如果你的列里是文字(“Yes”、“Chrome”、“Q3”),用 barplot(table(x))。如果是测量值(身高、价格、响应时间),用直方图和 hist(x)。

ggplot2:geom_col 与 geom_bar

ggplot2 版本有两个几何对象,区别在于计数是否已经做好:

library(ggplot2)

# geom_bar(): raw observations in, counting done for you
ggplot(survey, aes(x = response)) +
    geom_bar(fill = "steelblue")

# geom_col(): heights already computed, one row per bar
totals <- data.frame(day = c("Mon", "Tue", "Wed", "Thu", "Fri"),
                     visits = c(42, 51, 47, 60, 78))

ggplot(totals, aes(x = day, y = visits)) +
    geom_col(fill = "steelblue")

geom_bar() 相当于把 table() 内置到了图里:它接收每个观测值一行的数据,然后计数。geom_col() 接收已经聚合好的数据,把一个 y 列映射为条形高度,相当于经典的 barplot()。把已经计好数的数据交给 geom_bar() 是初学者的典型陷阱:它会统计行数(每行一个),结果每根条形都一样高。

要点回顾

  • barplot(heights) 绘制一个命名向量,而 table(x) 正好能从原始类别数据生成这种形状,所以 barplot(table(x)) 是最核心的惯用写法。
  • 画图之前先给频数表排序(或设置因子水平);条形的顺序本身就是要传达的信息的一部分。
  • 用矩阵可以画分组(beside = TRUE)或堆叠(默认)条形图;记得加上 legend.text = rownames(m)。
  • names.arg、col、las = 2 和 horiz = TRUE 负责处理标签;名字很长时横向条形图最合适。
  • 类别 → 条形图;数值分箱 → 直方图。在 ggplot2 中:原始行 → geom_bar(),算好的高度 → geom_col()。

下一篇:ggplot2 本身,一套图形语法,用同样几个可组合的部件就能构建出所有这些图表,乃至更多。

常见问题

在 R 中如何画条形图?

对一个表示高度的向量调用 barplot()。给向量加上名字,或者用 table() 生成它(它的输出本身就带名字),每个元素就会变成一根带标签的条形:barplot(table(df$category))。

在 R 中如何画分组或堆叠条形图?

传入一个矩阵。每一列对应类别轴上的一个位置;beside = TRUE 把各行画成并排的条形(分组),默认的 beside = FALSE 则把它们堆叠起来。再加上 legend.text = rownames(m),读者就能分清各行。

R 中 barplot() 和 hist() 有什么区别?

barplot() 比较的是互不相同的类别:每个类别一根条形,条形之间有间隔。hist() 把一个数值变量划分成连续的区间:条形紧挨着,因为坐标轴是连续的刻度。如果你的变量是文字,用 barplot(table(x));如果是数字,用 hist(x)。

在 ggplot2 中应该用 geom_bar 还是 geom_col?

geom_bar() 会替你计数:给它原始观测值,它会统计每个类别的数量。geom_col() 绘制的是你已经算好的值:给它每根条形一行数据,外加一列高度。把已经计好数的数据交给 geom_bar() 是经典错误,这种情况需要用 geom_col()。

Coddy 编程语言插图

和 Coddy 一起学编程

开始