0

My original question can be seen here (Automating a ggplot for each level in a group), but I thought I'd ask it differently to leave it open for many different ways to answer this question rather than a "how to" question to fix my poor attempt.

I'd like to make the process of creating a time series plot like the one below quicker/automatic (i.e., doesn't require the user to enter one species name at a time). Perhaps with an "if" loop. Something that tells R to cycle through all the unique common names in the data and print (or save to png) a plot using the code below (with the "common_name" of each species as the title of their respective plot). If there isn't enough data for a plot, R should print a message: "Not enough data for a plot", or something.

Here is a sample of my data (as you can see, there are over 100 species to make a plot of). This data sample shows only 3 species, 5 sites out of 47, and 3 years out of 16 years worth of data.

data <- structure(list(year = c(2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 
2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 
2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 
2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 2019L, 2019L, 2019L, 
2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 
2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 
2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 
2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 
2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 
2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 
2020L, 2020L, 2020L), season = structure(c(1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L), .Label = c("dry", "wet"), class = "factor"), 
    site = c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 
    5L, 5L, 5L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 
    5L, 5L, 5L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 
    5L, 5L, 5L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 
    5L, 5L, 5L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 
    5L, 5L, 5L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 
    5L, 5L, 5L), common_name = structure(c(68L, 92L, 105L, 68L, 
    92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L, 
    68L, 92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L, 68L, 92L, 
    105L, 68L, 92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L, 68L, 
    92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L, 
    68L, 92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L, 68L, 92L, 
    105L, 68L, 92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L, 68L, 
    92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L, 
    68L, 92L, 105L, 68L, 92L, 105L, 68L, 92L, 105L), .Label = c("Atlantic Mud Crab", 
    "Atlantic Needlefish", "Banded Blenny", "Banded Brittle Star", 
    "Banded Killifish", "Bandtail Puffer", "Barracuda spp", "Bigclaw Snapping Shrimp", 
    "Bigeye Mojarra", "Blenny spp", "Blue Crab", "Blue Crab spp", 
    "Blue Striped Grunt", "Bluethroat Pikeblenny", "Bonefish", 
    "Brittle Star spp", "Broadback Mud Crab", "Brown Shrimp", 
    "Bryozoan Shrimp", "Chain Pipefish", "Checkered Puffer", 
    "Chub spp", "Clown Goby", "Code Goby", "Combtooth Blenny spp", 
    "Crested Blenny", "Crested Goby", "Crossbanded Grass Shrimp", 
    "Cushion Sea Star", "Daggerblade Grass Shrimp", "Darter Goby", 
    "Dusky Pipefish", "Dwarf Seahorse", "Estuarine Snapping Shrimp", 
    "False Zostera Shrimp", "Fiddler Crab spp", "Flagfin Mojarra", 
    "Flatback Mud Crab", "Florida Blenny", "Florida Grass Shrimp", 
    "Florida Grassflat Crab", "Frillfin Goby", "Fringed Pipefish", 
    "Furrowed Mud Crab", "Giant Decorator crab", "Giant Tiger Prawn", 
    "Glass Shrimp", "Goby spp", "Goby spp (Ctenogobius spp)", 
    "Goldspotted Killifish", "Grass Shrimp (H obliquimanus)", 
    "Grass Shrimp (Leander spp)", "Grass Shrimp (Nikoides schmitti)", 
    "Grass Shrimp (P mundusnovus)", "Grass Shrimp (Palaemon spp)", 
    "Grass Shrimp (Palaemonidae spp)", "Grass Shrimp (Periclimenes spp)", 
    "Grass Shrimp (Thor spp)", "Grass Shrimp Spp", "Gray Snapper", 
    "Great Barracuda", "Grunt spp", "Gulf Flounder", "Gulf Killifish", 
    "Gulf Pipefish", "Gulf Toadfish", "Halfbeak spp", "Hardhead Silverside", 
    "Harlequin Brittle Star", "Harris Mud Crab", "Highfin Blenny", 
    "Hogchoker", "Horseshoe Crab", "Iridescent Shrimp", "Jack spp", 
    "Jewel Cichlid", "Killifish spp", "Least Puffer", "Lesser Blue Crab", 
    "Lined Seahorse", "Lined Sole", "Lobate Mud Crab", "Longnose Spider Crab", 
    "Longsnout Seahorse", "Longtail Grass Shrimp", "Mangrove Gambusia", 
    "Mangrove Rivulus", "Manning Grass Shrimp", "Marsh Killifish", 
    "Marsh Shrimp", "Mayan Cichlid", "Mojarra spp", "Mud Crab spp", 
    "Mullet spp", "Needlefish spp", "Oyster Mud Crab", "Pearl Blenny", 
    "Pinfish", "Pink Shrimp", "Pink Shrimp spp", "Pipefish spp", 
    "Porgy spp", "Puffer spp", "Pugnose Pipefish", "Rainwater Killifish", 
    "Red-Algae Shrimp", "Redear Sardine", "Redfin Needlefish", 
    "Roughneck Shrimp", "Sailfin Molly", "Sailor's Choice", "Saltmarsh Mud Crab", 
    "Sargassum Fish", "Sargassum Pipefish", "Sargassum Shrimp", 
    "Sargassum Swimming Crab", "Say Mud Crab", "Schoolmaster Snapper", 
    "Sea Star spp", "Seabream", "Seahorse spp", "Sheepshead", 
    "Sheepshead Minnow", "Silver Jenny", "Silverside spp", "Slender Mojarra", 
    "Slender Sargassum Shrimp", "Small Spine Sea Star", "Smooth Mud Crab", 
    "Snapper spp", "Snapping Shrimp (A viridari)", "Snapping Shrimp (A. angulosus)", 
    "Snapping Shrimp spp", "Southern Pink Shrimp", "Southern Puffer", 
    "Southern Sennet", "Spaghetti Eel", "Speckled Worm Eel", 
    "Spider Crab spp", "Sponge Spider Crab", "Spotted Pink Shrimp", 
    "Spotted Whiff", "Squat Grass Shrimp", "Stone Crab", "Striped Mullet", 
    "Swimming Crab spp", "Timicu", "Tomtate", "Tripletail", "White Grunt", 
    "White Mullet", "Whitespotted Filefish", "Yellowfin Mojarra", 
    "Zostera Shrimp"), class = "factor"), num = c(0L, 1L, 0L, 
    4L, 2L, 0L, 0L, 0L, 4L, 0L, 5L, 24L, 0L, 0L, 0L, 0L, 1L, 
    5L, 0L, 2L, 3L, 0L, 0L, 38L, 25L, 0L, 14L, 0L, 0L, 0L, 0L, 
    0L, 0L, 0L, 1L, 9L, 0L, 5L, 20L, 10L, 0L, 17L, 0L, 0L, 0L, 
    66L, 2L, 64L, 0L, 5L, 4L, 0L, 12L, 49L, 0L, 0L, 2L, 0L, 2L, 
    0L, 0L, 0L, 0L, 0L, 1L, 4L, 0L, 1L, 4L, 0L, 0L, 2L, 0L, 0L, 
    0L, 0L, 0L, 0L, 0L, 0L, 0L, 16L, 12L, 12L, 0L, 0L, 26L, 2L, 
    0L, 0L)), class = "data.frame", row.names = c(NA, -90L))

This is how I started making one plot for a single species at a time:

# Select species
rain <- subset(data,common_name == "Rainwater Killifish", 
                   select = c(year, 
                              season, 
                              site, 
                              common_name,
                              num))

cdata2 <- ddply(rain, c("year", "season"), summarise,
                N    = length(num),
                n_mean = mean(num),
                n_median = median(num),
                sd   = sd(num),
                se   = sd / sqrt(N))

cdata2$year_season <- paste(cdata2$year, "_", cdata2$season, sep = "")

cdata2 <-cdata2 %>% mutate(year=ifelse(season=="wet",year+0.75,year+0.25))

ggplot(cdata2, aes(x = year, y = n_mean, color = season)) +
  annotate(geom = "rect", xmin = 2010, xmax = 2010.5, ymin = -Inf, ymax = Inf,
           fill = "lightblue", colour = NA, alpha = 0.4) +
  annotate(geom = "rect", xmin = 2013.5, xmax = 2014, ymin = -Inf, ymax = Inf,
           fill = "lightgreen", colour = NA, alpha = 0.4) +
  annotate(geom = "rect", xmin = 2017.5, xmax = 2018, ymin = -Inf, ymax = Inf,
           fill = "#E0E0E0", colour = NA, alpha = 0.4) +
  annotate(geom = "rect", xmin = 2011.5, xmax = 2012, ymin = -Inf, ymax = Inf,
           fill = "pink", colour = NA, alpha = 0.4) +
  annotate(geom = "rect", xmin = 2015.5, xmax = 2016, ymin = -Inf, ymax = Inf,
           fill = "pink", colour = NA, alpha = 0.4) +
  annotate(geom = "rect", xmin = 2018.5, xmax = 2019, ymin = -Inf, ymax = Inf,
           fill = "orange", colour = NA, alpha = 0.4) +
  geom_errorbar(aes(ymin=n_mean-se, ymax=n_mean+se), 
                width=.2, 
                color = "black") +
  geom_point(color = "black", 
             shape = 21, 
             size = 3,
             aes(fill = season)) +
  scale_fill_manual(values=c("white", "#C0C0C0")) + 
  scale_x_continuous(breaks=c(2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2018,2019,2020)) +
  theme(panel.border = element_rect(fill = NA, color = "black"),
        panel.background = element_blank(), 
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank()) +
  labs(x="Year", y = "Mean count") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(axis.text.y = element_text(size = 10, face = "bold")) +
  theme(axis.text.x = element_text(size = 10, face = "bold")) +
  theme(axis.title = element_text(size = 14, face = "bold"))
Nate
  • 411
  • 2
  • 10

2 Answers2

1

Yes, this is what for-loops are for! You've already done all the hardwork. Now just paste the code you've written within the loop and replace common_name == "Rainwater Killifish" with common_name == species

allspecies <- unique(data$common_name)

for(species in allspecies){

    ## insert your code here
    
    ggsave(paste0(species, ".png"))
}
Skaqqs
  • 4,010
  • 1
  • 7
  • 21
  • Awesome! Thank you very much!! As a bonus, is there a way to also have R print the common name as the plot title for each respective species as well? – Nate Sep 07 '21 at 20:10
  • You're welcome! Yes, just add `ggtitle(species) +` to your `ggplot()` call. More info here: http://www.sthda.com/english/wiki/ggplot2-title-main-axis-and-legend-titles – Skaqqs Sep 08 '21 at 00:41
0

You can nest the data frame to species groups and then use the mutate and map combo to create a plot for each species group. Then you can use deframe to turn the name and value columns into a named list:

library(tidyverse)
plots <-
  data %>%
  nest(-common_name) %>%
  mutate(
    plt = data %>% map(possibly(~ {
      .x %>%
        ggplot(aes(year, num, color = site)) +
          geom_point()
    }, NA))
  ) %>%
  select(common_name, plt) %>%
  deframe()

plots[["Mojarra spp"]]
plots[["Rainwater Killifish"]]

The possibly function will return NA in case the plot fails. This is using the newer tidyverse packages as explained in https://r4ds.had.co.nz/many-models.html#nested-data

danlooo
  • 10,067
  • 2
  • 8
  • 22