library(ggplot2) library(scales) library(dplyr) gg.histo.pct.by.group <- function(g, ...) { g + geom_histogram(aes(y=unlist(lapply(unique(..group..), function(grp) ..count..[..group..==grp] / sum(..count..[..group..==grp])))), ...) + scale_y_continuous(labels = percent) + ylab("% of total count by group") } data = diamonds %>% select(carat, color) %>% filter(color %in% c('H', 'D')) g = ggplot(data, aes(carat, fill=color)) %>% gg.histo.pct.by.group(binwidth=0.5, position="dodge")
通常为这些类型的图形添加带有标签的一些百分位线,例如,
这样做的一个切入方式是
facts = data %>% group_by(color) %>% summarize( p50=quantile(carat, 0.5, na.rm=T), p90=quantile(carat, 0.9, na.rm=T) ) ymax = ggplot_build(g)$panel$ranges[[1]]$y.range[2] g + geom_vline(data=facts, aes(xintercept=p50, color=color), linetype="dashed", size=1) + geom_vline(data=facts, aes(xintercept=p90, color=color), linetype="dashed", size=1) + geom_text(data=facts, aes(x=p50, label=paste("p50=", p50), y=ymax, color=color), vjust=1.5, hjust=1, size=4, angle=90) + geom_text(data=facts, aes(x=p90, label=paste("p90=", p90), y=ymax, color=color), vjust=1.5, hjust=1, size=4, angle=90)
我喜欢把它封装成像g%>%gg.percentile.x(c(.5,.9)这样的东西但是我无法找到一种结合使用aes_或aes_string的好方法在图形对象中发现分组列,以便正确计算百分位数.我很感激一些帮助.
我认为创建所需图表的最有效方法包括三个步骤:>写两个单独的简单统计数据(从https://cran.r-project.org/web/packages/ggplot2/vignettes/extending-ggplot2.html创建一个新的统计数据部分):一个用于在百分位数位置添加垂直线,另一个用于添加文本标签;
>根据需要将刚写入的统计数据与所需的参数组合在一起;
>使用工作结果.
所以答案也包括3个部分.
第1部分.在百分位数处添加垂直线的统计数据应根据x轴中的数据计算这些值,并以适当的格式返回结果.这是代码:
library(ggplot2) StatPercentileX <- ggproto("StatPercentileX", Stat, compute_group = function(data, scales, probs) { percentiles <- quantile(data$x, probs=probs) data.frame(xintercept=percentiles) }, required_aes = c("x") ) stat_percentile_x <- function(mapping = NULL, data = NULL, geom = "vline", position = "identity", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ...) { layer( stat = StatPercentileX, data = data, mapping = mapping, geom = geom, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list(na.rm = na.rm, ...) ) }
添加文本标签的统计信息也是如此(默认位置位于图表的顶部):
StatPercentileXLabels <- ggproto("StatPercentileXLabels", Stat, compute_group = function(data, scales, probs) { percentiles <- quantile(data$x, probs=probs) data.frame(x=percentiles, y=Inf, label=paste0("p", probs*100, ": ", round(percentiles, digits=3))) }, required_aes = c("x") ) stat_percentile_xlab <- function(mapping = NULL, data = NULL, geom = "text", position = "identity", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ...) { layer( stat = StatPercentileXLabels, data = data, mapping = mapping, geom = geom, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list(na.rm = na.rm, ...) ) }
我们已经拥有了非常强大的乐器,可以用ggplot2提供的任何方式(着色,分组,刻面等).例如:
set.seed(1401) plot_points <- data.frame(x_val=runif(100), y_val=runif(100), g=sample(1:2, 100, replace=TRUE)) ggplot(plot_points, aes(x=x_val, y=y_val)) + geom_point() + stat_percentile_x(probs=c(0.25, 0.5, 0.75), linetype=2) + stat_percentile_xlab(probs=c(0.25, 0.5, 0.75), hjust=1, vjust=1.5, angle=90) + facet_wrap(~g) # ggsave("Example_stat_percentile.png", width=10, height=5, units="in")
第2部分尽管为行和文本标签保留单独的图层看起来很自然(尽管计算百分位数计算效率低两倍)但每次添加两层都非常冗长.特别是对于这个ggplot2有简单的组合层的方法:将它们放在列表中,这是结果函数调用.代码如下:
stat_percentile_x_wlabels <- function(probs=c(0.25, 0.5, 0.75)) { list( stat_percentile_x(probs=probs, linetype=2), stat_percentile_xlab(probs=probs, hjust=1, vjust=1.5, angle=90) ) }
使用此功能,可以通过以下命令重现上一个示例:
ggplot(plot_points, aes(x=x_val, y=y_val)) + geom_point() + stat_percentile_x_wlabels() + facet_wrap(~g)
注意,stat_percentile_x_wlabels获取所需百分位数的概率,然后将其传递给分位数函数.这是指定它们的地方.
第3部分再次使用组合图层的想法,您的问题中的图可以重现如下:
library(scales) library(dplyr) geom_histo_pct_by_group <- function() { list(geom_histogram(aes(y=unlist(lapply(unique(..group..), function(grp) { ..count..[..group..==grp] / sum(..count..[..group..==grp]) }))), binwidth=0.5, position="dodge"), scale_y_continuous(labels = percent), ylab("% of total count by group") ) } data = diamonds %>% select(carat, color) %>% filter(color %in% c('H', 'D')) ggplot(data, aes(carat, fill=color, colour=color)) + geom_histo_pct_by_group() + stat_percentile_x_wlabels(probs=c(0.5, 0.9)) # ggsave("Question_plot.png", width=10, height=6, unit="in")
备注
>这里解决这个问题的方法允许构建具有百分位线和标签的更复杂的图;>将x改为y(反之亦然),vline为hline,xintercept为yintercept,在适当的位置,可以为y轴的数据定义相同的统计数据;>当然,如果您喜欢使用%>%代替ggplot2,您可以在函数中包装已定义的统计信息,就像您在问题帖子中所做的那样.就个人而言,我不建议这样做,因为它违反了ggplot2的标准使用.