我想在底部制作一个带有图例的情节,但图例总是被剪切......因为看起来ggplot2
无法自动确定底部图例中的最佳列数,我尝试自己做......但没有成功。
假设我有以下内容mydf
数据框:
mydf <- data.frame(group=paste0('gr',1:10), var=paste('some long text -', LETTERS), value=runif(260, 0, 100))
head(mydf)
# group var value
#1 gr1 some long text - A 7.941256
#2 gr2 some long text - B 50.740651
#3 gr3 some long text - C 89.068872
#4 gr4 some long text - D 77.572413
#5 gr5 some long text - E 9.792349
#6 gr6 some long text - F 35.194944
我希望输出图的宽度为 12(英寸)。
当我制作情节时ggplot2
,图例的宽度大于图并被剪切:
width_scale <- 12
grDevices::pdf(file='test.pdf', height=10, width=width_scale)
print(#or ggsave()
ggplot2::ggplot(mydf, ggplot2::aes(group, value, fill=var)) +
ggplot2::geom_bar(stat="identity") +
ggplot2::scale_y_continuous("%") +
ggplot2::theme_light() +
ggplot2::theme(legend.text=ggplot2::element_text(size=1.5*width_scale),
legend.title=ggplot2::element_text(size=1.5*width_scale,face="bold"),
legend.position="bottom",
legend.key.size = grid::unit(width_scale/50, "inch"),
legend.key.width = grid::unit(width_scale/50, "inch"))
)
grDevices::dev.off()
它产生这个情节:
由于我的图例由于某种原因被删除,我尝试自己确定图例列的最佳数量。我必须即时计算它,因为所有这些都进入一个函数(但对于本例,答案应该是 4)。
由于绘图的宽度以英寸为单位,因此我尝试巧妙地计算图例中某个级别的平均尺寸(以英寸为单位)。那么列数将是绘图宽度除以单个级别的大小,向下舍入(减去图例标题将占用的列)。
#find optimal number of legend columns
ceiling_dec <- function(x, level=1) round(x + 5*10^(-level-1), level)
floor_dec <- function(x, level=1) round(x - 5*10^(-level-1), level)
letter_size <- 1.5*width_scale/72.27 #72.27 point in 1 inch
mean_level_size <- mean(nchar(levels(mydf$var))) * letter_size #this is the size in inches of a group level in the legend
mean_level_size <- mean_level_size + (width_scale/50) + (width_scale/50) #plus the size of the level key and some extra space
num_cols <- floor_dec(width_scale/mean_level_size, 0)
cols_to_remove <- ceiling_dec((3*letter_size) / mean_level_size, 0) #number of columns that the legend title (var) would occupy
num_cols <- num_cols - cols_to_remove
if (num_cols<=0){num_cols <- length(levels(mydf$var))}
if (num_cols>length(levels(mydf$var))){num_cols <- length(levels(mydf$var))}
num_rows <- ceiling(length(levels(mydf$var)) / num_cols)
if ((num_rows==1) & (num_cols<length(levels(mydf$var)))){num_cols <- length(levels(mydf$var))}
#
有了这些信息,我将使用ggplot2
再次制作绘图,将列数传递给guide_legend
.
grDevices::pdf(file='test.pdf', height=10, width=width_scale)
print(#or ggsave()
ggplot2::ggplot(mydf, ggplot2::aes(group, value, fill=var)) +
ggplot2::geom_bar(stat="identity") +
ggplot2::scale_y_continuous("%") +
ggplot2::theme_light() +
ggplot2::theme(legend.text=ggplot2::element_text(size=1.5*width_scale),
legend.title=ggplot2::element_text(size=1.5*width_scale,face="bold"),
legend.position="bottom",
legend.key.size = grid::unit(width_scale/50, "inch"),
legend.key.width = grid::unit(width_scale/50, "inch")) +
ggplot2::guides(fill=ggplot2::guide_legend(ncol=num_cols))
)
grDevices::dev.off()
我以为我几乎拥有了它,但结果从来没有成功...请参阅此 MWE 中的代码产生以下情节...
传说为何被剪成这样?为什么ggplot2
不会自动选择最佳列数?
如果我尝试自己确定最佳列数,该怎么做?我在上面做错了什么?
我知道在这种情况下要传递的最佳列数guide_legend
(the num_col
那里的块的结果)应该是 4,但我只知道它post hoc...我需要动态计算这个最佳数字,因为所有这些都在函数内部...
Thanks!