【问题标题】:Multirow axis labels with nested grouping variables具有嵌套分组变量的多行轴标签
【发布时间】:2013-08-12 12:25:09
【问题描述】:

我希望两个不同嵌套分组变量的级别出现在绘图下方的单独行上,而不是在图例中。我现在拥有的是这段代码:

data <- read.table(text = "Group Category Value
    S1 A   73
    S2 A   57
    S1 B   7
    S2 B   23
    S1 C   51
    S2 C   87", header = TRUE)

ggplot(data = data, aes(x = Category, y = Value, fill = Group)) + 
  geom_bar(position = 'dodge') +
  geom_text(aes(label = paste(Value, "%")), 
            position = position_dodge(width = 0.9), vjust = -0.25)

我想要的是这样的:

有什么想法吗?

【问题讨论】:

  • 要将标签实际放置在您所描绘的面板之外需要一些严肃的grid 图形魔法。但是,如果您可以接受将它们放在面板中,geom_text 可以为您提供解决方案。
  • 我在打电话,但这个问题已经被问过好几次了。我敢肯定,有进取心的 Google 员工会找到一个副本。
  • @joran 我找不到重复的问题。所以我希望我没有使解决方案过于复杂。
  • 谢谢弗兰克,但这不是我想要的。很棒的工作 agstudy,我也试图找到副本(再次,没有成功)并使用 Drew Steen 的建议,它有点工作,但你的解决方案是完美的!
  • xmax = Inf 应该为 annotation_custom 解决问题(最好是 annotate("segment", ...)annotate("hline", ...)

标签: r ggplot2 bar-chart axis-labels


【解决方案1】:

ggplot2 2.2.0 以来,facet_wrap() 中的 strip.position 参数和 facet_grid() 中的 switch 参数现在使得通过刻面创建该图的简单版本变得相当简单。要使绘图具有不间断的外观,请将 panel.spacing 设置为 0。

这是使用数据集的示例,每个类别的组数与@agtudy 的回答不同。

  • 我使用scales = "free_x" 从没有它的类别中删除额外的组,尽管这并不总是可取的。
  • strip.position = "bottom" 参数将分面标签移动到底部。我连同strip.background 一起删除了条形背景,但我可以看到在某些情况下保留条形矩形会很有用。
  • 我使用width = 1 使每个类别中的条形触摸 - 默认情况下它们之间有空格。

我还在theme 中使用strip.placementstrip.background 来获取底部的条带并删除条带矩形。

ggplot2_2.2.0 或更新版本的代码:

ggplot(data = data, aes(x = Group, y = Value, fill = Group)) + 
    geom_bar(stat = "identity", width = 1) +
    geom_text(aes(label = paste(Value, "%")), vjust = -0.25) +
    facet_wrap(~Category, strip.position = "bottom", scales = "free_x") +
    theme(panel.spacing = unit(0, "lines"), 
         strip.background = element_blank(),
         strip.placement = "outside")

如果您希望所有条的宽度相同,而不管每个类别有多少组,您可以在 facet_grid() 中使用 space= "free_x"。请注意,这使用switch = "x" 而不是strip.position。您可能还想更改 x 轴的标签;我不确定它应该是什么,也许是 Category 而不是 Group?

ggplot(data = data, aes(x = Group, y = Value, fill = Group)) + 
    geom_bar(stat = "identity", width = 1) +
    geom_text(aes(label = paste(Value, "%")), vjust = -0.25) +
    facet_grid(~Category, switch = "x", scales = "free_x", space = "free_x") +
    theme(panel.spacing = unit(0, "lines"), 
         strip.background = element_blank(),
         strip.placement = "outside") + 
    xlab("Category")

旧代码版本

ggplot2_2.0.0 的代码在首次引入此功能时略有不同。我已将其保存在下面以供后代使用:

ggplot(data = data, aes(x = Group, y = Value, fill = Group)) + 
    geom_bar(stat = "identity") +
    geom_text(aes(label = paste(Value, "%")), vjust = -0.25) +
    facet_wrap(~Category, switch = "x", scales = "free_x") +
    theme(panel.margin = unit(0, "lines"), 
         strip.background = element_blank())

【讨论】:

  • 效果很华丽,解决方法很简单。尽管我认为现在更改已接受的答案并不公平。尽管如此,很好的答案。
【解决方案2】:

您可以为axis.text.x 创建自定义元素函数。

library(ggplot2)
library(grid)

## create some data with asymmetric fill aes to generalize solution 
data <- read.table(text = "Group Category Value
                   S1 A   73
                   S2 A   57
                   S3 A   57
                   S4 A   57
                   S1 B   7
                   S2 B   23
                   S3 B   57
                   S1 C   51
                   S2 C   57
                   S3 C   87", header=TRUE)

# user-level interface 
axis.groups = function(groups) {
  structure(
    list(groups=groups),
    ## inheritance since it should be a element_text
    class = c("element_custom","element_blank")  
  )
}
# returns a gTree with two children: 
# the categories axis
# the groups axis
element_grob.element_custom <- function(element, x,...)  {
  cat <- list(...)[[1]]
  groups <- element$group
  ll <- by(data$Group,data$Category,I)
  tt <- as.numeric(x)
  grbs <- Map(function(z,t){
    labs <- ll[[z]]
    vp = viewport(
             x = unit(t,'native'), 
             height=unit(2,'line'),
             width=unit(diff(tt)[1],'native'),
             xscale=c(0,length(labs)))
    grid.rect(vp=vp)
    textGrob(labs,x= unit(seq_along(labs)-0.5,
                                'native'),
             y=unit(2,'line'),
             vp=vp)
  },cat,tt)
  g.X <- textGrob(cat, x=x)
  gTree(children=gList(do.call(gList,grbs),g.X), cl = "custom_axis")
}

## # gTrees don't know their size 
grobHeight.custom_axis = 
  heightDetails.custom_axis = function(x, ...)
  unit(3, "lines")

## the final plot call
ggplot(data=data, aes(x=Category, y=Value, fill=Group)) + 
  geom_bar(position = position_dodge(width=0.9),stat='identity') +
  geom_text(aes(label=paste(Value, "%")),
            position=position_dodge(width=0.9), vjust=-0.25)+
  theme(axis.text.x = axis.groups(unique(data$Group)),
        legend.position="none")

【讨论】:

  • 您可能需要定义一个 heightDetails 方法以避免垂直对齐问题
【解决方案3】:

agstudy 的另一种方法是编辑 gtable 并插入一个由 ggplot2 计算的“轴”,

p <- ggplot(data=data, aes(x=Category, y=Value, fill=Group)) + 
  geom_bar(position = position_dodge(width=0.9),stat='identity') +
  geom_text(aes(label=paste(Value, "%")),
            position=position_dodge(width=0.9), vjust=-0.25)

axis <- ggplot(data=data, aes(x=Category, y=Value, colour=Group)) +
  geom_text(aes(label=Group, y=0),
            position=position_dodge(width=0.9))

annotation <- gtable_filter(ggplotGrob(axis), "panel", trim=TRUE)
annotation[["grobs"]][[1]][["children"]][c(1,3)] <- NULL #only keep textGrob

library(gtable)
g <- ggplotGrob(p)
gtable_add_grobs <- gtable_add_grob # let's use this alias
g <- gtable_add_rows(g, unit(1,"line"), pos=4)
g <- gtable_add_grobs(g, annotation, t=5, b=5, l=4, r=4)
grid.newpage()
grid.draw(g)

【讨论】:

  • +10!用ggplot2创建textGrob的好主意(避免视口低级)。你为什么使用别名?您还可以删除图例(不再需要)。
  • 我认为应该将其命名为 gtable_add_grobs 以与 add_rows 等保持一致,如果将来进行更改,则必须编辑所有这些以前的答案。
【解决方案4】:

提供相似(尽管不相同)结果的一个非常简单的解决方案是使用刻面。缺点是类别标签在上方而不是下方。

ggplot(data=data, aes(x=Group, y=Value, fill=Group)) +
  geom_bar(position = 'dodge', stat="identity") +
  geom_text(aes(label=paste(Value, "%")), position=position_dodge(width=0.9), vjust=-0.25) + 
  facet_grid(. ~ Category) + 
  theme(legend.position="none")

【讨论】:

  • 如果不是所有组都包含所有类别,您将如何更改?
  • 旧评论,但您可以drop = F 确保为任何缺少的类别留出空间
【解决方案5】:

@agstudy 已经回答了这个问题,我将自己使用它,但如果你接受更丑陋但更简单的东西,这就是我在他回答之前提出的:

data <- read.table(text = "Group Category Value
    S1 A   73
    S2 A   57
    S1 B   7
    S2 B   23
    S1 C   51
    S2 C   87", header=TRUE)

p <- ggplot(data=data, aes(x=Category, y=Value, fill=Group))
p + geom_bar(position = 'dodge') +
  geom_text(aes(label=paste(Value, "%")), position=position_dodge(width=0.9),   vjust=-0.25) +
  geom_text(colour="darkgray", aes(y=-3, label=Group),  position=position_dodge(width=0.9), col=gray) +
  theme(legend.position = "none", 
    panel.background=element_blank(),
    axis.line = element_line(colour = "black"),
    axis.line.x = element_line(colour = "white"),
    axis.ticks.x = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank()) +
  annotate("segment", x = 0, xend = Inf, y = 0, yend = 0)

这会给我们:

【讨论】:

  • 不错的解决方案 :),如果您仅包含特定于该问题的代码,可能会更好一些。额外的代码常常会使人们感到困惑。 :)
  • 您必须将 geom_bar(position = 'dodge') 替换为 geom_bar(position= 'dodge', stat='identity') 才能使其工作。
【解决方案6】:

这是另一个使用我正在为分组条形图 (ggNestedBarChart) 开发的包的解决方案:

data <- read.table(text = "Group Category Value
                   S1 A   73
                   S2 A   57
                   S3 A   57
                   S4 A   57
                   S1 B   7
                   S2 B   23
                   S3 B   57
                   S1 C   51
                   S2 C   57
                   S3 C   87", header = TRUE)

devtools::install_github("davedgd/ggNestedBarChart")
library(ggNestedBarChart)
library(scales)

p1 <- ggplot(data, aes(x = Category, y = Value/100, fill = Category), stat = "identity") +
  geom_bar(stat = "identity") +
  facet_wrap(vars(Category, Group), strip.position = "top", scales = "free_x", nrow = 1) +
  theme_bw(base_size = 13) +
  theme(panel.spacing = unit(0, "lines"),
        strip.background = element_rect(color = "black", size = 0, fill = "grey92"),
        strip.placement = "outside",
        axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        panel.grid.major.y = element_line(colour = "grey"),
        panel.grid.major.x = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_rect(color = "black", fill = NA, size = 0),
        panel.background = element_rect(fill = "white"),
        legend.position = "none") + 
  scale_y_continuous(expand = expand_scale(mult = c(0, .1)), labels = percent) + 
  geom_text(aes(label = paste0(Value, "%")), position = position_stack(0.5), color = "white", fontface = "bold")

ggNestedBarChart(p1)

ggsave("p1.png", width = 10, height = 5)

请注意,ggNestedBarChart 可以根据需要对多个级别进行分组,并且不仅限于两个(即本示例中的 Category 和 Group)。例如,使用数据(mtcars):

此示例的代码位于 GitHub 页面上。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-17
    • 2022-11-03
    • 2011-02-09
    • 1970-01-01
    相关资源
    最近更新 更多