通过 facets 对 GGPlot 的 geom_text 进行着色

5

希望有人能够帮助我解决一个ggplot脚本的问题。我想创建一个折线图,并在右边栏中为每条线添加标签。由于该脚本将多次使用,因此需要相对灵活地处理不同的数据。目前已经基本满足我的要求,但是我遇到了一个未能解决的问题。

这个脚本可以绘制分面图或非分面图的折线图。我遇到的问题是,在右边的标签区域中,如果随着时间的推移没有显著变化,则应将其颜色编码为黑色;如果有积极的变化,则为绿色;如果有消极的变化,则为红色。 当我只有一个分面时,我已经有了一个可行的脚本来实现这一点,但是一旦在图表中有多个分面,标签的颜色编码会出现以下错误。

   Error: Incompatible lengths for set aesthetics:

以下是带有多个方面数据的脚本。问题似乎在于我在geom_text行中指定颜色的方式。如果我删除脚本中geom_text行中的color调用,则可以在正确的位置打印属性,但没有颜色。我真的很困惑。这是我在这里的第一篇帖子,如果我在帖子中做错了什么,请告诉我。
   require(ggplot2)
require(grid)
require(zoo)
require(reshape)
require(reshape2)
require(directlabels)

time.data<-structure(list(Attribute = structure(c(1L, 1L, 2L, 2L, 3L, 3L, 
                                                  4L, 4L, 5L, 5L, 6L, 6L), .Label = c("Taste 1", "Taste 2", "Taste 3", 
                                                                                      "Use 1", "Use 2", "Use 3"), class = "factor"), Attribute.Category = structure(c(2L, 
                                                                                                                                                                      2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("Nutritional/Usage", 
                                                                                                                                                                                                                              "Taste/Quality"), class = "factor"), Attribute.Order = c(1L, 
                                                                                                                                                                                                                                                                                       1L, 2L, 2L, 3L, 3L, 4L, 4L, 5L, 5L, 6L, 6L), Category.Order = c(1L, 
                                                                                                                                                                                                                                                                                                                                                       1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L), Color = structure(c(1L, 
                                                                                                                                                                                                                                                                                                                                                                                                                        1L, 2L, 2L, 3L, 3L, 4L, 4L, 5L, 5L, 6L, 6L), .Label = c("#084594", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                "#2171B5", "#4292C6", "#6A51A3", "#807DBA", "#9E9AC8"), class = "factor"), 
                          value = c(75L, 78L, 90L, 95L, 82L, 80L, 43L, 40L, 25L, 31L, 
                                    84L, 84L), Date2 = structure(c(2L, 1L, 2L, 1L, 2L, 1L, 2L, 
                                                                   1L, 2L, 1L, 2L, 1L), .Label = c("1/1/2013", "9/1/2012"), class = "factor")), .Names = c("Attribute", 
                                                                                                                                                           "Attribute.Category", "Attribute.Order", "Category.Order", "Color", 
                                                                                                                                                           "value", "Date2"), class = "data.frame", row.names = c(NA, -12L
                                                                                                                                                           ))

label.data<-structure(list(7:12, Attribute = structure(1:6, .Label = c("Taste 1", 
                                                                       "Taste 2", "Taste 3", "Use 1", "Use 2", "Use 3"), class = "factor"), 
                           Attribute.Category = structure(c(2L, 2L, 2L, 1L, 1L, 1L), .Label = c("Nutritional/Usage", 
                                                                                                "Taste/Quality"), class = "factor"), Attribute.Order = 1:6, 
                           Category.Order = c(1L, 1L, 1L, 2L, 2L, 2L), Color = structure(1:6, .Label = c("#084594", 
                                                                                                         "#2171B5", "#4292C6", "#6A51A3", "#807DBA", "#9E9AC8"), class = "factor"), 
                           Significance = structure(c(2L, 3L, 1L, 1L, 3L, 2L), .Label = c("neg", 
                                                                                          "neu", "pos"), class = "factor"), variable = structure(c(1L, 
                                                                                                                                                   1L, 1L, 1L, 1L, 1L), .Label = "1/1/2013", class = "factor"), 
                           value = c(78L, 95L, 80L, 40L, 31L, 84L), Date2 = structure(c(1L, 
                                                                                        1L, 1L, 1L, 1L, 1L), .Label = "2013-01-01", class = "factor"), 
                           label.color = structure(c(1L, 2L, 3L, 3L, 2L, 1L), .Label = c("black", 
                                                                                         "forestgreen", "red"), class = "factor")), .Names = c("", 
                                                                                                                                               "Attribute", "Attribute.Category", "Attribute.Order", "Category.Order", 
                                                                                                                                               "Color", "Significance", "variable", "value", "Date2", "label.color"
                                                                                         ), class = "data.frame", row.names = c(NA, -6L))

color.palette<-as.character(unique(time.data$Color))

time.data$Date2<-as.Date(time.data$Date2,format="%m/%d/%Y")

plot<-ggplot()+
  geom_line(data=time.data,aes(as.numeric(time.data$Date2),time.data$value,group=time.data$Attribute,color=time.data$Color),size=1)+
  geom_text(data=label.data,aes(x=Inf, y=label.data$value, label=paste("  ",label.data$Attribute)),
            color=label.data$label.color,
            size=4,vjust=0, hjust=0,na.rm=T)+
  facet_grid(Attribute.Category~.,space="free")+
  theme_bw()+
  scale_x_continuous(breaks=as.numeric(unique(time.data$Date2)),labels=format(unique(time.data$Date2),format = "%b %Y"))+
  theme(strip.background=element_blank(),
        strip.text.y=element_blank(),
        legend.text=element_blank(),
        legend.title=element_blank(),
        plot.margin=unit(c(1,5,1,1),"cm"),
        legend.position="none")+
  scale_colour_manual(values=color.palette)

gt3 <- ggplot_gtable(ggplot_build(plot))
gt3$layout$clip[gt3$layout$name == "panel"] <- "off"
grid.draw(gt3)

欢迎来到StackOverflow。很高兴看到您提供数据和代码。我可以建议您更新代码,为两个变量plot.startplot.end提供值,因为它们目前未定义。 - SlowLearner
@user19686010 我怀疑为什么还没有人处理这个问题,可能是因为你提供的代码不是一个“最小化”的例子。也就是说,你给我们提供了很多(相当冗长的)代码,而不是一个简单明了、易于第三方在短时间内理解的紧凑示例。这会掩盖问题的真实本质,降低问题和答案的价值。我自己也曾经在 Stack Overflow 上掉入这个陷阱。正如这篇非常有用的帖子所指出的那样,你应该提供最小可运行代码。 - SlowLearner
谢谢SlowLearner。我今晚会尝试放置更简洁的内容。目前,我已更新代码以包括plot.start和plot.end。 - tkvaran
你有两个大型数据框(time.datalabel.data),因此一种方法是创建2个类似的数据框,仅使用3-4个日期作为x轴,3个Attribute而不是10个(像HighLowMed这样的短文本!),2个Attribute.Category。此外,对于示例,您真的需要Significancevariable和其他列吗?如果不需要,请删除所有这些内容。删除major.line.colorymin和其他声明-与问题无关。最后,省略theme()部分:这只是您稍后可以添加的外观调整。祝你好运! - SlowLearner
再次感谢Slowlearner。我刚刚用更简洁的示例编辑了我的原始回复。我留下了一些主题内容,但只有我认为真正关键的部分。 - tkvaran
注意:如果在上述代码中注释掉geom_text的“color=”行,则图形将运行,但右边距中标签的颜色编码不起作用。当正确工作时,标签应根据label.data$label.color变量中的值进行着色。 - tkvaran
1个回答

4

一些问题:

在你的美学声明中,你不应该引用数据列作为time.data$Date2,而只需要使用Date2即可。 data参数指定了要查找信息的位置(对于给定层来说,所有信息都必须在同一个数据框中,但是,正如您利用的那样,可以在各个层之间变化)。

geom_text调用中,color没有在aes调用中; 如果你将它映射到数据,它必须在aes调用中。在修复第一部分后,这会抛出一个不同的错误,因为它不知道在哪里寻找label.color,因为它不知道要在label.data中查找。

解决这些问题后,scale_colour_manual会抱怨只提供了6个颜色。这是因为有6个颜色来自线条,3个来自文本。由于你指定了这些实际颜色名称,所以可以使用scale_colour_identity

将所有这些放在一起:

plot <- ggplot()+
  geom_line(data=time.data, aes(as.numeric(Date2), value, 
                                group=Attribute, color=Color), 
            size=1)+
  geom_text(data=label.data, aes(x=Inf, y=value, 
                                 label=paste("  ",Attribute),
                                 color=label.color),
            size=4,vjust=0, hjust=0)+
  facet_grid(Attribute.Category~.,space="free") +
  scale_x_continuous(breaks=as.numeric(unique(time.data$Date2)),
                     labels=format(unique(time.data$Date2),format = "%b %Y")) +
  scale_colour_identity() +
  theme_bw()+
  theme(strip.background=element_blank(),
        strip.text.y=element_blank(),
        legend.text=element_blank(),
        legend.title=element_blank(),
        plot.margin=unit(c(1,5,1,1),"cm"),
        legend.position="none")
gt3 <- ggplot_gtable(ggplot_build(plot))
gt3$layout$clip[gt3$layout$name == "panel"] <- "off"
grid.draw(gt3)

上传图片描述

为了让您了解可以将示例简化到什么程度,以下是更接近最小的示例:

time.data <- 
structure(list(Attribute = structure(c(1L, 1L, 2L, 2L, 3L, 3L, 
4L, 4L), .Label = c("Taste 1", "Taste 2", "Use 1", "Use 2"), class = "factor"), 
    Attribute.Category = structure(c(2L, 2L, 2L, 2L, 1L, 1L, 
    1L, 1L), .Label = c("Nutritional/Usage", "Taste/Quality"), class = "factor"), 
    Color = c("#084594", "#084594", "#2171B5", "#2171B5", "#6A51A3", 
    "#6A51A3", "#807DBA", "#807DBA"), value = c(75L, 78L, 90L, 
    95L, 43L, 40L, 25L, 31L), Date2 = structure(c(15584, 15706, 
    15584, 15706, 15584, 15706, 15584, 15706), class = "Date")), .Names = c("Attribute", 
"Attribute.Category", "Color", "value", "Date2"), row.names = c(NA, 
-8L), class = "data.frame")

label.data <- 
structure(list(value = c(78L, 95L, 40L, 31L), Attribute = structure(1:4, .Label = c("Taste 1", 
"Taste 2", "Use 1", "Use 2"), class = "factor"), label.color = c("black", 
"forestgreen", "red", "forestgreen"), Attribute.Category = structure(c(2L, 
2L, 1L, 1L), .Label = c("Nutritional/Usage", "Taste/Quality"), class = "factor"), 
    Date2 = structure(c(15706, 15706, 15706, 15706), class = "Date")), .Names = c("value", 
"Attribute", "label.color", "Attribute.Category", "Date2"), row.names = c(NA, 
-4L), class = "data.frame")

ggplot() +
  geom_line(data = time.data, 
            aes(x=Date2, y=value, group=Attribute, colour=Color)) +
  geom_text(data = label.data,
            aes(x=Date2, y=value, label=Attribute, colour=label.color),
            hjust = 1) +
  facet_grid(Attribute.Category~.) +
  scale_colour_identity()

enter image description here

主题样式(以及使标签在图外可见)与问题无关,日期轴从日期转换为数字来处理“inf”的方式也与问题无关。我还将数据削减到只需要的列,并将分类变量减少为仅两个类别。


Brian,非常感谢您的帮助。无论是具体的示例还是更一般的教训,都让我受益匪浅。看到真正的最小示例是什么样子的非常有洞察力。我不知道我之前怎么会错过scale_color_identity。 - tkvaran
非常好的答案(我试过解决但失败了)。在我看来,使用内置数据集(如irismtcars)中的一个来创建一个最小化示例,效果会更好。 - Andrie
1
@Andrie 我也倾向于使用内置数据集的示例,但在这里我认为它们会忽略两个显著特征:一对数据集,一个带有数据,另一个带有注释;以及在两个数据集中明确指定的非重叠颜色。 - Brian Diggs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接