Designing three-species subcommunities

Author

Shane Hogle

Published

May 24, 2025

Abstract
This notebook creates and lays out the steps for constructing the experimental species trios for this experiment. It is inspired by the method outlined in this paper: Full factorial construction of synthetic microbial communities, eLife 13:RP101906

1 Setup

1.1 Libraries

Show/hide code
library(here)
library(tidyverse)
library(ggplate)

1.2 Global variables

Show/hide code
data <- here::here("data", "communities", "experiment_design")

# make processed data directory if it doesn't exist
fs::dir_create(data)

1.3 Functions and vars

Species color vector

Show/hide code
my_colors <- c(
  "ANC_0403_10" = "#ffaaaa", "ANC_0403_70" = "#aa0000", "ANC_0403_80" = "#aa0000", "ANC_0403_90" = "#aa0000", 
  "ANC_1287_10" = "#ffeeaa", "ANC_1287_70" = "#d4aa00", "ANC_1287_80" = "#d4aa00", "ANC_1287_90" = "#d4aa00", 
  "ANC_1896_10" = "#ccffaa", "ANC_1896_70" = "#44aa00", "ANC_1896_80" = "#44aa00", "ANC_1896_90" = "#44aa00", 
  "ANC_1977_10" = "#aaccff", "ANC_1977_70" = "#0055d4", "ANC_1977_80" = "#0055d4", "ANC_1977_90" = "#0055d4",
  "EVO_0403_10" = "#ffaaee", "EVO_0403_70" = "#ff00cc", "EVO_0403_80" = "#ff00cc", "EVO_0403_90" = "#ff00cc", 
  "EVO_1287_10" = "#ffccaa", "EVO_1287_70" = "#ff6600", "EVO_1287_80" = "#ff6600", "EVO_1287_90" = "#ff6600", 
  "EVO_1896_10" = "#aaffee", "EVO_1896_70" = "#00ffcc", "EVO_1896_80" = "#00ffcc", "EVO_1896_90" = "#00ffcc", 
  "EVO_1977_10" = "#ccaaff", "EVO_1977_70" = "#7f2aff", "EVO_1977_80" = "#7f2aff", "EVO_1977_90" = "#7f2aff"
  )

For plotting plates

Show/hide code
plate_plot_steps <- function(df, well_colmun, val_column, step, colr_vec){
  ggplate::plate_plot(df,
  position = {{ well_colmun }},
  value = {{ val_column }},
  colour = colr_vec,
  plate_size = 96,
  plate_type = "round"
) + ggtitle(step)
}

2 Construct trio combinations

Get all possible trios while excluding combinations of ANC/EVO of the same species

Show/hide code
combos03 <- t(combn(c("ANC_0403", "EVO_0403", "ANC_1287", "EVO_1287", "ANC_1977", "EVO_1977", "ANC_1896", "EVO_1896"),  3))
combos03 <- tibble("a" = combos03[, 1], "b" = combos03[, 2], "c" = combos03[, 3])
combos03_filt <- combos03 %>%
  filter(str_extract(a, "\\d+") != str_extract(b, "\\d+")) %>% 
  filter(str_extract(a, "\\d+") != str_extract(c, "\\d+")) %>% 
  filter(str_extract(b, "\\d+") != str_extract(c, "\\d+"))

Make plate layouts for the 3-species combos

Show/hide code
combos03_filt_wells <- combos03_filt %>% 
  slice(rep(1:n(), each = 3)) %>%
  mutate(a = paste(a, c(80, 10, 10), sep = "_"),
         b = paste(b, c(10, 80, 10), sep = "_"),
         c = paste(c, c(10, 10, 80), sep = "_")) %>% 
  arrange(a, b, c) %>% 
  # this is a kludge to arrange the species in the most continuous blocks
  mutate(well = paste0(rep(c(LETTERS[c(c(1:8)[1:8%%2 == 1], 
                                         c(1:8)[1:8%%2 == 0])]), 
                             each = n()/8),
                         str_pad(rep(1:12, times = n()/12), 2, pad = "0")))

2.1 Format and save

Show/hide code
combos03_filt_wells %>%
  group_by(a, b, c) %>% 
  mutate(microcosm_id = cur_group_id()) %>% 
  ungroup() %>% 
  mutate(a_sp = paste0(str_split_i(a, "_", 2), stringr::str_extract(str_split_i(a, "_", 1), "^.{1}")),
         b_sp = paste0(str_split_i(b, "_", 2), stringr::str_extract(str_split_i(b, "_", 1), "^.{1}")),
         c_sp = paste0(str_split_i(c, "_", 2), stringr::str_extract(str_split_i(c, "_", 1), "^.{1}")),
         a_f = str_split_i(a, "_", 3),
         b_f = str_split_i(b, "_", 3),
         c_f = str_split_i(c, "_", 3)) %>% 
  arrange(well) %>% 
  dplyr::select(microcosm_id, well, a, a_sp, a_f, b, b_sp, b_f, c, c_sp, c_f) %>% 
  readr::write_tsv(here::here(data, "trios_sample_composition_wide.tsv"))
Show/hide code
combos03_filt_wells %>%
  group_by(a, b, c) %>% 
  mutate(microcosm_id = cur_group_id()) %>% 
  ungroup() %>% 
  dplyr::select(a:c, well, microcosm_id) %>% 
  tidyr::pivot_longer(c(-well, -microcosm_id)) %>% 
  tidyr::separate(value, c("evo_hist", "strainID", "target_f")) %>% 
  dplyr::mutate(evo_hist = stringr::str_to_lower(evo_hist),
                strainID = paste0("HAMBI_", strainID),
                target_f = as.numeric(target_f)/100,
                n_species = 3) %>% 
  dplyr::select(-name) %>% 
  dplyr::relocate(microcosm_id, n_species) %>% 
  readr::write_tsv(here::here(data, "trios_sample_composition_long.tsv"))

3 Pipetting

Proceed in the order of steps below to construct master plates used to inoculate different conditions

3.1 First pipetting step

Figure 1: Layout for the first pipetting step for constructing trios Different colors show different species/evolution combinations. The darkness of the color indicates whether a species/evolution combo is added at 80% (e.g., _80) or 10% (e.g., _10).

3.2 Second pipetting step

Figure 2: Layout for the second pipetting step for constructing trios Different colors show different species/evolution combinations. The darkness of the color indicates whether a species/evolution combo is added at 80% (e.g., _80) or 10% (e.g., _10).

3.3 Third pipetting step

Figure 3: Layout for the third pipetting step for constructing trios Different colors show different species/evolution combinations. The darkness of the color indicates whether a species/evolution combo is added at 80% (e.g., _80) or 10% (e.g., _10).