R ggplot2-底部的图例被剪切,如何快速找到图例的最佳列数? [英] R ggplot2 - legend at the bottom gets cut, how to find optimal number of columns for the legend on the fly?

查看:102
本文介绍了R ggplot2-底部的图例被剪切,如何快速找到图例的最佳列数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想绘制一个图例在底部的图,但图例总是被剪切...由于似乎ggplot2无法自动确定图例底部的最佳列数,因此我尝试做它自己...没有成功.

I want to make a plot with a legend at the bottom, but the legend gets invariably cut... Since it seems ggplot2 cannot automatically determine the best number of columns in a legend at the bottom, I try to do it myself... with no success.

说我有以下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(英寸).

I want my output plot to have a width of 12 (inches).

当我使用ggplot2绘制图时,图例的宽度大于图的宽度并被剪切:

When I make the plot with ggplot2, the legend takes more width than the plot and gets cut:

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).

Since my legend gets cut for some reason, I tried to determine the optimal number of legend columns myself. I have to calculate it on the fly cause all of this goes into a function (but for this case, the answer should be 4).

由于地块的宽度以英寸为单位,因此我尝试变得精明,并为图例中的某个级别计算以英寸为单位的平均尺寸.然后,列数将是图的宽度除以单个级别的大小,并四舍五入(减去图例标题将占据的列).

Since the width of the plot is in inches, I tried to be smart about it and calculate the average size in inches for a level in the legend. Then the number of columns would be the plot width divided by the size of a single level, rounded down (minus the columns that the legend title would occupy).

#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.

With this information, I would use ggplot2 again to make the plot, passing the number of columns to 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中的代码会产生以下图...

I thought I almost had it, but the results are never successful... see the code in this MWE produces the following plot...

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

Why does the legend get cut like that? Why ggplot2 doesn't choose the optimal number of columns automatically?

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

If I try to determine the optimal number of columns myself, how to do it? What am I doing wrong up there?

我知道在这种情况下传递给guide_legend的最佳列数(那里的块的num_col结果)应该是4,但我只知道事后 ...我需要即时计算出最佳数量,因为所有这些都在一个函数中...

I know that the optimal number of columns in this case to pass to guide_legend (the num_col result of the chunk up there) should be 4, but I only know it post hoc... I would need to calculate this optimal number on the fly cause all this goes inside a function...

谢谢!

推荐答案

此方法有效:

  • 减小字体大小(用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"))

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

You can adjust width_scale according to the number of elements in the legend :

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))

这篇关于R ggplot2-底部的图例被剪切,如何快速找到图例的最佳列数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆