--- title: "A Portrait of Extinction: profiling the world's lost species with redlist" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{A Portrait of Extinction} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", message = FALSE, warning = FALSE, fig.align = "center" ) library(dplyr) library(ggplot2) library(scales) library(kableExtra) # A sober palette for a sober subject ember <- "#B23A2E" # loss, highlight charcoal <- "#2B2B2B" # text, structure ash <- "#9A9A9A" # secondary sand <- "#D9C7B8" # neutral fill slate <- "#4C6472" # cool accent theme_extinct <- function(base_size = 14) { theme_minimal(base_size = base_size) + theme( plot.title = element_text(face = "bold", colour = charcoal, size = rel(1.15)), plot.subtitle = element_text(colour = ash, size = rel(0.95), margin = margin(b = 10)), plot.caption = element_text(colour = ash, size = rel(0.75), hjust = 0), axis.title = element_text(colour = charcoal), axis.text = element_text(colour = charcoal), panel.grid.minor = element_blank(), panel.grid.major = element_line(colour = "grey90"), legend.position = "top", plot.margin = margin(12, 16, 12, 12) ) } theme_set(theme_extinct()) # Data ex <- readRDS("data/extinct_data_sis.rds") ``` ## Why look back? The IUCN Red List is best known for warning us about species that *might* disappear. This article turns the lens the other way, toward the species that already have. The category **Extinct (EX)** is reserved for taxa for which, in the words of the IUCN, there is no reasonable doubt that the last individual has died. It is the one label on the Red List that can never improve. Reading the Extinct list is not a morbid exercise. Each name is a data point in the larger story of how, when, and where biodiversity has been lost, and the patterns that emerge point straight at the places and the kinds of life that remain most fragile today. Everything below is built with two functions from the `redlist` package. You need an IUCN API key (the [Get Data article](https://stangandaho.github.io/redlist/articles/get_data.html#set-up-an-api-key) sets one up in a minute) ## Getting the data The Extinct catalogue is one function call away. The `code = "EX"` argument selects the Extinct category, and `page = NA` tells `redlist` to walk through every page of results automatically. ```{r query, eval = FALSE} library(redlist) library(dplyr) # 1. Retrieve every assessment currently classified as Extinct extinct <- rl_red_list_categories(code = "EX", page = NA) # Save saveRDS(extinct, "data/extinct_data.rds") ``` This returns one row per *assessment*. A species can carry several over the years (older versions and regional evaluations alongside its current global listing), so I keep the most recent listing of each taxon, identified by its **SIS id** (the IUCN's stable species identifier). ```{r ids, eval = FALSE} # 2. The species I want: the latest listing of each taxon ids <- unique(extinct$sis_taxon_id[extinct$latest == TRUE]) ``` The category endpoint does not carry taxonomy, so on its own it cannot tell a bird from a snail. To profile extinction across the tree of life I enrich each species with `rl_sis()`, which looks a taxon up by its SIS id and returns its full classification (kingdom, class, family and more). Because `rl_sis()` also returns every assessment of a taxon, I keep the most recent one. ```{r enrich, eval = FALSE} # 3. Enrich each species with its taxonomy sis_data <- tibble() for (i in seq_along(ids)) { # System sleep set to 1s to avoid the API call overload Sys.sleep(1) # Show simple progress status cat(paste0("\f", i, "/", length(ids), " (", round(i * 100 / length(ids), 2), "%)", "\r")) # Red List taxa by SIS ID one <- rl_sis(ids[i]) %>% slice_max(as.numeric(year_published), n = 1, with_ties = FALSE) # Bind every single request to one data set in sis_data sis_data <- bind_rows(sis_data, one) } # Save saveRDS(sis_data, "data/extinct_data_sis.rds") ``` That loop makes one request per species, so it takes approximately `r round(nrow(ex)/60, 2)` minutes. ```{r load-data-real, include = FALSE} extinct_raw <- readRDS("data/extinct_data.rds") ex <- readRDS("data/extinct_data_sis.rds") ``` ```{r tidy} ex <- ex %>% mutate( year_published = as.integer(year_published), genus = taxon_genus_name, # Fold taxonomic class into reader-friendly major groups group = case_when( taxon_class_name == "MAMMALIA" ~ "Mammals", taxon_class_name == "AVES" ~ "Birds", taxon_class_name == "AMPHIBIA" ~ "Amphibians", taxon_class_name == "REPTILIA" ~ "Reptiles", taxon_class_name %in% c("ACTINOPTERYGII", "CHONDRICHTHYES") ~ "Fishes", taxon_class_name == "GASTROPODA" ~ "Snails & slugs", taxon_class_name == "BIVALVIA" ~ "Mussels & clams", taxon_class_name == "INSECTA" ~ "Insects", taxon_class_name %in% c("ARACHNIDA", "MALACOSTRACA", "DIPLOPODA", "MAXILLOPODA", "HEXANAUPLIA", "OSTRACODA") ~ "Other arthropods", taxon_class_name %in% c("MAGNOLIOPSIDA", "LILIOPSIDA") ~ "Flowering plants", taxon_class_name %in% c("BRYOPSIDA", "POLYPODIOPSIDA") ~ "Ferns & mosses", taxon_kingdom_name == "PLANTAE" ~ "Other plants", TRUE ~ "Other invertebrates" ), kingdom = tools::toTitleCase(tolower(taxon_kingdom_name)), branch = case_when( group %in% c("Mammals", "Birds", "Amphibians", "Reptiles", "Fishes") ~ "Vertebrates", taxon_kingdom_name == "PLANTAE" ~ "Plants", TRUE ~ "Invertebrates" ) ) glimpse(ex[, c("taxon_scientific_name", "kingdom", "taxon_class_name", "group", "year_published")]) ``` ```{r headline-numbers} n_records <- nrow(extinct_raw) n_species <- nrow(ex) n_genera <- n_distinct(ex$genus) n_family <- n_distinct(ex$taxon_family_name) n_animals <- sum(ex$kingdom == "Animalia") n_plants <- sum(ex$kingdom == "Plantae") n_mollusc <- sum(ex$group %in% c("Snails & slugs", "Mussels & clams")) yr_min <- min(ex$year_published) yr_max <- max(ex$year_published) since_2020 <- sum(ex$year_published >= 2020) ``` Starting from **`r comma(n_records)` assessment records**, keeping the latest listing of each taxon and enriching it leaves **`r n_species` species** confirmed Extinct, spanning **`r n_genera` genera** in **`r n_family` families**. Their current listings were published between **`r yr_min`** and **`r yr_max`**. ## The scale of loss at a glance ```{r overview-table} dplyr::tibble( Measure = c( "Assessment records returned", "Distinct Extinct species (latest listing)", "Genera represented", "Families represented", "Animals / Plants", "Species listed since 2020", "Publication span of current listings" ), Value = c( comma(n_records), comma(n_species), comma(n_genera), comma(n_family), paste0(comma(n_animals), " / ", comma(n_plants)), comma(since_2020), paste0(yr_min, " to ", yr_max) ) ) %>% kable(align = c("l", "r"), caption = "The Extinct record in numbers") %>% kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) ``` Every one of these `r n_species` species is a **confirmed** extinction, gone everywhere: all but two carry a global-scope listing. Confirmed loss is a high bar, so this catalogue is best read as a conservative floor, not a full accounting of what has vanished. ## Extinction across the tree of life Extinction does not fall evenly across life. Split by kingdom, animals outnumber plants by more than five to one (`r comma(n_animals)` against `r comma(n_plants)`), but the broad animal-versus-plant contrast hides the real structure. The chart below sorts the catalogue into major groups. ```{r groups, fig.width = 8, fig.height = 5.5, fig.alt = "Number of Extinct species by major taxonomic group"} group_tbl <- ex %>% count(group, branch, name = "species") %>% mutate(group = reorder(group, species)) ggplot(group_tbl, aes(species, group, colour = branch)) + geom_segment(aes(x = 0, xend = species, y = group, yend = group), colour = ash, linewidth = 0.6) + geom_point(size = 4) + geom_text(aes(label = species), colour = charcoal, size = 2.9, hjust = -0.6) + scale_colour_manual(values = c(Vertebrates = slate, Invertebrates = ember, Plants = "#4E7A51"), name = NULL) + scale_x_continuous(expand = expansion(mult = c(0, 0.10))) + labs( title = "Which groups have lost the most species", x = "Extinct species", y = NULL ) + theme_extinct() + theme(panel.grid.major.y = element_blank()) ``` The single largest share is not the charismatic vertebrates but the **molluscs**: snails, slugs, mussels and clams together account for **`r comma(n_mollusc)` species**, about `r round(100 * n_mollusc / n_species)`% of the entire catalogue. Land snails alone (`r sum(ex$group == "Snails & slugs")`) exceed every other group. Birds (`r sum(ex$group == "Birds")`) and flowering plants (`r sum(ex$group == "Flowering plants")`) come next, with mammals and fishes tied close behind. The pattern is a familiar one to conservation biologists: small, overlooked, narrow-range invertebrates dominate the toll, even though mammals and birds dominate public attention. ```{r group-table} ex %>% group_by(Group = group) %>% summarise( Species = n(), `Since 2020` = sum(year_published >= 2020), Example = first(sort(taxon_scientific_name)), .groups = "drop" ) %>% arrange(desc(Species)) %>% mutate(Share = percent(Species / sum(Species), accuracy = 0.1)) %>% select(Group, Species, Share, `Since 2020`, `Example species` = Example) %>% kable(align = c("l", "r", "r", "r", "l"), caption = "Confirmed extinctions by major group") %>% kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) ``` The `Since 2020` column reveals how uneven the *documentation* of loss is. Almost every extinct **bird** on the list (`r sum(ex$group == "Birds" & ex$year_published >= 2020)` of `r sum(ex$group == "Birds")`) was formalised in the 2020s, the fruit of a recent systematic review, while the **mussels and clams** were catalogued in an earlier wave and none appear since 2020. These are pulses of assessment effort, not sudden changes in the rate of extinction itself. ## The tempo of documented loss The chart below counts species by the year their current Extinct listing was published. It is worth stressing that this is the year of assessment, not the year the animal or plant actually died. Many species here were lost decades or centuries ago and formalised on the Red List much later. ```{r timeline, fig.alt = "Number of species newly listed as Extinct per publication year", fig.width=8, fig.height=4} per_year <- ex %>% count(year_published, name = "species") ggplot(per_year, aes(year_published, species)) + geom_area(fill = ember, alpha = 0.12) + geom_line(colour = ember, linewidth = 0.9) + geom_point(colour = ember, size = 1.6) + scale_x_continuous(breaks = pretty_breaks(8)) + labs( title = "When the world's Extinct species entered the Red List", subtitle = "Species counted by the publication year of their current Extinct assessment", x = "Publication year", y = "Species listed" ) + theme_extinct() ``` The record is uneven, with clear pulses of activity that track waves of systematic reassessment rather than sudden bursts of extinction. The signal is unmistakably recent all the same: **`r since_2020` species**, roughly `r round(100 * since_2020 / n_species)`% of the whole catalogue, received their current Extinct listing in `r 2020` or later. The accumulation is easier to feel as a running total. ```{r cumulative, fig.alt = "Cumulative number of species listed as Extinct over time", fig.width=8, fig.height=4} cumulative <- per_year %>% arrange(year_published) %>% mutate(cumulative = cumsum(species)) ggplot(cumulative, aes(year_published, cumulative)) + geom_area(fill = charcoal, alpha = 0.08) + geom_line(colour = charcoal, linewidth = 1) + scale_x_continuous(breaks = pretty_breaks(8)) + scale_y_continuous(labels = comma) + labs( title = "The rising tally of confirmed extinctions on the Red List", subtitle = "Cumulative species carrying a current Extinct listing", x = "Publication year", y = "Cumulative species" ) + theme_extinct() ``` Splitting each decade between animals and plants shows the same rhythm playing out in both kingdoms. ```{r decade-table} ex %>% mutate(decade = paste0(floor(year_published / 10) * 10, "s")) %>% group_by(Decade = decade) %>% summarise( Animals = sum(kingdom == "Animalia"), Plants = sum(kingdom == "Plantae"), Total = n(), .groups = "drop" ) %>% kable(align = c("l", "r", "r", "r"), caption = "Extinct listings by publication decade and kingdom") %>% kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) ``` ## The hardest-hit genera Zooming from groups down to genera sharpens the picture. A handful of genera recur again and again, and each is a well known tragedy of modern conservation. ```{r genera, fig.width = 8, fig.height = 5.5, fig.alt = "Genera with the most Extinct species"} top_genera <- ex %>% count(genus, group, name = "species") %>% slice_max(species, n = 12) %>% mutate(genus = reorder(genus, species)) ggplot(top_genera, aes(species, genus, fill = group)) + geom_col(width = 0.7) + geom_text(aes(label = species), hjust = -0.3, size = 3, colour = charcoal) + scale_x_continuous(expand = expansion(mult = c(0, 0.10))) + scale_fill_manual(values = c( "Snails & slugs" = ember, "Fishes" = slate, "Amphibians" = "#7A5C99", "Flowering plants" = "#4E7A51", "Mussels & clams" = "#C9862B" ), name = NULL) + labs( title = "The genera that lost the most species", x = "Extinct species", y = NULL ) + theme_extinct() + theme(panel.grid.major.y = element_blank()) ``` The leading genera map the modern extinction crisis onto real places. *Partula*, *Achatinella*, *Amastra* and *Carelia* are Pacific and Hawaiian land snails, decimated by habitat clearance and by introduced predatory snails. *Barbodes* is a flock of small cyprinid fishes once endemic to a single Philippine lake, lost after invasive species arrived. *Pseudophilautus* gathers the shrub frogs of Sri Lanka, many known only from old museum specimens. *Cyanea* are Hawaiian lobelioid plants, *Coregonus* the whitefishes of European lakes, and *Epioblasma* and *Pleurobema* are freshwater mussels of North American rivers dammed and dredged across the twentieth century. Two themes bind them: **islands and fresh water**. Isolated island biotas and confined river systems concentrate narrow-range endemics that vanish the moment their single home is disturbed. ```{r genera-table} ex %>% count(genus, Group = group, name = "Species") %>% slice_max(Species, n = 10, with_ties = FALSE) %>% left_join( ex %>% group_by(genus) %>% summarise(Example = first(sort(taxon_scientific_name)), .groups = "drop"), by = "genus" ) %>% rename(Genus = genus) %>% select(Genus, Group, Species, `Example species` = Example) %>% kable(align = c("l", "l", "r", "l"), caption = "The ten hardest-hit genera in the Extinct catalogue") %>% kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) ``` The long tail matters as much as the peaks. Of the `r n_genera` genera in the catalogue, `r sum(table(ex$genus) == 1)` contain a single Extinct species. Loss is overwhelmingly a story of scattered, one-off disappearances rather than a few collapsing dynasties, which makes it that much harder to see and to prevent. ## The most recent names Extinction is not a closed chapter of history. The species below received their current Extinct listing most recently, a reminder that the catalogue is still growing in our own time. ```{r recent-table} ex %>% filter(year_published >= 2024) %>% arrange(desc(year_published), taxon_scientific_name) %>% transmute( Species = paste0("*", taxon_scientific_name, "*"), `Common name` = ifelse(is.na(taxon_common_names_name), " _ ", taxon_common_names_name), Group = group, Listed = year_published ) %>% head(15) %>% kable(align = c("l", "l", "l", "r"), caption = "A selection of the most recently published Extinct listings") %>% kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) ```