在具有一个X轴的ggplot中添加第二个X轴标签

6

编辑:这里有两个很棒的解决方案,其中一个被标记为答案,但是@hrbrmstr提供了一个很好的解决方案,将两个ggplots结合起来,对于这个简单的图表效果很好。

以下是代码:

breaks.major <- c(0,15,37.5,52.5,67.5,82.5,95,100) #defines the midpoints of the categories (label locations)
breaks.minor <- c(30,45,60,75,90) #defines the edges of the categories (second label set I need)
labels.minor <- c("","Extremely \nDissatisfied","Dissatisfied","Uncertain","Satisfied","Very \nSatisfied","Extremely \nSatisfied","")
lims =c(0,100)
g <- ggplot(mpg, aes(class))+
  geom_bar()+
  coord_flip()+
  scale_y_continuous(limit = lims, minor_breaks = breaks.minor, breaks = breaks.major, labels = labels.minor) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.major.y = element_blank()) +
  theme(axis.ticks.x=element_blank()) +
  theme(axis.title= element_blank()) 

它生成了这张图:

enter image description here

我需要有两组X轴标签,一组显示类别名称(即通过labels.minor已经存在的“satisfied”等),另一组显示在breaks.minor位置处的值(对应于类别限制,即垂直面板网格线)。我需要当前的labels.minor标签位于所需的附加标签下方。

我目前使用换行符来实现这一点,以便数字和类别都在一个长字符串中,但随着绘图大小的调整,间距会变得奇怪。我可以使用文本框(我假设),但ggplot内部是否有方法可以实现?

如果您能使我的当前标签位于其区段的中心(例如,“极度满意”偏离中心),那就更好了。

这是我想要的输出(请原谅我的“mspaint”)

enter image description here


你似乎希望 labels.minor 标签位于 breaks.minor 位置的值下方,但前者有6个元素,而后者只有5个。我有什么遗漏吗? - Weihuang Wong
@Weihuang Wong 6和7实际上(包括0和100),这些是类别边界和类别中点。请参见所需输出的更新示例。 - Alex
3个回答

7
我认为这个可以满足你的需求:

我认为这个可以满足你的需求:

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

breaks.major <- c(0, 15, 37.5, 52.5, 67.5, 82.5, 95, 100)
breaks.minor <- c(30, 45, 60, 75, 90)
labels.minor <- c("", "Extremely\nDissatisfied", "Dissatisfied", "Uncertain",
                  "Satisfied", "Very\nSatisfied", "Extremely\nSatisfied", "")
lims <- c(0, 100)

# build the main plot with the text axis
gg1 <- ggplot(mpg, aes(class))
gg1 <- gg1 + geom_bar()
gg1 <- gg1 + scale_y_continuous(expand=c(0,0), limit=lims, 
                                minor_breaks=breaks.minor, 
                                breaks=breaks.major,
                                labels=labels.minor)
gg1 <- gg1 + coord_flip()
gg1 <- gg1 + theme(panel.grid.major.x=element_blank())
gg1 <- gg1 + theme(panel.grid.major.y=element_blank())
gg1 <- gg1 + theme(axis.ticks.x=element_blank())
gg1 <- gg1 + theme(axis.title=element_blank())

# let ggplot2 do the work of building the second axis
gg2 <- ggplot(mpg, aes(class))
gg2 <- gg2 + scale_y_continuous(expand=c(0,0), limit=lims, 
                                breaks=c(0, breaks.minor, 100))
gg2 <- gg2 + coord_flip()
gg2 <- gg2 + theme(axis.ticks.x=element_blank())
gg2 <- gg2 + theme(axis.text.x=element_text(hjust=c(0, 0.5, 0.5, 0.5, 0.5, 0.5, 1)))

gt1 <- ggplot_gtable(ggplot_build(gg1))
gt2 <- ggplot_gtable(ggplot_build(gg2))
axis2 <- grid.arrange(gt2$grobs[[5]])

gt <- gtable_add_rows(gt1, unit(0.1, "null"), 4)
grid.arrange(gtable_add_grob(gt, axis2, t=5, l=4, b=5, r=4))

enter image description here


这是一个非常好的解决方案,可以很好地与这个示例代码配合使用。不幸的是,我的实际绘图代码要长得多,并且具有条件误差条和其他复杂的格式设置。有效地使绘图例程的代码加倍会导致问题扩大。我可以将两个答案标记为正确吗?因为从技术上讲,这是正确的。 - Alex
我相当确定这可以更加简约化。我晚些时候会尝试一下。 - hrbrmstr
嘿 @hrbrmstr,你想到如何压缩这个了吗? - Alex

6

也许类似于这样。请注意,为了处理适当的间距和类别名称的位置,两个轴的expand都被设置。

您图中的标签并不是真正的偏离中心,它们在其类别边界的中心。只是默认情况下,轴会扩展得更远。

如果您想变得更加花哨,您也可以在绘图区域之外绘制,但需要进行一些微调。 这个问题可以帮助您入门。

ggplot(mpg, aes(class))+
  geom_bar()+
  geom_text(data = data.frame(br = breaks.minor), aes(y = br, label = br, x = 7.75),
            size = 4, col = 'grey30') +
  coord_flip()+
  scale_y_continuous(limit = lims, minor_breaks = breaks.minor, 
                     breaks = breaks.major, labels = labels.minor, 
                     expand = c(0, 0)) +
  scale_x_discrete(expand = c(0.05, 0)) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.major.y = element_blank()) +
  theme(axis.ticks.x=element_blank()) +
  theme(axis.title= element_blank())

enter image description here


太棒了@Axeman,感谢你的努力和链接,我会继续下去。不过,很遗憾,我需要在图表和“满意”等之间的数字(请参见op中的更新图)。我可能没有表达清楚,但是你的建议也可能是一种替代方法,考虑到ggplot本身的限制。 - Alex
只需将geom_text中的“x”替换为较小的值,即可将数字向下移动以使它们更接近。 - Axeman
你也可以将它们全部添加为“breaks”,并使用换行符(\n)向上或向下移动一些标签。 - Axeman
当然要更改x,这是一个愚蠢的疏忽。至于使用“breaks”,这是最初编写此代码时的方式,将所有内容作为一个长字符字符串,并使用空格和换行符进行对齐。不幸的是,由于情节偶尔会发生变化,因此需要进行调整。 - Alex
嘿,伙计,当我在ggplot调用中使用fill拆分我的条形图时,我遇到了奇怪的问题。除非breaks.minor的长度与y轴标签(在coord_flip之后)的长度相同,否则我会收到错误消息“Error: Aesthetics must be either length 1 or the same as the data (12): x, y, label, fill”。太奇怪了,我如何使geom_text独立于绘图中的数据?感觉这应该很容易!!! - Alex

4

我将所有标签都写成了主要标签。

# OP's
breaks.major <- c(0,15,37.5,52.5,67.5,82.5,95,100) #defines the midpoints of the categories (label locations)
breaks.minor <- c(30,45,60,75,90) #defines the edges of the categories (second label set I need)
labels.minor <- c("","Extremely \nDissatisfied","Dissatisfied","Uncertain","Satisfied","Very \nSatisfied","Extremely \nSatisfied","")
lims =c(0,100)

breaks.major2 <- c(0,15,37.5,52.5,67.5,82.5,95)
breaks.minor2 <- c(30,45,60,75,90,100)       # put 100 into minor from major

breaks.comb <- sort(c(breaks.major2, breaks.minor2 - 1.0E-6))  # avoid the just same value as minor
label.comb <- c(0, "\nExtremely \nDissatisfied", 30, "\nDissatisfied", 45, "\nUncertain", 60, 
                "\nSatisfied", 75, "\nVery \nSatisfied", 90, "\nExtremely \nSatisfied", 100)

library(ggplot2)
g <- ggplot(mpg, aes(class))+
  geom_bar()+
  coord_flip()+
  scale_y_continuous(limit = lims, minor_breaks = breaks.minor2, breaks = breaks.comb, 
                     labels = label.comb, expand = c(0,0)) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.major.y = element_blank()) +
  theme(axis.ticks.x=element_blank()) +
  theme(axis.title= element_blank()) +
  theme(plot.margin = unit(c(0.5, 0.5, 0.5, 0.5), "lines"))

enter image description here


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