【问题标题】:Simpler population pyramid in ggplot2ggplot2中更简单的人口金字塔
【发布时间】:2013-02-04 03:47:39
【问题描述】:

我想用 ggplot2 创建一个人口金字塔。这个问题被问到before,但我相信解决方案一定要简单得多。

test <- (data.frame(v=rnorm(1000), g=c('M','F')))
require(ggplot2)
ggplot(data=test, aes(x=v)) + 
    geom_histogram() + 
    coord_flip() + 
    facet_grid(. ~ g)

生成此图像。在我看来,这里创建人口金字塔唯一缺少的步骤是反转第一个方面的 x 轴,即从 50 变为 0,同时保持第二个不变。有人可以帮忙吗?

【问题讨论】:

  • 我认为stackoverflow.com/questions/4559229/… 更适合之前关于同一主题的问题。有时必须从ggplot2 移出。
  • @dmvianna 我是一个狂热的ggplot2 用户,但是当我最近不得不创建一个人口金字塔时,我最终放弃并使用了plotrix 包中的pyramid.plot。这并不困难,而且我的眼睛完全可以接受结果。坦率地说,这比使用ggplot 或我自己使用ggplot 的链接问题中的结果要好得多。

标签: r ggplot2


【解决方案1】:

这是一个没有刻面的解决方案。首先,创建数据框。我使用了 1 到 20 之间的值来确保没有一个值是负数(使用人口金字塔,您不会得到负数/年龄)。

test <- data.frame(v=sample(1:20,1000,replace=T), g=c('M','F'))

然后为每个 g 值分别组合两个 geom_bar() 调用。 F 的计数按原样计算,但 M 的计数乘以 -1 以得到相反方向的柱。然后scale_y_continuous() 用于获取轴的漂亮值。

require(ggplot2)
require(plyr)    
ggplot(data=test,aes(x=as.factor(v),fill=g)) + 
  geom_bar(subset=.(g=="F")) + 
  geom_bar(subset=.(g=="M"),aes(y=..count..*(-1))) + 
  scale_y_continuous(breaks=seq(-40,40,10),labels=abs(seq(-40,40,10))) + 
  coord_flip()

更新

由于参数subset=. 在最新的ggplot2 版本中已弃用,因此使用函数subset() 可以获得相同的结果。

ggplot(data=test,aes(x=as.factor(v),fill=g)) + 
  geom_bar(data=subset(test,g=="F")) + 
  geom_bar(data=subset(test,g=="M"),aes(y=..count..*(-1))) + 
  scale_y_continuous(breaks=seq(-40,40,10),labels=abs(seq(-40,40,10))) + 
  coord_flip()

【讨论】:

  • 我收到一个错误:'do.call("layer", list(mapping = mapping, data = data, stat = stat, : could not find function "."' 但 '+ geom_bar(data=subset(test, g=="F"))' 为我工作
  • 您可能需要使用library(plyr) 显式加载plyr
  • 很酷的情节。我收到一条警告:警告消息:在 loop_apply(n, do.ply) 中:当 ymin != 0 时未明确定义堆叠你知道这是什么意思吗?
  • @ExpectoPatronum 这是警告发生,因为我们在 barplot 中使用负值进行堆叠。
  • 在ggplot 2.1.0下发现“错误:未知参数:子集”的错误。最好更新新版ggplot的答案。谢谢。
【解决方案2】:

人口金字塔的通用ggplot代码模板(下)

  1. 使用geom_col() 而不是geom_bar(),后者具有更好的默认stat 并避免使用coord_flip()
  2. 避免在缩放函数中使用labels = abs 手动设置标签分隔符。
  3. 具有相同的男性和女性水平轴(和标签),以便更轻松地比较性别 - 在柠檬包中使用 scale_x_symmetric()
  4. 仅使用一个几何图形,避免了对数据进行子集化的需要;如果您想在分面图中创建多个金字塔,这将非常有用。

正在创建数据...

set.seed(100)
a <- seq(from = 0, to = 90, by = 10)
d <- data.frame(age = paste(a, a + 10, sep = "-"),
                sex = rep(x = c("Female", "Male"), each = 10),
                pop = sample(x = 1:100, size = 20))
head(d)
#     age    sex pop
# 1  0-10 Female  74
# 2 10-20 Female  89
# 3 20-30 Female  78
# 4 30-40 Female  23
# 5 40-50 Female  86
# 6 50-60 Female  70

绘图代码...

library(ggplot2)
library(lemon)

ggplot(data = d, 
       mapping = aes(x = ifelse(test = sex == "Male", yes = -pop, no = pop), 
                     y = age, fill = sex)) +
  geom_col() +
  scale_x_symmetric(labels = abs) +
  labs(x = "Population")

【讨论】:

  • ggplot 2.1.0 的新版本下工作。
  • 这是一个简单的方法,效果很好。应该是最佳答案。
  • 干净、简单且可扩展——这太棒了!
【解决方案3】:

扩展@gjabel 的帖子,这里是一个更干净的人口金字塔,再次使用 ggplot2。

popPy1 <- ggplot(data = venDemo, 
   mapping = aes(
      x = AgeName, 
      y = ifelse(test = sex == "M",  yes = -Percent, no = Percent), 
      fill = Sex2,
      label=paste(round(Percent*100, 0), "%", sep="")
   )) +
geom_bar(stat = "identity") +
#geom_text( aes(label = TotalCount, TotalCount = TotalCount + 0.05)) +
geom_text(hjust=ifelse(test = venDemo$sex == "M",  yes = 1.1, no = -0.1), size=6, colour="#505050") +
#  scale_y_continuous(limits=c(0,max(appArr$Count)*1.7)) +
# The 1.1 at the end is a buffer so there is space for the labels on each side
scale_y_continuous(labels = abs, limits = max(venDemo$Percent) * c(-1,1) * 1.1) +
# Custom colours
scale_fill_manual(values=as.vector(c("#d23f67","#505050"))) +
# Remove the axis labels and the fill label from the legend - these are unnecessary for a Population Pyramid
labs(
  x = "",
  y = "",
  fill="", 
  family=fontsForCharts
) +
theme_minimal(base_family=fontsForCharts, base_size=20) +   
coord_flip() +
# Remove the grid and the scale
theme( 
  panel.grid.major = element_blank(), 
  panel.grid.minor = element_blank(),
  axis.text.x=element_blank(), 
  axis.text.y=element_text(family=fontsForCharts, size=20),
  strip.text.x=element_text(family=fontsForCharts, size=24),
  legend.position="bottom",
  legend.text=element_text(size=20)
)

popPy1

【讨论】:

    猜你喜欢
    • 2021-04-20
    • 1970-01-01
    • 1970-01-01
    • 2015-11-01
    • 2020-12-16
    • 2013-03-26
    • 1970-01-01
    • 2021-11-16
    相关资源
    最近更新 更多