最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

r - Set legend in ggplot with several factor levels without using aes() - Stack Overflow

programmeradmin1浏览0评论

I have 2 different dataframes :

# Factor levels
species_order <- c("AMPI","LALO", "HOLA","BASA","SNBU", "WRSA", "REPH","BBSA","CRPL", "PESA", "RUTU","REKN","AMGP","BBPL","LTJA", "PAJA", "ROPT", "PEFA","LTDU", "CORA", "RLHA", "RTLO", "GLGU", "KIEI","SNOW","PALO","SNGO", "CAGO", "ARFO","SACR", "ARHA","TUSW") 
# Dataframe Bylot
n <- 192
bylot <- data.frame(sp_code=rep(species_order, times = n/32),
                    ind_km2=sample(1:100, n, replace=TRUE),
                    site_code=factor(paste("BYLO")))

# Dataframe density
n <- 384
densityy <- data.frame(sp_code=rep(species_order, times = n/32),
                      ind_km2=sample(1:100, n, replace=TRUE),
                      site_code=rep(LETTERS, length.out = n))

I am making a plot like this one :

ggplot(densityy, aes(x=factor(sp_code, level = species_order), y=ind_km2, fill=sp_code, colour = sp_code)) +
  geom_violin(scale = "width", show.legend = FALSE) +
  stat_summary(fun=mean, geom="point", fill = "#FF8247", shape=23, size=3, show.legend = FALSE)+
  stat_summary(aes(x = sp_code, y = ind_km2), data = bylot, fun=mean, geom="point", fill = "#BBFFFF", shape=23, size=3, show.legend = FALSE)+
  scale_y_continuous(breaks = seq(0,400,25), expand = c(0, 0))+
  scale_fill_viridis_d(option = "D", begin = 0.2, direction = -1)+
  scale_color_viridis_d(option = "D", begin = 0.2, direction = -1)+
  labs(x = "Espèces", y = "Densité (individus/km2)")

I would like to add a legend that indicates that the orange points are the mean density for all sites, and the blue points are the mean for the site BYLO :

I've looked for solutions, and I've seen mostly aes(shape). Since I'm using 2 dataframes, I couldn't use aes(shape) for the bylot mean. Is there a way for me to add this legend without changing aes() ?

Here is something I tried, which didn't work :

ggplot(densityy, aes(x=factor(sp_code, level = species_order), y=ind_km2, fill=sp_code, colour = sp_code)) +
  geom_violin(scale = "width", show.legend = FALSE) +
  stat_summary(aes(shape = "Mean"), fun=mean, geom="point", fill = "#FF8247", shape=23, size=3, show.legend = FALSE)+
  stat_summary(aes(x = sp_code, y = ind_km2), data = Bylot, fun=mean, geom="point", fill = "#BBFFFF", shape=23, size=3, show.legend = FALSE)+
  scale_shape_manual("", values=c("Mean"="x"))+
  scale_y_continuous(breaks = seq(0,400,25), expand = c(0, 0))+
  scale_fill_viridis_d(option = "D", begin = 0.2, direction = -1)+
  scale_color_viridis_d(option = "D", begin = 0.2, direction = -1)+
  labs(x = "Espèces", y = "Densité (individus/km2)")+
  annotate("text",
           x = 1:length(table(density$sp_code)),
           y = aggregate(ind_km2 ~ sp_code, density, median)[ , 2],
           label = table(density$sp_code),
           col = "black",
           vjust = - 3)

# Message from the console
Warning messages:
3: No shared levels found between `names(values)` of the manual scale and the data's
shape values. 

Thank you!

I have 2 different dataframes :

# Factor levels
species_order <- c("AMPI","LALO", "HOLA","BASA","SNBU", "WRSA", "REPH","BBSA","CRPL", "PESA", "RUTU","REKN","AMGP","BBPL","LTJA", "PAJA", "ROPT", "PEFA","LTDU", "CORA", "RLHA", "RTLO", "GLGU", "KIEI","SNOW","PALO","SNGO", "CAGO", "ARFO","SACR", "ARHA","TUSW") 
# Dataframe Bylot
n <- 192
bylot <- data.frame(sp_code=rep(species_order, times = n/32),
                    ind_km2=sample(1:100, n, replace=TRUE),
                    site_code=factor(paste("BYLO")))

# Dataframe density
n <- 384
densityy <- data.frame(sp_code=rep(species_order, times = n/32),
                      ind_km2=sample(1:100, n, replace=TRUE),
                      site_code=rep(LETTERS, length.out = n))

I am making a plot like this one :

ggplot(densityy, aes(x=factor(sp_code, level = species_order), y=ind_km2, fill=sp_code, colour = sp_code)) +
  geom_violin(scale = "width", show.legend = FALSE) +
  stat_summary(fun=mean, geom="point", fill = "#FF8247", shape=23, size=3, show.legend = FALSE)+
  stat_summary(aes(x = sp_code, y = ind_km2), data = bylot, fun=mean, geom="point", fill = "#BBFFFF", shape=23, size=3, show.legend = FALSE)+
  scale_y_continuous(breaks = seq(0,400,25), expand = c(0, 0))+
  scale_fill_viridis_d(option = "D", begin = 0.2, direction = -1)+
  scale_color_viridis_d(option = "D", begin = 0.2, direction = -1)+
  labs(x = "Espèces", y = "Densité (individus/km2)")

I would like to add a legend that indicates that the orange points are the mean density for all sites, and the blue points are the mean for the site BYLO :

I've looked for solutions, and I've seen mostly aes(shape). Since I'm using 2 dataframes, I couldn't use aes(shape) for the bylot mean. Is there a way for me to add this legend without changing aes() ?

Here is something I tried, which didn't work :

ggplot(densityy, aes(x=factor(sp_code, level = species_order), y=ind_km2, fill=sp_code, colour = sp_code)) +
  geom_violin(scale = "width", show.legend = FALSE) +
  stat_summary(aes(shape = "Mean"), fun=mean, geom="point", fill = "#FF8247", shape=23, size=3, show.legend = FALSE)+
  stat_summary(aes(x = sp_code, y = ind_km2), data = Bylot, fun=mean, geom="point", fill = "#BBFFFF", shape=23, size=3, show.legend = FALSE)+
  scale_shape_manual("", values=c("Mean"="x"))+
  scale_y_continuous(breaks = seq(0,400,25), expand = c(0, 0))+
  scale_fill_viridis_d(option = "D", begin = 0.2, direction = -1)+
  scale_color_viridis_d(option = "D", begin = 0.2, direction = -1)+
  labs(x = "Espèces", y = "Densité (individus/km2)")+
  annotate("text",
           x = 1:length(table(density$sp_code)),
           y = aggregate(ind_km2 ~ sp_code, density, median)[ , 2],
           label = table(density$sp_code),
           col = "black",
           vjust = - 3)

# Message from the console
Warning messages:
3: No shared levels found between `names(values)` of the manual scale and the data's
shape values. 

Thank you!

Share Improve this question asked Mar 30 at 13:29 MagMag 335 bronze badges 1
  • 4 I believe this is an XY problem. Why not merge the two data frames, creating a column to indicate if the data is BYLOT or other, and then plot the merged data, using the new column to define the shape/colour of the points? That way, you're not making your life difficult by fighting against the expectations that ggplot has for your data. – Limey Commented Mar 30 at 13:58
Add a comment  | 

1 Answer 1

Reset to default 2

You can achieve your desired result by mapping on the fill aes instead of setting the colors as parameters. Additionally, as you are already mapping on the fill and color aes you could use the ggnewscale package which allows to have multiple scales and legends for the same aesthetic.

library(ggplot2)
library(ggnewscale)

ggplot(densityy, aes(
  x = factor(sp_code, level = species_order),
  y = ind_km2, fill = sp_code, colour = sp_code
)) +
  geom_violin(scale = "width", show.legend = FALSE) +
  scale_fill_viridis_d(option = "D", begin = 0.2, direction = -1) +
  scale_color_viridis_d(option = "D", begin = 0.2, direction = -1) +
  ggnewscale::new_scale_fill() +
  stat_summary(
    aes(fill = "other"),
    fun = mean, geom = "point", shape = 23, size = 3,
    show.legend = c(fill = TRUE, color = FALSE)
  ) +
  stat_summary(aes(x = sp_code, y = ind_km2, fill = "bylot"),
    data = bylot, fun = mean,
    geom = "point", shape = 23, size = 3,
    show.legend = c(fill = TRUE, color = FALSE)
  ) +
  scale_fill_manual(
    values = c(bylot = "#BBFFFF", other = "#FF8247"),
    labels = c(bylot = "Bylot", other = "Autres sites")
  ) +
  scale_y_continuous(breaks = seq(0, 400, 25), expand = c(0, 0)) +
  labs(x = "Espèces", y = "Densité (individus/km2)")

发布评论

评论列表(0)

  1. 暂无评论