用于手动分配颜色的一致六边形尺寸和图例

时间:2017-10-08 21:57:58

标签: r ggplot2 legend

这是我最近提出的问题(Manually assigning colors with scale_fill_manual only works for certain hexagon sizes)的延续。

我无法绘制geom_hex(),因此所有六边形都是相同的大小。有人解决了这个问题。但是,他们的解决方案删除了​​图例键。现在,我无法保持所有六边形大小相同,同时还保留了图例。

具体来说,我真的想让传奇标签保持敏感。在下面的示例中,图例具有值(0,2,4,6,8,20),而不是十六进制标签(#08306B,#08519C等)。

以下是说明问题的MWE。最后,根据3条评论,你可以看到我能够1)创建一个六角尺寸一致但没有图例的图,2)用图例创建一个图,但六边形尺寸不一致,3)尝试创建一个具有一致六边形大小和图例的图但失败:

char *my_strtrim(char const *string)
{
    char *i;
    char *s;
    int ready;

    s = (char *)string;
    i = s;
    ready = 0;
    while(*i)
    {
        ++i;
        if(isspace(*i))
        {
            if(!ready)
            {
                continue ;
            }
            ready = 0;
        }
        ready = 1;
        *(s++) = *i;
    }

    *s = 0;
    return ((char *)string);
}

关于如何在保留图例的同时保持六边形尺寸一致的任何建议都会非常有用!

enter image description here

1 个答案:

答案 0 :(得分:1)

哇,这是一个有趣的问题 - geom_hex似乎真的不喜欢将颜色/填充映射到分类变量上。我认为这是因为它被设计成一个二维直方图并可视化连续的摘要统计数据,但如果有人对幕后发生的事情有任何了解,我很想知道。

对于您的具体问题,这确实会引发工作,因为您尝试进行分类着色,将非线性组分配给各个六边形。从概念上讲,您可能会考虑为什么要这样做。可能有一个很好的理由,但您基本上采用线性颜色渐变并将其非线性地映射到您的数据上,这最终会在视觉上产生误导。

然而,如果这是你想要做的,我可以想出的最好的方法是创建一个新的连续变量,线性地映射到你选择的颜色,然后使用它们来创建颜色渐变。让我试着引导你完成我的思考过程。

你基本上有一个连续变量(counts),你想要映射到颜色。使用简单的颜色渐变很容易,这是连续变量的ggplot2中的默认值。使用您的数据:

ggplot(hexdf, aes(x=x, y=y)) +
  geom_hex(stat="identity", aes(fill=counts))

产生了一些接近的东西。

First try

然而,具有非常高计数的区域会清除计数更低的点的渐变,因此我们需要更改渐变将颜色映射到值的方式。您已经在clrs变量中声明了要使用的颜色;我们只需要在数据框中添加一列以与这些颜色结合使用,以创建平滑渐变。我这样做了如下:

all_breaks <- c(0, my_breaks)
breaks_n <- 1:length(all_breaks)
get_break_n <- function(n) {
  break_idx <- max(which((all_breaks - n) < 0))
  breaks_n[break_idx]
}
hexdf$bin <- sapply(hexdf$counts, get_break_n)

我们创建bin变量作为最接近count变量的break的索引而不超过它。现在,您会注意到:

ggplot(hexdf, aes(x=x, y=y)) +
  geom_hex(stat="identity", aes(fill=bin))

越来越接近目标了。

Added bin variable

下一步是更改颜色渐变映射到bin变量的方式,我们可以通过添加对scale_fill_gradientn的调用来执行此操作:

ggplot(hexdf, aes(x=x, y=y)) +
  geom_hex(stat="identity", aes(fill=bin)) +
  scale_fill_gradientn(colors=rev(clrs[-1])) # odd color reversal to
                                             # match OP's color mapping

这将采用您想要插入渐变的颜色矢量。我们设置它的方式,插值中的点将与bin变量的唯一值完美匹配,这意味着每个值将获得指定的颜色之一。

Custom color gradient for bin variable

现在我们正在用煤气做饭,剩下要做的就是从原始图表中添加各种铃声和口哨声。最重要的是,我们需要让传奇看起来像我们想要的那样。这需要三件事:(1)将其从默认颜色条更改为离散化图例,(2)指定我们自己的自定义标签,以及(3)为其提供信息性标题。

# create the custom labels for the legend
all_break_labs <- as.character(all_breaks[1:(length(allb)-1)])

ggplot(hexdf, aes(x=x, y=y)) +
  geom_hex(stat="identity", aes(fill=bin)) +
  scale_fill_gradientn(colors=rev(clrs[-1]),
                       guide="legend",        # (1) make legend discrete
                       labels=all_break_labs, # (2) specify labels
                       name="Count") +        # (3) legend title
  # All the other prettification from the OP
  geom_abline(intercept = 0, color = "red", size = 0.25) +
  labs(x = "A", y = "C") +
  coord_fixed(xlim = c(-0.5, (maxRange[2]+buffer)),
              ylim = c(-0.5, (maxRange[2]+buffer))) +
  theme(aspect.ratio=1)

所有这些都给我们留下了以下图表:

Final hex plot

希望能帮到你。为了完整起见,请完整填写新代码:

# ... the rest of your code before the plots
clrs <- clrs[3:length(clrs)]
hexdf$countColor <- cut(hexdf$counts,
                        breaks = c(0, my_breaks, Inf),
                        labels = rev(clrs))

### START OF NEW CODE ###

# create new bin variable
all_breaks <- c(0, my_breaks)
breaks_n <- 1:length(all_breaks)
get_break_n <- function(n) {
  break_idx <- max(which((all_breaks - n) < 0))
  breaks_n[break_idx]
}
hexdf$bin <- sapply(hexdf$counts, get_break_n)

# create legend labels
all_break_labs <- as.character(all_breaks[1:(length(all_breaks)-1)])

# create final plot
ggplot(hexdf, aes(x=x, y=y)) +
  geom_hex(stat="identity", aes(fill=bin)) +
  scale_fill_gradientn(colors=rev(clrs[-1]),
                       guide="legend",
                       labels=all_break_labs,
                       name="Count") +
  geom_abline(intercept = 0, color = "red", size = 0.25) +
  labs(x = "A", y = "C") +
  coord_fixed(xlim = c(-0.5, (maxRange[2]+buffer)),
              ylim = c(-0.5, (maxRange[2]+buffer))) +
  theme(aspect.ratio=1)