两个水平条形图在ggplot2中共享轴(类似于人口金字塔)

22
我想在一个图表上绘制两个变量,类似于一个人口金字塔,就像这样:

plot

这个图表差一点就到位了,但还不够完美,原因如下所列。
我用以下代码生成了这个图表:
DATA <- data.frame(
    state = c("AK", "TX", "CA", "MT", "NM", "AZ", "NV", "CO", "OR", "WY", "MI", "MN", "UT", "ID", "KS", "NE", "SD", "WA", "ND", "OK"),
    sales_staff = c(20,30,40,10,15,35,18,25,22,7,12,22,3,4,5,8,14,28,24,32)
)

set.seed(1)
DATA$sales <- DATA$sales_staff * 50 + (runif(nrow(DATA)) * 1000)

# Order the state factor by number of sales staff so that it is plotted in that order
DATA$state <- factor(DATA$state, levels = DATA[order(DATA$sales_staff),"state"])

我想要将两个图形背靠背地“粘”在一起,所以我直接使用了http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_%28ggplot2%29/中的multiplot()函数。
(为了简洁和清晰起见,我不会在这里重复该函数的代码)
我最终图形的代码是:
library(ggplot2)

g1 <- ggplot(data = DATA, aes(x = state, y = sales_staff)) +
  geom_bar(stat = "identity") + ggtitle("Number of sales staff") +
  theme(axis.title.x = element_blank(), axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y = element_blank(), plot.margin = unit(c(1,0,1,0), "mm")) +
  scale_y_reverse() + coord_flip()

g2 <- ggplot(data = DATA, aes(x = state, y = sales)) +
  geom_bar(stat = "identity") + ggtitle("Sales (x $1000)") +
  theme(axis.title.x = element_blank(), axis.title.y = element_blank(), plot.margin = unit(c(1,5,1,0), "mm")) +
  coord_flip()

multiplot(g1, g2, cols = 2)

好的。那么这个图的问题是什么?
- 我需要在左边的图上获得右轴的刻度标记。我搞不清楚怎么做。 - 这两个图的宽度不同。这是因为中间的州是右边图的轴标签,并且使用了该图的一部分空间。
我在将这个图提升到“生产质量”方面遇到了困难。我开始怀疑自己的方法是否正确,因为我认为下一步将是在两个图之间绘制轴标签作为独立的第三列(我还不知道如何做到这一点)。这将解决“相等大小”的问题,并允许我添加一个“州”标题,所以这可能仍然是一个可行的方法。但我不禁想知道是否有更简单的方法...
任何建议或帮助都将不胜感激!

1
你能否根据自己的需求调整https://dev59.com/MmUq5IYBdhLWcg3wN94t?lq=1? - mnel
2
我是一个忠实的ggplot2粉丝,尽可能地使用这个包来完成所有任务。但是几个月前,当我需要创建类似上面那个金字塔图时,最终放弃了并使用了plotrix包中的pyramid.plot函数。虽然我不喜欢它的语法,但它并不难,而且比ggplot2得到更好的结果,也少了很多麻烦。 - SlowLearner
你应该搜索“背靠背图表”。 - Roman Luštrik
这里有一个最近添加的、非常棒的示例,演示了如何微调ggplot2反向对齐的柱状图:https://dev59.com/22455IYBdhLWcg3wD_2Z#37374271 - Uwe
1个回答

31

这里是一个非常冗长的解决方案来制作你的绘图。思路是创建一个仅包含州名和两侧刻度线的新图,然后将其用作中间图。

为了这个图,我添加了没有名称的标题以获取空间,并使用 ylab(NULL) 来删除空间。左右边距值为-1,以使图形更靠近其他图形。在绘制之前应添加库 grid 以使用函数 unit() 进行绘图边距。

library(grid)
g.mid<-ggplot(DATA,aes(x=1,y=state))+geom_text(aes(label=state))+
  geom_segment(aes(x=0.94,xend=0.96,yend=state))+
  geom_segment(aes(x=1.04,xend=1.065,yend=state))+
  ggtitle("")+
  ylab(NULL)+
  scale_x_continuous(expand=c(0,0),limits=c(0.94,1.065))+
  theme(axis.title=element_blank(),
        panel.grid=element_blank(),
        axis.text.y=element_blank(),
        axis.ticks.y=element_blank(),
        panel.background=element_blank(),
        axis.text.x=element_text(color=NA),
        axis.ticks.x=element_line(color=NA),
        plot.margin = unit(c(1,-1,1,-1), "mm"))

两个原始图形都被修改了。首先,第二个图形的y轴被删除,并且左/右边距调整为-1。

g1 <- ggplot(data = DATA, aes(x = state, y = sales_staff)) +
  geom_bar(stat = "identity") + ggtitle("Number of sales staff") +
  theme(axis.title.x = element_blank(), 
        axis.title.y = element_blank(), 
        axis.text.y = element_blank(), 
        axis.ticks.y = element_blank(), 
        plot.margin = unit(c(1,-1,1,0), "mm")) +
  scale_y_reverse() + coord_flip()

g2 <- ggplot(data = DATA, aes(x = state, y = sales)) +xlab(NULL)+
  geom_bar(stat = "identity") + ggtitle("Sales (x $1000)") +
  theme(axis.title.x = element_blank(), axis.title.y = element_blank(), 
        axis.text.y = element_blank(), axis.ticks.y = element_blank(),
        plot.margin = unit(c(1,0,1,-1), "mm")) +
  coord_flip()
现在使用库gridExtra和函数grid.arrange()来组合绘图。在绘制之前,所有的图都被制作成了grobs。
library(gridExtra)
gg1 <- ggplot_gtable(ggplot_build(g1))
gg2 <- ggplot_gtable(ggplot_build(g2))
gg.mid <- ggplot_gtable(ggplot_build(g.mid))

grid.arrange(gg1,gg.mid,gg2,ncol=3,widths=c(4/9,1/9,4/9))

在此输入图片描述


7
感谢您的充足努力!显然也感谢您回答了楼主的问题。 - Simon O'Hanlon
类似的东西已经被Prasad建议过了 https://dev59.com/22455IYBdhLWcg3wD_2Z - Roman Luštrik
4
在斯洛文尼亚,我们有一句话,大意是“如果你不动脑筋,你就要动你的脚”。 :) - Roman Luštrik
不错。但是在创建 g.mid 之前,您应该先加载 gridExtra 库,因为 unit 函数来自 grid 包。 - Jonas Tundo
@idzis Elferts,如何在两侧添加文本标签?或者如何增加“轴”的大小? - nik

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