How to highlight a specific region of a line changing linetype or size using ggplot

Viewed 108

I have the following dataframe:

test = structure(list(Student = c("Ana", "Brenda", "Max", "Ana", "Brenda", 
"Max", "Ana", "Brenda", "Max"), Month = structure(c(1L, 1L, 1L, 
2L, 2L, 2L, 3L, 3L, 3L), .Label = c("January", "February", "March"
), class = "factor"), Grade = c(7L, 6L, 7L, 8L, 6L, 7L, 5L, 10L, 
10L), Change = c("February", "February", "February", "February", 
"February", "February", "February", "February", "February")), row.names = c(NA, 
-9L), class = "data.frame")

I plotted the grades of each student throughout the months, and wanted to know if there is a simple way to accentuate a specific part of each plotted line that would correspond to the period of time which showed the greatest shift in their grades (this info is already present in the dataset: column "Change". In this case, for all students it would be from February to March).

Trying to use a similar logic as presented here, to change the color a specific part of the line I attempted to change the linetype and/or size of the line (since I already used the color to group and display each student) in order to highlight that particular portion of the line. However, it doesn't seem to be as straightforward.

My attempts were the following:

ggplot(test, aes(x = Month, y = Grade, color = Student, group = Student)) + 
+     geom_point() + geom_line(data = test, aes(linetype = (ifelse(Month == Change, "solid", "dashed")))) 

Which yielded the error:

Error: geom_path: If you are using dotted or dashed lines, colour, size and linetype must be constant over the line

And

ggplot(test, aes(x = Month, y = Grade, color = Student, group = Student)) + 
    geom_point() + geom_line(data = test, aes(size = (ifelse(Month == Change, 1, 0.8)))) 

Which kinda does what I'm looking for, but looks horrible, and doesn't really seem like its using the size of the line that I'm trying to specify:

This is how it looks

How do I fix it? Thanks in advance! n_n

2 Answers

Don't specify the size in aes. Use the scale function

For another suggestion regarding line design see below.

test = structure(list(Student = c("Ana", "Brenda", "Max", "Ana", "Brenda", 
                                  "Max", "Ana", "Brenda", "Max"), Month = structure(c(1L, 1L, 1L, 
                                                                                      2L, 2L, 2L, 3L, 3L, 3L), .Label = c("January", "February", "March"
                                                                                      ), class = "factor"), Grade = c(7L, 6L, 7L, 8L, 6L, 7L, 5L, 10L, 
                                                                                                                      10L), Change = c("Februrary", "Februrary", "Februrary", "Februrary", 
                                                                                                                                       "Februrary", "Februrary", "Februrary", "Februrary", "Februrary"
                                                                                                                      )), row.names = c(NA, -9L), class = "data.frame")
## typo corrected
test$Change <- "February"

library(ggplot2)

ggplot(test, aes(x = Month, y = Grade, color = Student, group = Student)) + 
  geom_point() +
  ## don't specify size in aes
  geom_line(data = test, aes(size = Month == Change)) +
  ## change the size scale
  scale_size_manual(values = c(`TRUE` = 2, `FALSE` = .8))

Another option might be to make use of the ggforce::geom_link functions, which interpolate aesthetics between two points.

library(ggforce)

ggplot(test, aes(x = Month, y = Grade, color = Student, group = Student)) + 
  geom_point() +
  geom_link2(data = test, aes(size = Grade, ), lineend = "round") 

This fails when trying to change the line type. In this case, use geom_segment instead - you will need some data transformation though.

library(tidyverse)

test %>%
  group_by(Student) %>%
  mutate(xend = lead(Month), yend = lead(Grade)) %>%
ggplot(aes(x = Month, y = Grade, color = Student, group = Student)) + 
  geom_point() +
  geom_segment(aes(xend = xend, yend = yend, linetype = Month == Change)) +
  # need to specify your x
  scale_x_discrete(limits = unique(test$Month))
#> Warning: Removed 3 rows containing missing values (geom_segment).

Changing the line width along its course is possible (as tjebo points out), but it rarely makes for a nice plot. A clearer way might be simply to add a coloured background over the area of interest:

library(hrbrthemes)
library(ggplot2)

ggplot(test, aes(x = Month, y = Grade, color = Student, group = Student)) +
  geom_point(alpha = 0) +
  geom_rect(data = test[1,],
            aes(xmin = 'February', xmax = 'March', ymin = -Inf, ymax = Inf),
            color = NA, fill = 'deepskyblue4', alpha = 0.1) +
  geom_line(size = 1) +
  geom_point(shape = 21, fill = 'white', size = 3) +
  theme_minimal() +
  scale_color_manual(values = c('pink3', 'orange2', 'red4')) +
  theme_tinyhand()

enter image description here

Related