0

I have a figure with geom_segments of different size. The legend superposes dashes and dots of different sizes. How to prevent that?

Reproductible example:

dataframe = data.frame(country = c("Burundi", "Kenya"),
                       rate1 = c(10, 3),
                       threshold1 = c(180, 72.2),
                       rate2 = c(25, 20),
                       threshold2 = c(242, 144),
                       rate3 = c(30,30))
xstart_limit <- 0
xend_limit <- 10000
linetypes <- c("dotted", "dashed")
my_colors <- c("blue","red")

pit_comparison2 <- ggplot() +
  geom_segment(aes(x = xstart_limit, y = rate1, xend = threshold1, yend = rate1, colour = country, linetype = country), size = 0.6, data = dataframe[dataframe$country == "Kenya",]) +
  geom_segment(aes(x = threshold1, y = rate1, xend = threshold1, yend = rate2, colour = country, linetype = country), size = 0.6, data = dataframe[dataframe$country == "Kenya",]) +
  geom_segment(aes(x = threshold1, y = rate2, xend = threshold2, yend = rate2, colour = country, linetype = country), size = 0.6, data = dataframe[dataframe$country == "Kenya",]) +
  geom_segment(aes(x = threshold2, y = rate2, xend = threshold2, yend = rate3, colour = country, linetype = country), size = 0.6, data = dataframe[dataframe$country == "Kenya",]) +
  geom_segment(aes(x = threshold2, y = rate3, xend = xend_limit, yend = rate3, colour = country, linetype = country), size = 0.6, data = dataframe[dataframe$country == "Kenya",]) +
  
  geom_segment(aes(x = xstart_limit, y = rate1, xend = threshold1, yend = rate1, colour = country, linetype = country), size = 1, data = dataframe[dataframe$country == "Burundi",]) +
  geom_segment(aes(x = threshold1, y = rate1, xend = threshold1, yend = rate2, colour = country, linetype = country), size = 1, data = dataframe[dataframe$country == "Burundi",]) +
  geom_segment(aes(x = threshold1, y = rate2, xend = threshold2, yend = rate2, colour = country, linetype = country), size = 1, data = dataframe[dataframe$country == "Burundi",]) +
  geom_segment(aes(x = threshold2, y = rate2, xend = threshold2, yend = rate3, colour = country, linetype = country), size = 1, data = dataframe[dataframe$country == "Burundi",]) +
  geom_segment(aes(x = threshold2, y = rate3, xend = xend_limit, yend = rate3, colour = country, linetype = country), size = 1, data = dataframe[dataframe$country == "Burundi",]) +
  
  scale_linetype_manual(values = linetypes, breaks=c("Burundi", "Kenya")) +
  scale_color_manual(values = my_colors, breaks=c("Burundi", "Kenya")) 

pit_comparison2

plot

I tried many different things, but never could fix this problem with the legend.

Ben Bolker
  • 211,554
  • 25
  • 370
  • 453
Antoine D
  • 21
  • 2

2 Answers2

3

You can put linewidth in the aesthetic mapping and map it to `country.

library(ggplot2)
dataframe = data.frame(country = c("Burundi", "Kenya"),
                       rate1 = c(10, 3),
                       threshold1 = c(180, 72.2),
                       rate2 = c(25, 20),
                       threshold2 = c(242, 144),
                       rate3 = c(30,30))
xstart_limit <- 0
xend_limit <- 10000
linetypes <- c("dotted", "dashed")
my_colors <- c("blue","red")

pit_comparison2 <- ggplot() +
  geom_segment(aes(x = xstart_limit, y = rate1, xend = threshold1, yend = rate1, colour = country, linetype = country, linewidth=country), , data = dataframe[dataframe$country == "Kenya",]) +
  geom_segment(aes(x = threshold1, y = rate1, xend = threshold1, yend = rate2, colour = country, linetype = country, linewidth=country), data = dataframe[dataframe$country == "Kenya",]) +
  geom_segment(aes(x = threshold1, y = rate2, xend = threshold2, yend = rate2, colour = country, linetype = country, linewidth=country), data = dataframe[dataframe$country == "Kenya",]) +
  geom_segment(aes(x = threshold2, y = rate2, xend = threshold2, yend = rate3, colour = country, linetype = country, linewidth=country), data = dataframe[dataframe$country == "Kenya",]) +
  geom_segment(aes(x = threshold2, y = rate3, xend = xend_limit, yend = rate3, colour = country, linetype = country, linewidth=country), data = dataframe[dataframe$country == "Kenya",]) +
  
  geom_segment(aes(x = xstart_limit, y = rate1, xend = threshold1, yend = rate1, colour = country, linetype = country, linewidth=country), data = dataframe[dataframe$country == "Burundi",]) +
  geom_segment(aes(x = threshold1, y = rate1, xend = threshold1, yend = rate2, colour = country, linetype = country, linewidth=country),  data = dataframe[dataframe$country == "Burundi",]) +
  geom_segment(aes(x = threshold1, y = rate2, xend = threshold2, yend = rate2, colour = country, linetype = country, linewidth=country),  data = dataframe[dataframe$country == "Burundi",]) +
  geom_segment(aes(x = threshold2, y = rate2, xend = threshold2, yend = rate3, colour = country, linetype = country, linewidth=country),  data = dataframe[dataframe$country == "Burundi",]) +
  geom_segment(aes(x = threshold2, y = rate3, xend = xend_limit, yend = rate3, colour = country, linetype = country, linewidth=country),  data = dataframe[dataframe$country == "Burundi",]) +
  scale_linewidth_manual(values=c(.6,1)) +   
  scale_linetype_manual(values = linetypes, breaks=c("Burundi", "Kenya")) +
  scale_color_manual(values = my_colors, breaks=c("Burundi", "Kenya")) 

pit_comparison2

Created on 2023-04-09 with reprex v2.0.2

DaveArmstrong
  • 18,377
  • 2
  • 13
  • 25
2

You can fix the way a legend looks without modifying what you see in the plot by defining and overriding those aesthetics in guides(). However, I would also strongly suggest a different approach to plotting the data you have here.

Changing Aesthetics in the Legend (without altering the plot)

The guides() function can be used to specify different ways to show each legend. Since you are applying both color and linetype aesthetics to the data, we have to be careful not to change appearance in a way that will result in a split legend. You can specify different linewidth values for each item in the legend via guide_legend(override.aes=list(...)). I'm also specifying a wider key width to show the line a bit better.

pit_comparison2 + guides(
  linetype=guide_legend(
    override.aes = list(linewidth=c(1,0.6)),
    keywidth = unit(1, "cm")
  )
)

enter image description here

A Better way to Plot: Use Path Geom

The better way to plot is (1) use tidy data principles to organize your data better, and (2) use geom_path() instead of individual calls to geom_segment(). If your data becomes much larger, you'll have a huge set of code just to create your plot. Also, anytime you get new data, you have to add each of those data points as a line in your data and a line in your code! It's much better to think about a better convention for organizing your data and plot with code that gives the result which is not connected to the number of observations in the data.

The path geom draws lines between points in a dataset, so it's useful for data like this.

First, your data frame is represented below in a way that is appropriate for Tidy Data:

df <- data.frame(
  country = rep(c("Burundi", "Kenya"), each=6),
  threshold = c(
    0, 180,  180 , 242, 242, 10000,
    0, 72.2, 72.2, 144, 144, 10000
  ),
  rate = c(
    10, 10, 25, 25, 30, 30,
     3,  3, 20, 20, 30, 30
  )
)

The entire plot code is much simpler, and can be used regardless of how many observations you have in the data frame df:

ggplot(data=df, aes(x=threshold, y=rate)) +
  geom_path(aes(color=country, linetype=country), size=0.6) +
  scale_linetype_manual(values = linetypes, breaks=c("Burundi", "Kenya")) +
  scale_color_manual(values = my_colors, breaks=c("Burundi", "Kenya"))

enter image description here

chemdork123
  • 12,369
  • 2
  • 16
  • 32