如何添加一个带有不同大小和颜色的ggplot2子标题?

110

我正在使用ggplot2来改进降水柱状图。

这是一个可重现的例子,展示了我想要实现的效果:

library(ggplot2)
library(gridExtra)
secu <- seq(1, 16, by=2)
melt.d <- data.frame(y=secu, x=LETTERS[1:8])
m <- ggplot(melt.d, aes(x=x, y=y)) +
  geom_bar(fill="darkblue") + 
  labs(x="Weather    stations", y="Accumulated Rainfall [mm]") +
  opts(axis.text.x=theme_text(angle=-45, hjust=0, vjust=1),
       title=expression("Rainfall"), plot.margin = unit(c(1.5, 1, 1, 1), "cm"),
       plot.title = theme_text(size = 25, face = "bold", colour = "black", vjust = 5))
z <- arrangeGrob(m, sub = textGrob("Location", x = 0, hjust = -3.5, vjust = -33, gp = gpar(fontsize = 18, col = "gray40"))) #Or guessing x and y with just option
z

我不知道如何避免在ggplot2中使用猜测的数字来设置hjust和vjust?有没有更好的方法来添加副标题(不仅仅是使用\n,而是一个具有不同文本颜色和大小的副标题)?

我需要能够使用ggsave生成PDF文件。

以下是两个相关问题:

如何在R中将脚注引文添加到图形区域之外?

如何在R中添加副标题并更改ggplot绘图的字体大小?

感谢任何帮助。


vjust=-33 在 Linux 上对我起作用了。我知道 sub 应该放在图形下方,但这是我得到想要的唯一方法。 - Migue
由于某种原因,这会使我的图形非常小,并在图形下方创建一个巨大的空间。 - zazu
2
@hrbrmstr的答案似乎是目前要走的路。 - andschar
7个回答

132
最新的ggplot2版本(即2.1.0.9000或更高版本)已经内置了副标题和下方的图表说明功能。这意味着你可以这样做:
library(ggplot2) # 2.1.0.9000+ 

secu <- seq(1, 16, by=2)
melt.d <- data.frame(y=secu, x=LETTERS[1:8])

m <-  ggplot(melt.d, aes(x=x, y=y))
m <- m + geom_bar(fill="darkblue", stat="identity")
m <- m + labs(x="Weather    stations", 
              y="Accumulated Rainfall [mm]",
              title="Rainfall",
              subtitle="Location")
m <- m + theme(axis.text.x=element_text(angle=-45, hjust=0, vjust=1)) 
m <- m + theme(plot.title=element_text(size=25, hjust=0.5, face="bold", colour="maroon", vjust=-1))
m <- m + theme(plot.subtitle=element_text(size=18, hjust=0.5, face="italic", color="black"))
m

1
在(函数(el,elname)中出现错误: “plot.subtitle”不是有效的主题元素名称。 - Luís de Sousa
3
请阅读答案:“最新的ggplot2版本(即2.1.0.9000或更新版本)”。 - hrbrmstr

79

请忽略此回答ggplot2版本2.2.0拥有标题和副标题功能。请查看@hrbrmstr的回答下面的链接


你可以在expression中使用嵌套的atop函数来获得不同大小的文本。

编辑 更新了适用于ggplot2 0.9.3的代码。

m <-  ggplot(melt.d, aes(x=x, y=y)) + 
     geom_bar(fill="darkblue", stat = "identity") + 
     labs(x="Weather    stations", y="Accumulated Rainfall [mm]") + 
     ggtitle(expression(atop("Rainfall", atop(italic("Location"), "")))) +
     theme(axis.text.x = element_text(angle=-45, hjust=0, vjust=1), 
     #plot.margin = unit(c(1.5, 1, 1, 1), "cm"), 
     plot.title = element_text(size = 25, face = "bold", colour = "black", vjust = -1))

输入图像描述


10
你好,这是一个很棒的解决方案。我想使用它,但是我想用一个对象:atop(italic(my_string_vector)),而不是atop(italic("Location"))。我尝试了这种方法,但是结果出现了 (my_string_vector) 的字幕。如何强制该表达式使用字符串值,并避免将提供的文本视为文字意义? - Konrad
1
如果您在使用变量时遇到问题,应该使用bquote而不是expression,请参见这里 - toto_tico
3
为使用对象,用 bquote替换 expression,并在 .()中包装对象,例如,对于存储在名为 "main.title" 的对象中的主标题,以及存储在名为 "sub.title" 的对象中的副标题,请这样写:ggtitle(bquote(atop(.(main.title), atop(italic(.(sub.title)), ""))))。 感谢Didzis Elferts在此处提供的答案:https://dev59.com/8mIj5IYBdhLWcg3wuHTP - coip

12

将grobs添加到gtable并以此制作出漂亮的标题并不难。

library(ggplot2)
library(grid)
library(gridExtra)
library(magrittr)
library(gtable)

p <- ggplot() + 
  theme(plot.margin = unit(c(0.5, 1, 1, 1), "cm"))

lg <- list(textGrob("Rainfall", x=0, hjust=0, 
                    gp = gpar(fontsize=24, fontfamily="Skia", face=2, col="turquoise4")),
               textGrob("location", x=0, hjust=0, 
                        gp = gpar(fontsize=14, fontfamily="Zapfino", fontface=3, col="violetred1")),
           pointsGrob(pch=21, gp=gpar(col=NA, cex=0.5,fill="steelblue")))

margin <- unit(0.2, "line")
tg <- arrangeGrob(grobs=lg, layout_matrix=matrix(c(1,2,3,3), ncol=2),
                  widths = unit.c(grobWidth(lg[[1]]), unit(1,"null")),
                  heights = do.call(unit.c, lapply(lg[c(1,2)], grobHeight)) + margin)

grid.newpage()
ggplotGrob(p) %>%
  gtable_add_rows(sum(tg$heights), 0) %>%
  gtable_add_grob(grobs=tg, t = 1, l = 4)  %>%
  grid.draw()

enter image description here


10

似乎自ggplot 2 0.9.1版本以来,opts功能已过时且不再可用。截至今天,最新版本中这个对我有用:+ ggtitle(expression(atop("顶部行", atop(italic("第2行"), ""))))


这也可以不用最后的, "" - 那部分是干什么用的呢? - naught101
不清楚。我可能是复制了我在别处看到的例子。 - Aren Cambre
可能是来自@SandyMuspratt上面的答案:我现在明白了,atop()类似于没有横线的分数。因此,在第一个atop()中放置第二个atop()会给您一个子分数,其中文本比例较小。 ""是子分数的底部。但它似乎是不必要的 - 或许atop()对于第二个参数有一个默认的空字符串。 - naught101
看起来 @SandyMuspratt 的回答在我发表我的回答之后被修改,以反映类似于我的代码。 :-) - Aren Cambre

8
这个版本使用了gtable函数。它允许在标题中显示两行文本。每行的文本、大小、颜色和字体可以独立设置,但是该函数只能修改单一绘图面板的绘图。
小改动:升级到ggplot2 v2.0.0。
# The original plot
library(ggplot2)

secu <- seq(1, 16, by = 2)
melt.d <- data.frame(y = secu, x = LETTERS[1:8])

m <- ggplot(melt.d, aes(x = x, y = y)) + 
     geom_bar(fill="darkblue", stat = "identity") + 
     labs(x = "Weather    stations", y = "Accumulated Rainfall [mm]") + 
     theme(axis.text.x = element_text(angle = -45, hjust = 0, vjust = 1))


# The function to set text, size, colour, and face
plot.title = function(plot = NULL, text.1 = NULL, text.2 = NULL, 
   size.1 = 12,  size.2 = 12,
   col.1 = "black", col.2 = "black", 
   face.1 = "plain",  face.2 = "plain") {

library(gtable)
library(grid)

gt = ggplotGrob(plot)

text.grob1 = textGrob(text.1, y = unit(.45, "npc"), 
   gp = gpar(fontsize = size.1, col = col.1, fontface = face.1))
text.grob2 = textGrob(text.2,  y = unit(.65, "npc"), 
   gp = gpar(fontsize = size.2, col = col.2, fontface = face.2))

text = matrix(list(text.grob1, text.grob2), nrow = 2)
text = gtable_matrix(name = "title", grobs = text, 
   widths = unit(1, "null"), 
   heights = unit.c(unit(1.1, "grobheight", text.grob1) + unit(0.5, "lines"), unit(1.1,  "grobheight", text.grob2) + unit(0.5, "lines")))

gt = gtable_add_grob(gt, text, t = 2, l = 4)
gt$heights[2] = sum(text$heights)

class(gt) =  c("Title", class(gt))

gt
}

# A print method for the plot
print.Title <- function(x) {
   grid.newpage()   
   grid.draw(x)
}


# Try it out - modify the original plot
p = plot.title(m, "Rainfall", "Location", 
   size.1 = 20, size.2 = 15, 
   col.1 = "red", col.2 = "blue", 
   face.2 = "italic")

p

enter image description here


3
你可以使用grid.arrange将图表包装起来,并传递自定义基于网格的标题。

enter image description here

library(ggplot2)
library(gridExtra)

p <- ggplot() + 
  theme(plot.margin = unit(c(0.5, 1, 1, 1), "cm"))

tg <- grobTree(textGrob("Rainfall", y=1, vjust=1, gp = gpar(fontsize=25, face=2, col="black")),
               textGrob("location", y=0, vjust=0, gp = gpar(fontsize=12, face=3, col="grey50")),
               cl="titlegrob")
heightDetails.titlegrob <- function(x) do.call(sum,lapply(x$children, grobHeight))

grid.arrange(p, top = tg)

ggplot的当前版本使用theme和element_text替代opts和theme_text。此外,在当前版本的gridExtra(gridExtra_0.8.1)和ggplot2(ggplot2_0.9.3.1)中,ggplotGrob方法似乎无法正常工作。 - russellpierce

2
您可能已经注意到,Sandy的代码没有为“降雨量”产生粗体标题 - 使其粗体应在atop()函数中而不是theme()函数中进行指示。
ggplot(melt.d, aes(x=x, y=y)) + 
 geom_bar(fill="darkblue", stat = "identity") + 
 labs(x="Weather    stations", y="Accumulated Rainfall [mm]") + 
 ggtitle(expression(atop(bold("Rainfall"), atop(italic("Location"), "")))) +
 theme(axis.text.x = element_text(angle=-45, hjust=0, vjust=1),
 plot.title = element_text(size = 25, colour = "black", vjust = -1))

enter image description here


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