4

Going to try this again with a better MRE...for context, here's the product I'm currently trying to improve

enter image description here

What I'm trying to do is get the lines from the endpoints to the labels to be the same color as the data lines.

For purposes of this question we can work with this script

library(ggplot2)
library(babynames)
library(dplyr)
library(ggrepel)
library(ggsci)

data <- babynames %>%
  filter(name %in% c("Ashley", "Patricia", "Mary", "Minnie")) %>%
  filter(sex=="F")

data <- data %>% group_by(name) %>%
  mutate(change = n - lag(n)) %>% 
  mutate(meanC = mean(change, na.rm = TRUE)) %>%
  ungroup()

data$label <- paste(data$name,"\n",round(data$meanC,0),sep="" )  

minYear = min(data$year)
maxYear = max(data$year)

#endpoint layer
Endpoints <- data %>% 
  group_by(name) %>%
  filter(year == max(year)) %>%
  select(year, name, n, label) %>%
  ungroup()
         
namePlot <- data %>%
  ggplot(mapping = aes(x=year, y=n)) +
  geom_line(aes(color=name), show.legend = FALSE) +
  coord_cartesian(xlim = c(minYear, maxYear+10)) +
  scale_color_ucscgb() +
  geom_point(data = Endpoints, size=1.5, shape=21,
            aes(color=name, fill=name), show.legend=FALSE) +
  geom_label_repel(data=Endpoints, aes(label=label),
                  color = c("forestgreen","red")[1+grepl("\\-\\d",Endpoints$label)],
                  show.legend = FALSE,
                  vjust = 0, xlim=c(maxYear+3,maxYear+10), size=3, direction='y')
         
print(namePlot)

which produces this plot

enter image description here

The colors of the labels is controlled by color = c("forestgreen","red")[1+grepl("\\-\\d",Endpoints$label)], so that, in this case, data with a positive value in the label is green and data with a negative value is red. What I'd like to is make the connecting lines from the endpoints to the label boxes be the same color as the data lines, which are controlled by geom_line(aes(color=name),show.legend = FALSE

In the ggrepel docs there is a segment.color parameter that can control the color of the line segment, but it is not an aesthetic. So it appears it has to be "hard-coded" like segment.color="red" which doesn't really help me. I also found this discussion about the issue that seemed to present a solution, but I have been unable to get it to work. Part of the issue there is that it involves scale_color_discrete(aesthetics = c("color", "segment.color")) and I already have scale_color_ucscgb() so I get a warning about replacing scales...

Any guidance would be most appreciated.

jerH
  • 1,085
  • 1
  • 12
  • 30
  • 2
    I installed the development version of **ggrepel* from GitHub (`devtools::install_github("slowkow/ggrepel")`) and the approach used in the discussion you link to then seemed to work for me for that example. I haven't tried it with your code. However, rather than adding a new color scale try using `aesthetics = c("color", "segment.color")` within `scale_color_ucscgb()`. – aosmith Jul 21 '20 at 16:02
  • @jerH: not answering your question but please consider not using `red` and `green` together because they are not good for colorblind people https://www.nature.com/articles/519291d & https://blogs.egu.eu/divisions/gd/2017/08/23/the-rainbow-colour-map/ – Tung Jul 21 '20 at 16:17
  • @aosmith Tried that and got `Error in self$palette(n) : attempt to apply non-function` If I remove the `aesthetics = c("color", "segment.color")` within `scale_color_ucscgb()` I get an error as you'd expect `Error: Unknown colour name: Ashley`. If I remove `scale_color_ucscgb()` and instead use `scale_color_discrete(aesthetics = c("color", "segment.color"))` it works. I guess my boss will just have to decide which he's more interested in, the consistent color scale across lots of different plots or the colored segment lines – jerH Jul 22 '20 at 16:12
  • 2
    @Tung I lost that argument. The boss wants green for improving metrics and red for deteriorating metrics and doesn't want to hear my thoughts on good visualization design or what Tufte would do :). – jerH Jul 22 '20 at 16:15
  • Hmm, if I'm reading things correctly it looks like the CRAN version of package **ggsci** (for `scale_color_ucscgb()`) hasn't been updated since 2018, and I *think* the `aesthetics` argument may be newer than that. I wonder if you could use `pal_uscsgb()` to get the correct color palette so you can use it in `scale_color_discrete()`; like `palette = ggsci::pal_uscsgb()` or something similar. Then you might be able to get the color scale AND the segment colors workings. – aosmith Jul 22 '20 at 16:29
  • @jerH: totally understand. Maybe this scientific rainbow colormap can help? http://www.fabiocrameri.ch/batlow.php – Tung Jul 22 '20 at 16:34
  • To get the color names used in that palette, you can use: `pal_ucscgb('default')(number)`, where `number` is the # of colors you want to show. – chemdork123 Jul 23 '20 at 00:19
  • 1
    @aosmith That did it...as soon as I typed "ucscgb" correctly :) – jerH Jul 23 '20 at 13:34
  • Glad you got it working. Maybe put your final solution code in as an answer for the record? – aosmith Jul 23 '20 at 14:01

1 Answers1

1

Working version based on guidance from @aosmith

library(ggplot2)
library(babynames)
library(dplyr)
library(ggrepel)
library(ggsci)

data <- babynames %>%
  filter(name %in% c("Ashley", "Patricia", "Mary", "Minnie")) %>%
  filter(sex=="F")

data <- data %>% group_by(name) %>%
  mutate(change = n - lag(n)) %>% 
  mutate(meanC = mean(change, na.rm = TRUE)) %>%
  ungroup()

data$label <- paste(data$name,"\n",round(data$meanC,0),sep="" )  

minYear = min(data$year)
maxYear = max(data$year)

#endpoint layer
Endpoints <- data %>% 
  group_by(name) %>%
  filter(year == max(year)) %>%
  select(year, name, n, label) %>%
  ungroup()

namePlot <- data %>%
  ggplot(mapping = aes(x=year, y=n)) +
  geom_line(aes(color=name), show.legend = FALSE) +
  coord_cartesian(xlim = c(minYear, maxYear+15)) +
  geom_point(data = Endpoints, size=1.5, shape=21,
             aes(color=name, fill=name), show.legend=FALSE) +
  geom_label_repel(data=Endpoints, aes(label=label,
                                       segment.color=name),
                   color = c("forestgreen","red")[1+grepl("\\-\\d",Endpoints$label)],
                   show.legend = FALSE,
                   force = 50, 
                   vjust = 0, xlim=c(maxYear+5,maxYear+12), size=3, direction='y') +
  scale_color_discrete(aesthetics = c("color", "segment.color"))

print(namePlot)

produces

enter image description here

jerH
  • 1,085
  • 1
  • 12
  • 30