ggplot2: цветные отдельные слова в заголовке

Недавно я увидел линейную диаграмму в Economist, где заголовок имел цветные слова в соответствии с цветами групп, используемых в линейной диаграмме. Мне было интересно, как это сделать с объектом ggplot2. Вот некоторый код, чтобы сделать линейную диаграмму со всем, как в статье, связанной с econimist, за исключением цветных слов в названии. Внизу я показываю желаемый результат.

Этот вопрос касается не теоретических способов отображения этой информации (например, прямой маркировки или легенды), а скорее о раскраске отдельных слов в заголовках.

data <- data.frame(
    group = rep(c('affluence', 'poverty'), each = 6),
    year = rep(c(1970, 1980, 1990, 2000, 2010, 2012), 2),  
    concentration = c(.125, .12, .14, .13, .145, .146, .068, .09, .125, .119, .13, .135)
)

library(ggplot2)

ggplot(data, aes(year, concentration, color = group)) +
    geom_line(size = 1.5) +
    geom_point(size = 4) +
    scale_y_continuous(limits = c(0, .15)) +
    labs(
        x = NULL, y = NULL, 
        title = 'Concentration of affluence and poverty nationwide'
    ) +
    theme_minimal() +
    theme(
        legend.position = 'none'
    ) +
    scale_color_manual(values = c('#EEB422', '#238E68'))

enter image description here

Ответ 1

Это решение основано на отображении текста под графиком, созданным ggplot2, и раскрашивать части заголовка в сюжете (кредиты для участников там!).

Используя phantom заполнители для текста, мы избегаем (большую часть) жесткого кодирования позиций.

# create text grobs, one for each color
t1 <- textGrob(expression("Concentration of " * phantom(bold("affluence")) * "and" * phantom(bold("poverty")) * " nationwide"),
                 x = 0.5, y = 1.1, gp = gpar(col = "black"))

t2 <- textGrob(expression(phantom("Concentration of ") * bold("affluence") * phantom(" and poverty nationwide")),
                 x = 0.5, y = 1.1, gp = gpar(col = "#EEB422"))

t3 <- textGrob(expression(phantom("Concentration of affluence and ") * bold("poverty") * phantom(" nationwide")),
                 x = 0.5, y = 1.1, gp = gpar(col = "#238E68"))


# plot and add grobs with annotation_custom
p <- ggplot(data, aes(year, concentration, color = group)) +
  geom_line(size = 1.5) +
  geom_point(size = 4) +
  scale_y_continuous(limits = c(0, .15)) +
  labs(x = NULL, y = NULL) +
  theme_minimal() +
  theme(legend.position = 'none',
        # add some extra margin on top
        plot.margin = unit(c(4, 1, 1, 1), "lines")) +
  scale_color_manual(values = c("#EEB422", "#238E68")) +
  annotation_custom(grobTree(t1, t2, t3))

# create gtable and remove clipping
g <- ggplot_gtable(ggplot_build(p))
g$layout$clip[g$layout$name == "panel"] <- "off"

# re-draw
grid.draw(g)

enter image description here


С большим количеством цветных слов создание другого expression должно выполняться более программно. См. Например, приятную функцию multiTitle в аналогичном вопросе для base сюжета: название: слова разных цветов? , что должно быть полезно и в ggplot.

Ответ 2

Несколько громоздкое решение с annotation_custom:

ggplot(dat, aes(year, concentration, color = group)) +
  geom_line(size = 1.5) +
  geom_point(size = 4) +
  scale_y_continuous(limits = c(0, 0.16)) +
  labs(x = NULL, y = NULL, title = ' ') +
  theme_minimal() +
  theme(legend.position = 'none') +
  scale_color_manual(values = c('#EEB422', '#238E68')) +
  annotation_custom(textGrob('Concentration of', gp = gpar(col = 'black')), 
                    xmin = 1972, xmax = 1972, ymin = 0.165, ymax = 0.165) +
  annotation_custom(textGrob('affluence', gp = gpar(col = '#EEB422', fontface = 'bold')), 
                    xmin = 1975.7, xmax = 1975.7, ymin = 0.165, ymax = 0.165) +
  annotation_custom(textGrob(' and ', gp = gpar(col = 'black')), 
                    xmin = 1977.65, xmax = 1977.65, ymin = 0.165, ymax = 0.165) +
  annotation_custom(textGrob('poverty', gp = gpar(col = '#238E68', fontface = 'bold')), 
                    xmin = 1979.35, xmax = 1979.35, ymin = 0.165, ymax = 0.165) +
  annotation_custom(textGrob('nationwide', gp = gpar(col = 'black')), 
                    xmin = 1982, xmax = 1982, ymin = 0.165, ymax = 0.165)

который дает:

enter image description here

Основным недостатком этого подхода является то, что он требует много усилий с параметрами, чтобы получить слова названия на правильных местах.