0

I have the following simple MWE.

set.seed(2022102517)
df <- data.frame(letter =factor(colors()[1:8], levels = colors()[1:8]), value = rnorm(8))
library(ggplot2)
ggplot(data = df, aes(x = letter, y = value, group = 1)) + geom_line()

Which gives me a line plot for "value" for each "letter" as follows:

enter image description here

Note the ordering.

Now, what I would like to do is to have a small square in front of each colorname of different (assigned) color (from fill.col) (in the x-axis). So, basically, wherever we have a letter on x-axis, then add a filled color box to the left of each letter, where the colors are picked up from,say, fill.col.

fill.col <- c("#005A32", "#FC8D62", "#D95F02", "#8DA0CB", "#7570B3", "#E78AC3", "#E7298A", "#A6D854")

I tried the following (from the solution provided below):

library(tidyverse)
library(RColorBrewer)
library(ggtext)

df %>% 
  mutate(new_letter = paste("<span style = 'color: ",
                            fill.col,
                            ";'>",
                            "\u25a0",
                            "</span>",
                            " ",
                            letter,
                            sep = ""), 
         new_letter = fct_reorder(new_letter, as.character(letter))) %>%
  ggplot(aes(new_letter, value, group = 1)) + 
  geom_line() + 
  theme(axis.text.x = element_markdown(size = 12))

which gives me:

enter image description here

It appears to me that in this case, we are reordering "letter" by the alphabet, though the color attachments appear to be correct here. Is it possible to keep the original order so that I can have control on the display? Thanks for further help and clarifications!

user3236841
  • 1,088
  • 1
  • 15
  • 39

2 Answers2

2

One solution using ggtext, based on this answer. We can add squares by pasting the unicode for a filled square, "\u25a0".

library(tidyverse)
library(RColorBrewer)
library(ggtext)

df %>% 
  mutate(new_letter = paste("<span style = 'color: ",
                            brewer.pal(8, "Set2"),
                            ";'>",
                            "\u25a0",
                            "</span>",
                            " ",
                            letter,
                            sep = ""), 
         new_letter = fct_reorder(new_letter, as.character(letter))) %>%
  ggplot(aes(new_letter, value, group = 1)) + 
  geom_line() + 
  theme(axis.text.x = element_markdown())

Result:

enter image description here

A slightly easier way is to add the colors in the axis theme, but this will also color the letters (and generate a warning):

df %>% 
  mutate(letter = paste("\u25a0", letter)) %>% 
  ggplot(aes(letter, value, group = 1)) + 
  geom_line() + 
  theme(axis.text.x = element_text(color = brewer.pal(8, "Set2")))

Result:

enter image description here

neilfws
  • 32,751
  • 5
  • 50
  • 63
  • For the first case, can I have bigger squares? Also a border around it so that the lighter colors are easier to spot? Thanks! – user3236841 Oct 26 '22 at 23:25
  • Two options: you can use the unicode for "black large square" = "u2b1b", or there is a "size = " argument to `element_markdown` (but that will alter the letter size too). The squares look larger in RStudio than in this PNG export. As for a border: no, because that's not defined by the unicode. You could use different colors or add a background. – neilfws Oct 26 '22 at 23:44
  • Thanks! My letters are not really letters, but character strings. However, adding the colors changes the ordering (and appears to place things in an order I do not understand). If my question is clear, how do I get around this problem. I wanted the axis in the order of the original factor levels. I will update the example. – user3236841 Oct 26 '22 at 23:59
  • You can use `fct_reorder`, you may need to adapt my code depending on exactly what the real labels are. – neilfws Oct 27 '22 at 00:02
  • Basically, you need to supply the values of the labels in the order you want to see them. It's easy with single letters since a < b < c etc. For your real labels, you need to figure that out. – neilfws Oct 27 '22 at 00:44
  • Got it! The `fct_reorder` is unnecessary here. – user3236841 Oct 27 '22 at 00:46
1

This is cumbersome, but I can't think of a better solution:

set.seed(2022102517)
df <- data.frame(letter =letters[1:8], value = rnorm(8))
library(ggplot2)
ggplot(data = df, aes(x = letter, y = value, group = 1)) +
  geom_line() +
  coord_cartesian(clip = "off", ylim = c(-0.5, 1.2)) +
  annotate("rect", fill = "#66C2A5",
           xmin = 0.7, xmax = 0.85,
           ymin = -0.65, ymax = -0.6) +
  annotate("rect", fill = "#FC8D62",
           xmin = 1.7, xmax = 1.85,
           ymin = -0.65, ymax = -0.6) +
  annotate("rect", fill = "#8DA0CB",
           xmin = 2.7, xmax = 2.85,
           ymin = -0.65, ymax = -0.6) +
  annotate("rect", fill = "#E78AC3",
           xmin = 3.7, xmax = 3.85,
           ymin = -0.65, ymax = -0.6) +
  annotate("rect", fill = "#A6D854",
           xmin = 4.7, xmax = 4.85,
           ymin = -0.65, ymax = -0.6) +
  annotate("rect", fill = "#FFD92F",
           xmin = 5.7, xmax = 5.85,
           ymin = -0.65, ymax = -0.6) +
  annotate("rect", fill = "#E5C494",
           xmin = 6.7, xmax = 6.85,
           ymin = -0.65, ymax = -0.6) +
  annotate("rect", fill = "#B3B3B3",
           xmin = 6.7, xmax = 6.85,
           ymin = -0.65, ymax = -0.6) +
  annotate("rect", fill = "grey50",
           xmin = 7.7, xmax = 7.85,
           ymin = -0.65, ymax = -0.6)

Created on 2022-10-27 by the reprex package (v2.0.1)

Does this solve your problem, or are you looking for a more 'flexible' solution?

jared_mamrot
  • 22,354
  • 4
  • 21
  • 46
  • While I am looking for a more 'flexible' (automated) general solution, this does address my question. Thank you. Hopefully, someone will have some more ideas/thoughts. Btw, why is the `coord_cartesian` needed? To specify the y-axes limits? – user3236841 Oct 26 '22 at 23:16
  • You need `coord_cartesian(clip = "off")` to plot the annotation outside the 'plotting' area. I'll try to come up with a more general solution, but hopefully someone else has a better approach – jared_mamrot Oct 26 '22 at 23:19
  • ah, got it! your solution does give me control. – user3236841 Oct 27 '22 at 00:39