如何使用嵌套类别轴绘制图表?

7

我正在绘制一些既有类别又有子类别的数据(请参见下面的示例数据),我想将它们嵌套显示(此示例是在Excel中创建的):

Nested categories from Excel
我在R中想到的最好方法是创建一个包含所需名称的新列,如下所示:

df <- data.frame(main.cat = c("A", "A", "B", "B", "B", "C"),
                 second.cat = c("a1", "a2", "b1", "b2", "b3", "c1"),
                 value = c(2, 3, 4, 2.5, 1.5, 2.3))

df$x.labels <- paste(df$second.cat, df$main.cat, sep = "\n")

ggplot(data = df, aes(x = x.labels, y = value)) + geom_point()

这至少保留了两级类别,但是复制了所有主要类别标签: enter image description here
有人知道是否有更好的方法,能使其看起来更像Excel的输出吗?
2个回答

5

我认为使用面向方面的方法是可以的:

library(ggplot2)
library(gtable)
library(grid)

df <- data.frame(main.cat = c("A", "A", "B", "B", "B", "C"),
                 second.cat = c("a1", "a2", "b1", "b2", "b3", "c1"),
                 value = c(2, 3, 4, 2.5, 1.5, 2.3))

p = ggplot(data = df, aes(x = second.cat, y = value)) + 
   geom_point() + facet_grid(.~main.cat, space = "free_x", scales = "free_x") +
   theme(strip.background = element_rect(fill = NA))

但是如果你想要更接近Excel的外观,一种方法是使用gtable函数来提取条带并将其插入到刻度标记标签下方,然后在主要类别之间插入一些边界线。请注意,下面的代码是针对您的示例数据而编写的。

p = p + theme(panel.spacing = unit(0, "lines"))
g = ggplotGrob(p)
 gtable_show_layout(g)  # to see the layout

# Add a row below the x-axis tick mark labels,
# the same height as the strip
g = gtable_add_rows(g, g$height[7], 9)

# Get the strip grob
stripGrob = gtable_filter(g, "strip")

# Insert the strip grob into the new row
g = gtable_add_grob(g, stripGrob, 10, 5, 10, 9)

# remove the old strip
g = g[-7, ]

# Insert line grobs as boundary lines between major categories
linesGrob = linesGrob(gp = gpar(col = "grey75"))
for(i in c(6,8))  g = gtable_add_grob(g, linesGrob, t=8, l=i, b=9, r=i)

# Insert new columns of zero width to take the line grobs for the first and last boundary lines
for(i in c(4, 10)) {
   g = gtable_add_cols(g, unit(0, "lines"), i)
   g = gtable_add_grob(g, linesGrob, t=8, l=i+1, b=9, r=i+1)
}

grid.newpage()
grid.draw(g)

enter image description here

编辑 一个粗略的尝试通用化

library(ggplot2)
library(gtable)
library(grid)

df <- data.frame(main.cat = c("A", "A", "B", "B", "C", "D"),
                 second.cat = c("a1", "a2", "b1", "b2", "c1", "d1"),
                 value = c(2, 3, 4, 2.5, 1.5, 2.3))

p = ggplot(data = df, aes(x = second.cat, y = value)) + 
   geom_point() + facet_grid(.~main.cat, space = "free_x", scales = "free_x") +
   theme(strip.background = element_rect(fill = NA))


p = p + theme(panel.spacing = unit(0, "lines"))
g = ggplotGrob(p)
 gtable_show_layout(g)  # to see the layout

# Get the indices for the panels (t=top, l=left, ...
panels <- c(subset(g$layout, grepl("panel", g$layout$name), se=t:r))

# Get the strip grob
stripGrob = gtable_filter(g, "strip")

 # Its height is
 height = stripGrob$height

# Add a row below the x-axis tick mark labels,
# the same height as the strip. 
g = gtable_add_rows(g, height, unique(panels$b+1))

# Insert the strip grob into the new row
g = gtable_add_grob(g, stripGrob, 
                      t = unique(panels$b+2), 
                      l = min(panels$l), 
                      r = max(panels$r))

# Insert line grobs as boundary lines between major categories
linesGrob = linesGrob(gp = gpar(col = "grey75"))
panelsR = panels$r[-length(panels$r)]
for(i in panelsR+1)  g = gtable_add_grob(g, linesGrob, 
                       t=unique(panels$b+1), 
                       l=i, 
                       b=unique(panels$b+2))

# Insert new columns of zero width to take the line grobs for the first and last boundary lines
 panelBound = c(4, max(panels$r)+1)
for(i in panelBound) {
   g = gtable_add_cols(g, unit(0, "lines"), i)
   g = gtable_add_grob(g, linesGrob, 
                    t=unique(panels$b+1), 
                    l=i+1, 
                    b=unique(panels$b+2))
}

# remove the old strip
g = g[-7, ]

# Draw it
grid.newpage()
grid.draw(g)

第二个版本正是我想要的,谢谢。我不熟悉 gtable 包,所以我显然需要查阅一些资料来了解如何扩展它以处理不同类别的数据集! - Mike Lynch
Baptiste已经准备了一些关于gtable函数的笔记,可在https://github.com/baptiste/gtable/wiki/Description上找到。 - Sandy Muspratt

3

未经测试,但尝试:

ggplot(data=df, aes(x=second.cat, y=value)) + geom_point() + facet_grid(~ main.cat, scales = 'free')

尽管如此,每个 main.cat 的宽度将是相同的,并且相同标签只能被放置在顶部。


分面排序似乎可以工作,但是每个分面中仍会出现所有的子分类(例如,在A部分中,您仍然有b1、b2、b3、c1;在B部分中,您仍然有a1、a2、c1等)。有没有办法去除不需要的类别? - Mike Lynch
1
使用参数scales='free'调用facet_grid函数(已编辑的回答)。 - MrGumble

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接