R ggplot2 - 底部的图例被剪切,如何即时找到图例的最佳列数?

Dan*_*Cee 6 size r legend ggplot2

我想用底部的图例制作一个情节,但图例总是被剪掉......因为它似乎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
Run Code Online (Sandbox Code Playgroud)

我希望我的输出图的宽度为 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()
Run Code Online (Sandbox Code Playgroud)

它产生这个情节:

测试1

由于我的图例由于某种原因被删除,我试图自己确定图例列的最佳数量。我必须即时计算它,因为所有这些都进入一个函数(但对于这种情况,答案应该是 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))}
#
Run Code Online (Sandbox Code Playgroud)

有了这些信息,我将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()
Run Code Online (Sandbox Code Playgroud)

我以为我几乎拥有它,但结果从来没有成功......看到这个MWE中的代码产生了以下情节......

测试1

为什么传奇会被剪成这样?为什么ggplot2不自动选择最佳列数?

如果我尝试自己确定最佳列数,该怎么做?我在那里做错了什么?

我知道在这种情况下传递给的最佳列数guide_legendnum_col那里的块的结果)应该是 4,但我只知道它事后......我需要即时计算这个最佳数字,因为所有这进入一个函数......

谢谢!

Wal*_*ldi 6

这有效:

  • 减小字体大小(width_scale 而不是 width_scale * 1.5)
  • 在图例周围添加一个框
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=width_scale),
                   legend.box.margin = margin(6, 6, 6, 6),
                   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"))
Run Code Online (Sandbox Code Playgroud)

查看结果

您可以根据图例中的元素数量调整 width_scale :

mydf <- data.frame(group=paste0('gr',1:10), var=paste('some long text -', 1:50), value=runif(500, 0, 100))
width_scale <- 12 * 26 / length(unique(mydf$var))
Run Code Online (Sandbox Code Playgroud)

width_scale 调整