¿Cómo agregar un subtítulo ggplot2 con diferentes tamaños y colores?

88

Estoy usando ggplot2 para mejorar los gráficos de barras de precipitación.

Aquí hay un ejemplo reproducible de lo que quiero lograr:

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

No sé cómo evitar el uso de adivinar números en hjust y vjust en ggplot2. ¿Existe una mejor manera de poner un subtítulo (no solo usando \ n, sino un subtítulo con diferente color y tamaño de texto)?

Necesito poder usar con ggsave para tener un archivo pdf.

Aquí hay dos preguntas relacionadas:

¿Agregar una cita de nota al pie fuera del área de la parcela en R?

¿Cómo puedo agregar un subtítulo y cambiar el tamaño de fuente de los gráficos de ggplot en R?

Gracias por cualquier ayuda.

Migue
fuente
El vjust = -33 funcionó para mí en Linux. Sé que ese submarino está destinado a ir por debajo de la trama, pero fue la única forma en que obtuve lo que quería.
Migue
por alguna razón, esto hace que mi trama sea realmente pequeña y crea un gran espacio debajo del gráfico
zazu
2
La respuesta de @hrbrmstr parece ser el camino a seguir hoy en día
andschar

Respuestas:

102

Las últimas compilaciones de ggplot2 (es decir, 2.1.0.9000 o más reciente) tienen subtítulos y leyendas debajo del gráfico como funcionalidad incorporada. Eso significa que puedes hacer esto:

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
hrbrmstr
fuente
Error en (función (el, elname): "plot.subtitle" no es un nombre de elemento de tema válido.
Luís de Sousa
1
Lea las respuestas: "últimas compilaciones de ggplot2 (es decir, 2.1.0.9000 o más reciente)"
hrbrmstr
75

Ignore esta respuesta, la ggplot2 versión 2.2.0 tiene funcionalidad de título y subtítulo. Vea la respuesta de @ hrbrmstr a continuación .


Puede usar atopfunciones anidadas dentro de un expressionpara obtener diferentes tamaños.

EDITAR Código actualizado para 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))

ingrese la descripción de la imagen aquí

Sandy Muspratt
fuente
10
Hola, esta es una solución asombrosa. Me gustaría usar, pero en lugar de la atop(italic("Location")que le gustaría tener un objeto: atop(italic(my_string_vector). Intenté eso, pero luego el subtítulo se evaluó como (my_string_vector) . ¿Cómo forzar a esta expresión a utilizar el valor de cadena y no tratar el texto proporcionado literalmente?
Konrad
1
en caso de que tenga problemas al usar variables que debería usar en bquotelugar de expression, consulte aquí
toto_tico
3
Objetos de uso @Konrad, sustituir expressioncon bquotey envolver los objetos con .(), de esta manera, para un título principal almacenada en un objeto llamado "main.title" y de un subtítulo almacenado en un objeto llamado "sub.title": ggtitle(bquote(atop(.(main.title), atop(italic(.(sub.title)), "")))) El crédito va a Didzis La respuesta de Elferts aquí: stackoverflow.com/questions/19957536/…
coip
10

Parece que optsestá obsoleto a partir de ggplot 2 0.9.1 y ya no funciona. Esto funcionó para mí con las últimas versiones a partir de hoy: + ggtitle(expression(atop("Top line", atop(italic("2nd line"), "")))).

Aren Cambre
fuente
Esto también funciona sin el final , "", ¿para qué es esa parte?
naught101
Me gana. Es posible que haya copiado un ejemplo que vi en otro lugar.
Aren Cambre
Probablemente de la respuesta anterior de @ SandyMuspratt: P - Lo entiendo ahora, atop()es algo así como una fracción sin barras. Así que poner el segundo atop()dentro del primero te da una sub-fracción, con texto proporcionalmente más pequeño. El ""es el final de la sub-fracción. Sin embargo, parece innecesario, tal vez atop()tenga una cadena vacía predeterminada para el segundo parámetro, o algo así.
naught101
Parece que la respuesta de @ SandyMuspratt se modificó después de que publiqué mi respuesta para reflejar un código similar al mío. :-)
Aren Cambre
9

no es muy difícil agregar grobs a gtable y hacer un título elegante de esa manera,

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

ingrese la descripción de la imagen aquí

baptiste
fuente
8

Esta versión usa una gtablefunción. Permite dos líneas de texto en el título. El texto, el tamaño, el color y el tipo de letra de cada línea se pueden configurar independientemente de la otra. Sin embargo, la función modificará un gráfico con un solo panel de gráfico.

Edición menor: Actualización a 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

ingrese la descripción de la imagen aquí

Sandy Muspratt
fuente
3

Puede usar envolver el gráfico en grid.arrange y pasar un título personalizado basado en la cuadrícula,

ingrese la descripción de la imagen aquí

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)
baptiste
fuente
Las versiones actuales de ggplot usan theme y element_text en lugar de opts y theme_text respectivamente. Además, el enfoque ggplotGrob parece fallar en la versión actual de gridExtra (gridExtra_0.8.1) y ggplot2 (ggplot2_0.9.3.1)
russellpierce
2

Es posible que haya notado que el código de Sandy no produce un título en negrita para "Rainfall"; la instrucción para hacer esto en negrita debe ocurrir dentro de la función atop () en lugar de la función 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))

ingrese la descripción de la imagen aquí

Nathan
fuente