jerH
jerH

Reputation: 1299

Conditional control of ggplot geom_line

I'd like to be able to control the color of a geom_line based on a logical test. For my actual application this is a line plot overlaid on a bar plot, but hopefully this simple example will get me close enough

Starting with

library(ggplot2)
library(dplyr)
library(babynames)


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)

namePlot <- data %>%
  ggplot(mapping = aes(x=year, y=n)) +
  geom_line(show.legend = FALSE) +
  geom_label(aes(label=label, x = 1950, y=40000)) +
  facet_wrap(~ name)

print(namePlot)

I get

enter image description here

Then using a bit of a hack I got from elsewhere on this site, I can color the labels based on whether the value in the label is negative or not

library(ggplot2)
library(dplyr)
library(babynames)


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)

namePlot <- data %>%
  ggplot(mapping = aes(x=year, y=n)) +
  geom_line(show.legend = FALSE) +
  geom_label(aes(label=label, x = 1950, y=40000),
             color = c("forestgreen", "red")[1+grepl("\\-\\d",data$label)]) +
  facet_wrap(~ name)

print(namePlot)

Which produces

enter image description here

Now I'd like to make the line itself red or green to match the label color. I tried using the same hack (thought that would be too easy)

library(ggplot2)
library(dplyr)
library(babynames)


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)

namePlot <- data %>%
  ggplot(mapping = aes(x=year, y=n)) +
  geom_line(show.legend = FALSE, 
            color = c("forestgreen", "red")[1+grepl("\\-\\d",data$label)]) +
  geom_label(aes(label=label, x = 1950, y=40000),
             color = c("forestgreen", "red")[1+grepl("\\-\\d",data$label)]) +
  facet_wrap(~ name)

print(namePlot)

And got this

enter image description here

Thanks in advance!

Upvotes: 1

Views: 203

Answers (1)

teunbrand
teunbrand

Reputation: 37913

I think because geom line orders the data on the x-axis the colour you're trying to feed the layers are out of sync with the data as it is handled under the hood.

You could counter this by simply mapping the colour to an aesthetic, and adding a scale for the correct colours. Example below:

library(ggplot2)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(babynames)


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)

namePlot <- data %>%
  ggplot(mapping = aes(x=year, y=n)) +
  geom_line(show.legend = FALSE, 
            aes(colour = grepl("\\-\\d", label))) +
  geom_label(aes(label=label, x = 1950, y=40000,
                 colour = grepl("\\-\\d", label))) +
  scale_colour_manual(
    name = "Negative?",
    values = c("forestgreen", "red")
  ) +
  facet_wrap(~ name)

print(namePlot)

If you're really intent on keeping the colour outside of the mappings, you could use geom_path() instead.

namePlot <- data %>%
  ggplot(mapping = aes(x=year, y=n)) +
  geom_path(show.legend = FALSE, 
            color = c("forestgreen", "red")[1+grepl("\\-\\d",data$label)]) +
  geom_label(aes(label=label, x = 1950, y=40000),
             color = c("forestgreen", "red")[1+grepl("\\-\\d",data$label)]) +
  facet_wrap(~ name)

The resulting plot is almost the same but without a legend.

Upvotes: 1

Related Questions