Change symbol of the lowest value in each line of a stacked line graph in ggplot2

I have a stacked line graph with 3 lines. I want to plot the lowest value of each line with a different symbol (e.g., triangle). How can I do that? Here is the code I am using so far:

ggplot(df, aes(PSF)) + 
  geom_line(aes(y = Delhi), colour = "red") +
  geom_point(aes(y = Delhi), colour = "red") +
  geom_line(aes(y = Paris), colour = "dark green") + 
  geom_point(aes(y = Paris), colour = "dark green")  +
  geom_line(aes(y = Lahore), colour = "blue") + 
  geom_point(aes(y = Lahore), colour = "blue")  +
  theme(panel.background = element_blank()) + 
  labs(y = "RMSE") + 
  scale_x_continuous(breaks = seq(0, 3, 0.2)) + 
  scale_y_continuous(breaks = seq(6, 30, 2)) + 
  theme(axis.line = element_line(colour = "grey"))

And here is my dataset:

df <- read.table(text = "
PSF Delhi Paris Lahore
0.2  10   12    17
0.4  12   17    19
0.6  8    9     12
0.8  15   14    13
1    13   14    16", header = TRUE)

As you can see from the simplified dataset, the lowest values for the Delhi, Paris and Lahore are when the PSF value is equals to 0.6.

Below is one way to change the symbol of the lowest value. In the example, the data is reshaped so a new column can be added (myshape) to identify the minimum value within each name group. This new structure then allows the plot to be created with only one call to each geom_line() and geom_point().

library(tidyverse)

df <- read.table(text = "
PSF Delhi Paris Lahore
0.2  10   12    17
0.4  12   17    19
0.6  8    9     12
0.8  15   14    13
1    13   14    16", header = TRUE) 

out = df %>%
  pivot_longer(-'PSF') %>%
  group_by(name) %>%
  mutate(myshape = ifelse(value == min(value, na.rm = T), '3', '1')) %>%
  ungroup()

ggplot(out, aes(PSF)) +
  geom_line(aes(y = value, group = name, color = name)) +
  geom_point(aes(y = value, color = name, shape = myshape), size = 3) +
  scale_color_manual(values = c('Delhi' = 'red',
                                'Paris' = 'dark green',
                                'Lahore' = 'blue')) +
  theme(panel.background = element_blank()) + 
  labs(y = "RMSE") + 
  scale_x_continuous(breaks = seq(0, 3, 0.2)) + 
  scale_y_continuous(breaks = seq(6, 30, 2)) + 
  theme(axis.line = element_line(colour = "grey"))

Created on 2023-02-15 with reprex v2.0.2.9000

2 Likes

Forget all this. I see I totally misread the question

ggplot(df, aes(PSF)) + 
  geom_line(aes(y = Delhi), colour = "red") +
  geom_point(aes(y = Delhi), colour = "red") +
  geom_line(aes(y = Paris), colour = "dark green") + 
  geom_point(aes(y = Paris), colour = "dark green")  +
  geom_line(aes(y = Lahore), colour = "blue") + 
  geom_point(aes(y = Lahore), colour = "blue",shape = 17, size = 3)  +
  theme(panel.background = element_blank()) + 
  labs(y = "RMSE") + 
  scale_x_continuous(breaks = seq(0, 3, 0.2)) + 
  scale_y_continuous(breaks = seq(6, 30, 2)) + 
  theme(axis.line = element_line(colour = "grey"))

You do not need to plot each line separately as long as you do not mind different symbols on each line. You just need to change the data layout from wide to long

dat2  <-  structure(list(PSF = c(0.2, 0.4, 0.6, 0.8, 1, 0.2, 0.4, 0.6, 
0.8, 1, 0.2, 0.4, 0.6, 0.8, 1), City = structure(c(1L, 1L, 1L, 
1L, 1L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L), levels = c("Delhi", 
"Paris", "Lahore"), class = "factor"), RMSE = c(10L, 12L, 8L, 
15L, 13L, 12L, 17L, 9L, 14L, 14L, 17L, 19L, 12L, 13L, 16L)), row.names = c(NA, 
-15L), class = "data.frame")

ggplot(dat2, aes(PSF, RMSE, colour = City)) +
    geom_line() + geom_point(aes(shape = City, size = 2)) +
  guides( size = FALSE)

I did the change from wide to long (see code below) but you can do the same thing using the pivot_longer~ function in the ~tidyr package which is a part of the tidyverse parkage.

library(data.table)
dat2 <- melt(dat1, id.var = "PSF",  measure.variables = c("Delhi", "Paris", "London"), 
             variable.name = "City", value.name = "RMSE")

```[quote="jrkrideau, post:3, topic:160070, full:true"]

ggplot(df, aes(PSF)) +
geom_line(aes(y = Delhi), colour = "red") +
geom_point(aes(y = Delhi), colour = "red") +
geom_line(aes(y = Paris), colour = "dark green") +
geom_point(aes(y = Paris), colour = "dark green") +
geom_line(aes(y = Lahore), colour = "blue") +
geom_point(aes(y = Lahore), colour = "blue",shape = 17, size = 3) +
theme(panel.background = element_blank()) +
labs(y = "RMSE") +
scale_x_continuous(breaks = seq(0, 3, 0.2)) +
scale_y_continuous(breaks = seq(6, 30, 2)) +
theme(axis.line = element_line(colour = "grey"))


You do not need to plot each line separately as long as you do not mind different symbols on each line.  You just need to change the data layout from `wide` to `long`

dat2 <- structure(list(PSF = c(0.2, 0.4, 0.6, 0.8, 1, 0.2, 0.4, 0.6,
0.8, 1, 0.2, 0.4, 0.6, 0.8, 1), City = structure(c(1L, 1L, 1L,
1L, 1L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L), levels = c("Delhi",
"Paris", "Lahore"), class = "factor"), RMSE = c(10L, 12L, 8L,
15L, 13L, 12L, 17L, 9L, 14L, 14L, 17L, 19L, 12L, 13L, 16L)), row.names = c(NA,
-15L), class = "data.frame")

ggplot(dat2, aes(PSF, RMSE, colour = City)) +
geom_line() + geom_point(aes(shape = City, size = 2)) +
guides( size = FALSE)


I did the change from `wide` to `long` (see code below) but you can do the same thing using the `pivot_longer~ function in the ~tidyr` package which is a part of the `tidyverse` parkage.

library(data.table)
dat2 <- melt(dat1, id.var = "PSF", measure.variables = c("Delhi", "Paris", "London"),
variable.name = "City", value.name = "RMSE")

[/quote]

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.