DatCol
  1. Growth Curves
  • Overview
  • Tn-Seq Pipeline
  • Essential Genes Analysis
  • LDH Assays
  • Growth Curves
  • CFUs Competition
  • CFUs Complementation
  • CFUs Cocolonization with Cps
  • Alanine Concentration in AODM and HNO Mucus
  • Linear Mixed Model Workflow
  • R Session Info
  • References

Table of contents

  • Data Input
  • Main figure
  • Stats and Supplemental Figures
    • TSB
    • CDM20
    • CDM19 - ala

Growth Curves

# loading packages
library(tidyverse)
library(ggtext)
library(kableExtra)
library(pracma)
library(lme4)
library(lmerTest)
library(emmeans)

source("R/LMM_functions.R")

Data Input

All data inputs and outputs in this repo are relative to FOLDER_PATH. Please, save the provided data frames in RDS format in the dataframes folder.

# Load the BOX_PATH environment variable
FOLDER_PATH <- Sys.getenv("BOX_PATH") 

# Define subdirectories within the Box folder
dataframes_folder <- file.path(FOLDER_PATH, "dataframes")

outputs_folder <- file.path(FOLDER_PATH, "outputs/GC_OD600")
if (!dir.exists(outputs_folder)) {
  dir.create(outputs_folder, recursive = TRUE)
}
# Read the provided RDS file
dataframeID <- "DatCol"
GC_OD600_data <- readRDS(file.path(dataframes_folder, paste0(dataframeID, "_GC_OD600.rds")))
# Filter the data to include only time points up to 20 hours and exclude the Uncolonized control
GC_OD600_20h <- GC_OD600_data %>%
  filter(time <= 20) %>%
  filter(label != "NB")

# Summarize data by calculating mean and SEM for each time point and condition
GC_OD600_20h_summarized <- GC_OD600_20h %>%
  group_by(label, media, time, strain, vector) %>%
  summarise(
    mean_OD600 = mean(OD600),
    sem_OD600 = sd(OD600) / sqrt(n()),  
    .groups = "drop"
  ) 
GC_plot_function <- function(data, media_filter, ymin = 0, ymax = 1.8) {
  # Filter data based on date and media type
  filtered_data <- data %>%
    filter(media %in% media_filter) %>%
    droplevels()
  
  # Create the growth curve plot
  growth_curve_plot <- ggplot(filtered_data, aes(x = time, y = mean_OD600, color = strain, shape = vector)) +
    geom_line() +
    geom_point(size = 3) +
    
    scale_color_manual(
      values = c(
        Parent  = "#5F05AF",
        "dat::Tn"  = "#2FD612",
        Δalr1 = "#03C0FF"
      ),
      labels = c(
        Parent = "<b><span style='color:#5F05AF'>parent<span></b>",
        "dat::Tn" = "<b><span style='color:#2FD612'><i>dat</i>::Tn<span></b>",
        "Δalr1" = "<b><span style='color:#03C0FF'>Δ  <i>alr1</i><span></b>"
      )
    ) +
    scale_shape_manual(
      values = c(
        None     = 16,  # filled circle
        EV     = 1,   # empty circle
        "PpepV dat"  = 17,  # filled triangle
        "Pdat dat"   = 2,   # empty triangle
        alr1  = 15   # filled square
      ),
      labels = c(
        None = "None",
        EV = "EV",
        "PpepV dat" = "P<i><sub>pepV</sub>dat</i>",
        "Pdat dat" = "P<i><sub>dat</sub>dat</i>",
        alr1 = "P<i><sub>alr1</sub>alr1</i>"
      )
    ) +
    
    geom_errorbar(aes(ymin = mean_OD600 - sem_OD600, ymax = mean_OD600 + sem_OD600), width = 0.2) +
    
    labs(title = paste(paste(media_filter)),
         x = "Time (hours)",
         y = "Mean OD<sub>600</sub> ± SEM") +
    
    scale_y_continuous(limits = c(ymin, ymax), expand = c(0, 0)) +
    theme_bw() +
    theme(
      plot.title = element_text(vjust = -10, hjust = 0.05, size = 14, face = "bold"),
      legend.text = element_markdown(size = 12),
      legend.title = element_markdown(size = 12),
      panel.grid = element_blank(),
      text = element_text(size = 16, color = "black"),
      axis.text = element_markdown(color = "black"),
      axis.title.y = element_markdown()
    ) +
    
    guides(color = guide_legend(override.aes = list(linetype = 0, shape = NA), keywidth = 0))                               
  
  return(growth_curve_plot)
}

Main figure

figMain <- GC_plot_function(GC_OD600_20h_summarized %>% filter (vector != "None"), media_filter = c("CDM19 - ala"), ymin = 0, ymax = 1.5)
figMain

# Save the plot
ggsave(filename = file.path(outputs_folder, "figMain.png"), plot = figMain, width = 10, height = 6, dpi = 300)
saveRDS(figMain, file.path(outputs_folder, "figMain.rds"))

Stats and Supplemental Figures

The Area Under the Curve (AUC) was calculated for each growth curve to quantitatively compare the growth across different conditions. The AUC was computed using the trapezoidal rule from the pracma package.

# Calculate AUC for each growth curve using package pracma's trapz function, which performs numerical integration using the trapezoidal rule.
GC_AUC <- GC_OD600_20h %>%
  mutate(
    media = recode(media, "CDM19 - ala" = "CDM19Ala"),
    across(where(is.character), ~ str_replace_all(.x, " ", "")),
    across(where(is.factor), ~ factor(str_replace_all(as.character(.x), " ", "")))
  ) %>%
  group_by(label, media, strain, vector, date) %>%  
  summarise(AUC = trapz(time, OD600), .groups = "drop")

# Make a dataframe for each media condion for stats 
GC_AUC_CDM19Ala <- GC_AUC %>%
  filter(media == "CDM19Ala") %>%
  droplevels()

GC_AUC_TSB <- GC_AUC %>%
  filter(media == "TSB") %>%
  droplevels()

GC_AUC_CDM20 <- GC_AUC %>%
  filter(media == "CDM20") %>%
  droplevels()

Data for each media conditions were analyzed separately using a LMM to determine statistical significance with label as fixed effects and experimental date as a random effect. We analyzed pairwise contrasts between Parent with the EV or no vector compared to all other strains. Also we look at all contrast within the strains dat::Tn and Δalr1. We then adjusted the p-values for multiple comparisons using the Holm method and created a sign annotation column to indicate which comparisons are statistically significant with p<0.05.

TSB

# Run the mixed model with emmeans contrasts for the JE2 dataset.
stats_results_AUC_TSB <- mixed_model_with_emmeans(
  input_data = GC_AUC_TSB, response = "AUC", log_response = FALSE, adjust = "none",
  fixed_effects = c("label"), random_effects1 = "date"
)
stats_formated_AUC_TSB <- format_stats_results(stats_results_AUC_TSB)
stats_formated_AUC_TSB$main = stats_formated_AUC_TSB$main %>%
  filter(condition1 == "Parent" | condition2 == "Parent" | condition1 == "Parent_EV" | condition2 == "Parent_EV" 
         | (grepl("dat::Tn", condition1) & grepl("dat::Tn", condition2))
         | (grepl("Δalr1", condition1) & grepl("Δalr1", condition2))
  ) %>%
  mutate(
    p.value.adj = p.adjust(p.value, method = "holm"),
    sign = case_when(p.value.adj < 0.05 ~ "*", TRUE ~ "")
  )

# Save the stats results
saveRDS(stats_formated_AUC_TSB, file.path(outputs_folder, "stats_results_AUC_TSB.rds"))
# Render the results
cat("Model Formula:", format(stats_results_AUC_TSB$model_formula))
Model Formula: AUC ~ label + (1 | date)
kable(stats_formated_AUC_TSB$random_effects, caption = "Random Effects")
Random Effects
grp var1 var2 vcov sdcor proportion singular
date (Intercept) NA 4.8158862 2.1945127 85.91 FALSE
Residual NA NA 0.7895996 0.8885942 14.09 FALSE
kable(stats_formated_AUC_TSB$anova, caption = "ANOVA Table")
ANOVA Table
Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
label 50.14469 4.178724 12 24 5.292207 0.0002623
kable(stats_formated_AUC_TSB$main %>%
    mutate(
      p.value.anova = formatC(p.value.anova, format = "e", digits = 2),
      p.value = formatC(p.value, format = "e", digits = 2),
      p.value.adj = formatC(p.value.adj, format = "e", digits = 2)
    )  %>%
    select(effect_name, p.value.anova,  condition1, condition2, predicted_mean.1, predicted_mean.2, FC, log2FC, p.value, p.value.adj, sign),
  caption = "Main Contrasts with Predicted Log2FC"
)
Main Contrasts with Predicted Log2FC
effect_name p.value.anova condition1 condition2 predicted_mean.1 predicted_mean.2 FC log2FC p.value p.value.adj sign
label 2.62e-04 dat::Tn dat::Tn_alr1 23.55050 22.88650 1.0290127 0.0412608 3.69e-01 1.00e+00
label 2.62e-04 dat::Tn dat::Tn_EV 23.55050 23.43250 1.0050357 0.0072468 8.72e-01 1.00e+00
label 2.62e-04 dat::Tn dat::Tn_Pdatdat 23.55050 23.30633 1.0104764 0.0150356 7.39e-01 1.00e+00
label 2.62e-04 dat::Tn dat::Tn_PpepVdat 23.55050 22.28233 1.0569135 0.0798574 9.33e-02 1.00e+00
label 2.62e-04 dat::Tn Parent 23.55050 22.33933 1.0542168 0.0761716 1.08e-01 1.00e+00
label 2.62e-04 dat::Tn Parent_EV 23.55050 22.74933 1.0352171 0.0499334 2.80e-01 1.00e+00
label 2.62e-04 dat::Tn_alr1 dat::Tn_EV 22.88650 23.43250 0.9766990 -0.0340140 4.59e-01 1.00e+00
label 2.62e-04 dat::Tn_alr1 dat::Tn_Pdatdat 22.88650 23.30633 0.9819863 -0.0262252 5.68e-01 1.00e+00
label 2.62e-04 dat::Tn_alr1 dat::Tn_PpepVdat 22.88650 22.28233 1.0271142 0.0385965 4.13e-01 1.00e+00
label 2.62e-04 dat::Tn_alr1 Parent 22.88650 22.33933 1.0244934 0.0349107 4.58e-01 1.00e+00
label 2.62e-04 dat::Tn_alr1 Parent_EV 22.88650 22.74933 1.0060295 0.0086726 8.52e-01 1.00e+00
label 2.62e-04 dat::Tn_EV dat::Tn_Pdatdat 23.43250 23.30633 1.0054134 0.0077888 8.63e-01 1.00e+00
label 2.62e-04 dat::Tn_EV dat::Tn_PpepVdat 23.43250 22.28233 1.0516179 0.0726106 1.26e-01 1.00e+00
label 2.62e-04 dat::Tn_EV Parent 23.43250 22.33933 1.0489346 0.0689248 1.45e-01 1.00e+00
label 2.62e-04 dat::Tn_EV Parent_EV 23.43250 22.74933 1.0300302 0.0426866 3.56e-01 1.00e+00
label 2.62e-04 dat::Tn_Pdatdat dat::Tn_PpepVdat 23.30633 22.28233 1.0459557 0.0648217 1.71e-01 1.00e+00
label 2.62e-04 dat::Tn_Pdatdat Parent 23.30633 22.33933 1.0432869 0.0611359 1.95e-01 1.00e+00
label 2.62e-04 dat::Tn_Pdatdat Parent_EV 23.30633 22.74933 1.0244842 0.0348978 4.50e-01 1.00e+00
label 2.62e-04 dat::Tn_PpepVdat Parent 22.28233 22.33933 0.9974484 -0.0036858 9.38e-01 1.00e+00
label 2.62e-04 dat::Tn_PpepVdat Parent_EV 22.28233 22.74933 0.9794719 -0.0299240 5.26e-01 1.00e+00
label 2.62e-04 Parent Parent_alr1 22.33933 23.25467 0.9606387 -0.0579341 2.19e-01 1.00e+00
label 2.62e-04 Parent Parent_EV 22.33933 22.74933 0.9819775 -0.0262381 5.77e-01 1.00e+00
label 2.62e-04 Parent Parent_Pdatdat 22.33933 23.08533 0.9676851 -0.0473904 3.14e-01 1.00e+00
label 2.62e-04 Parent Parent_PpepVdat 22.33933 23.12267 0.9661227 -0.0497217 2.91e-01 1.00e+00
label 2.62e-04 Parent Δalr1 22.33933 19.68833 1.1346483 0.1822451 1.26e-03 4.40e-02 *
label 2.62e-04 Parent Δalr1_alr1 22.33933 21.95267 1.0176137 0.0251899 5.99e-01 1.00e+00
label 2.62e-04 Parent Δalr1_EV 22.33933 20.42250 1.0938589 0.1294266 1.43e-02 4.57e-01
label 2.62e-04 Parent_alr1 Parent_EV 23.25467 22.74933 1.0222131 0.0316960 4.93e-01 1.00e+00
label 2.62e-04 Parent_EV Parent_Pdatdat 22.74933 23.08533 0.9854453 -0.0211523 6.47e-01 1.00e+00
label 2.62e-04 Parent_EV Parent_PpepVdat 22.74933 23.12267 0.9838542 -0.0234835 6.12e-01 1.00e+00
label 2.62e-04 Parent_EV Δalr1 22.74933 19.68833 1.1554728 0.2084833 3.03e-04 1.09e-02 *
label 2.62e-04 Parent_EV Δalr1_alr1 22.74933 21.95267 1.0362902 0.0514281 2.83e-01 1.00e+00
label 2.62e-04 Parent_EV Δalr1_EV 22.74933 20.42250 1.1139348 0.1556648 3.78e-03 1.28e-01
label 2.62e-04 Δalr1 Δalr1_alr1 19.68833 21.95267 0.8968538 -0.1570552 4.65e-03 1.53e-01
label 2.62e-04 Δalr1 Δalr1_EV 19.68833 20.42250 0.9640511 -0.0528185 3.22e-01 1.00e+00
label 2.62e-04 Δalr1_alr1 Δalr1_EV 21.95267 20.42250 1.0749255 0.1042367 4.56e-02 1.00e+00
figSuppA <- GC_plot_function(GC_OD600_20h_summarized, media_filter = c("TSB"), ymin = 0, ymax = 1.5)
figSuppA

# Save the plot
ggsave(filename = file.path(outputs_folder, "figSuppA.png"), plot = figSuppA, width = 10, height = 6, dpi = 300)
saveRDS(figSuppA, file.path(outputs_folder, "figSuppA.rds"))

CDM20

# Run the mixed model with emmeans contrasts for the JE2 dataset.
stats_results_AUC_CDM20 <- mixed_model_with_emmeans(
  input_data = GC_AUC_CDM20, response = "AUC", log_response = FALSE, adjust = "none",
  fixed_effects = c("label"), random_effects1 = "date"
)
stats_formated_AUC_CDM20 <- format_stats_results(stats_results_AUC_CDM20)
stats_formated_AUC_CDM20$main = stats_formated_AUC_CDM20$main %>%
  filter(condition1 == "Parent" | condition2 == "Parent" | condition1 == "Parent_EV" | condition2 == "Parent_EV" 
         | (grepl("dat::Tn", condition1) & grepl("dat::Tn", condition2))
         | (grepl("Δalr1", condition1) & grepl("Δalr1", condition2))
  ) %>%
  mutate(
    p.value.adj = p.adjust(p.value, method = "holm"),
    sign = case_when(p.value.adj < 0.05 ~ "*", TRUE ~ "")
  )

# Save the stats results
saveRDS(stats_formated_AUC_CDM20, file.path(outputs_folder, "stats_results_AUC_CDM20.rds"))
# Render the results
cat("Model Formula:", format(stats_results_AUC_CDM20$model_formula))
Model Formula: AUC ~ label + (1 | date)
kable(stats_formated_AUC_CDM20$random_effects, caption = "Random Effects")
Random Effects
grp var1 var2 vcov sdcor proportion singular
date (Intercept) NA 0.1615282 0.4019057 45.43 FALSE
Residual NA NA 0.1940174 0.4404741 54.57 FALSE
kable(stats_formated_AUC_CDM20$anova, caption = "ANOVA Table")
ANOVA Table
Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
label 4.220684 0.3517237 12 24 1.812846 0.103848
kable(stats_formated_AUC_CDM20$main %>%
    mutate(
      p.value.anova = formatC(p.value.anova, format = "e", digits = 2),
      p.value = formatC(p.value, format = "e", digits = 2),
      p.value.adj = formatC(p.value.adj, format = "e", digits = 2)
    )  %>%
    select(effect_name, p.value.anova,  condition1, condition2, predicted_mean.1, predicted_mean.2, FC, log2FC, p.value, p.value.adj, sign),
  caption = "Main Contrasts with Predicted Log2FC"
)
Main Contrasts with Predicted Log2FC
effect_name p.value.anova condition1 condition2 predicted_mean.1 predicted_mean.2 FC log2FC p.value p.value.adj sign
label 1.04e-01 dat::Tn dat::Tn_alr1 17.96267 18.24067 0.9847593 -0.0221569 4.47e-01 1.00e+00
label 1.04e-01 dat::Tn dat::Tn_EV 17.96267 17.98150 0.9989526 -0.0015118 9.59e-01 1.00e+00
label 1.04e-01 dat::Tn dat::Tn_Pdatdat 17.96267 18.17517 0.9883082 -0.0169671 5.60e-01 1.00e+00
label 1.04e-01 dat::Tn dat::Tn_PpepVdat 17.96267 17.76317 1.0112311 0.0161127 5.84e-01 1.00e+00
label 1.04e-01 dat::Tn Parent 17.96267 18.09783 0.9925313 -0.0108154 7.10e-01 1.00e+00
label 1.04e-01 dat::Tn Parent_EV 17.96267 18.21117 0.9863545 -0.0198218 4.96e-01 1.00e+00
label 1.04e-01 dat::Tn_alr1 dat::Tn_EV 18.24067 17.98150 1.0144130 0.0206451 4.78e-01 1.00e+00
label 1.04e-01 dat::Tn_alr1 dat::Tn_Pdatdat 18.24067 18.17517 1.0036038 0.0051899 8.57e-01 1.00e+00
label 1.04e-01 dat::Tn_alr1 dat::Tn_PpepVdat 18.24067 17.76317 1.0268815 0.0382697 1.97e-01 1.00e+00
label 1.04e-01 dat::Tn_alr1 Parent 18.24067 18.09783 1.0078923 0.0113415 6.95e-01 1.00e+00
label 1.04e-01 dat::Tn_alr1 Parent_EV 18.24067 18.21117 1.0016199 0.0023351 9.35e-01 1.00e+00
label 1.04e-01 dat::Tn_EV dat::Tn_Pdatdat 17.98150 18.17517 0.9893444 -0.0154552 5.95e-01 1.00e+00
label 1.04e-01 dat::Tn_EV dat::Tn_PpepVdat 17.98150 17.76317 1.0122914 0.0176246 5.49e-01 1.00e+00
label 1.04e-01 dat::Tn_EV Parent 17.98150 18.09783 0.9935720 -0.0093036 7.49e-01 1.00e+00
label 1.04e-01 dat::Tn_EV Parent_EV 17.98150 18.21117 0.9873887 -0.0183100 5.29e-01 1.00e+00
label 1.04e-01 dat::Tn_Pdatdat dat::Tn_PpepVdat 18.17517 17.76317 1.0231941 0.0330798 2.63e-01 1.00e+00
label 1.04e-01 dat::Tn_Pdatdat Parent 18.17517 18.09783 1.0042731 0.0061516 8.32e-01 1.00e+00
label 1.04e-01 dat::Tn_Pdatdat Parent_EV 18.17517 18.21117 0.9980232 -0.0028548 9.21e-01 1.00e+00
label 1.04e-01 dat::Tn_PpepVdat Parent 17.76317 18.09783 0.9815079 -0.0269282 3.61e-01 1.00e+00
label 1.04e-01 dat::Tn_PpepVdat Parent_EV 17.76317 18.21117 0.9753997 -0.0359346 2.25e-01 1.00e+00
label 1.04e-01 Parent Parent_alr1 18.09783 17.97667 1.0067402 0.0096915 7.39e-01 1.00e+00
label 1.04e-01 Parent Parent_EV 18.09783 18.21117 0.9937767 -0.0090064 7.55e-01 1.00e+00
label 1.04e-01 Parent Parent_Pdatdat 18.09783 18.11183 0.9992270 -0.0011156 9.69e-01 1.00e+00
label 1.04e-01 Parent Parent_PpepVdat 18.09783 18.15983 0.9965859 -0.0049340 8.65e-01 1.00e+00
label 1.04e-01 Parent Δalr1 18.09783 17.07667 1.0597989 0.0837906 9.06e-03 3.08e-01
label 1.04e-01 Parent Δalr1_alr1 18.09783 18.17167 0.9959369 -0.0058738 8.39e-01 1.00e+00
label 1.04e-01 Parent Δalr1_EV 18.09783 17.43733 1.0378785 0.0536376 7.87e-02 1.00e+00
label 1.04e-01 Parent_alr1 Parent_EV 17.97667 18.21117 0.9871233 -0.0186978 5.21e-01 1.00e+00
label 1.04e-01 Parent_EV Parent_Pdatdat 18.21117 18.11183 1.0054844 0.0078908 7.85e-01 1.00e+00
label 1.04e-01 Parent_EV Parent_PpepVdat 18.21117 18.15983 1.0028268 0.0040724 8.88e-01 1.00e+00
label 1.04e-01 Parent_EV Δalr1 18.21117 17.07667 1.0664357 0.0927970 4.29e-03 1.54e-01
label 1.04e-01 Parent_EV Δalr1_alr1 18.21117 18.17167 1.0021737 0.0031326 9.13e-01 1.00e+00
label 1.04e-01 Parent_EV Δalr1_EV 18.21117 17.43733 1.0443780 0.0626439 4.17e-02 1.00e+00
label 1.04e-01 Δalr1 Δalr1_alr1 17.07667 18.17167 0.9397414 -0.0896644 5.58e-03 1.95e-01
label 1.04e-01 Δalr1 Δalr1_EV 17.07667 17.43733 0.9793164 -0.0301530 3.26e-01 1.00e+00
label 1.04e-01 Δalr1_alr1 Δalr1_EV 18.17167 17.43733 1.0421127 0.0595113 5.23e-02 1.00e+00
figSuppB <- GC_plot_function(GC_OD600_20h_summarized, media_filter = c("CDM20"), ymin = 0, ymax = 1.5)
figSuppB

# Save the plot
ggsave(filename = file.path(outputs_folder, "figSuppB.png"), plot = figSuppB, width = 10, height = 6, dpi = 300)
saveRDS(figSuppB, file.path(outputs_folder, "figSuppB.rds"))

CDM19 - ala

# Run the mixed model with emmeans contrasts for the JE2 dataset.
stats_results_AUC_CDM19Ala <- mixed_model_with_emmeans(
  input_data = GC_AUC_CDM19Ala, response = "AUC", log_response = FALSE, adjust = "none",
  fixed_effects = c("label"), random_effects1 = "date"
)
stats_formated_AUC_CDM19Ala <- format_stats_results(stats_results_AUC_CDM19Ala)
stats_formated_AUC_CDM19Ala$main = stats_formated_AUC_CDM19Ala$main %>%
  filter(condition1 == "Parent" | condition2 == "Parent" | condition1 == "Parent_EV" | condition2 == "Parent_EV" 
         | (grepl("dat::Tn", condition1) & grepl("dat::Tn", condition2))
         | (grepl("Δalr1", condition1) & grepl("Δalr1", condition2))
  ) %>%
  mutate(
    p.value.adj = p.adjust(p.value, method = "holm"),
    sign = case_when(p.value.adj < 0.05 ~ "*", TRUE ~ "")
  )

# Save the stats results
saveRDS(stats_formated_AUC_CDM19Ala, file.path(outputs_folder, "stats_results_AUC_CDM19Ala.rds"))
# Render the results
cat("Model Formula:", format(stats_results_AUC_CDM19Ala$model_formula))
Model Formula: AUC ~ label + (1 | date)
kable(stats_formated_AUC_CDM19Ala$random_effects, caption = "Random Effects")
Random Effects
grp var1 var2 vcov sdcor proportion singular
date (Intercept) NA 0.0078936 0.0888461 1.55 FALSE
Residual NA NA 0.5020733 0.7085713 98.45 FALSE
kable(stats_formated_AUC_CDM19Ala$anova, caption = "ANOVA Table")
ANOVA Table
Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
label 1297.176 108.098 12 24 215.3032 0
kable(stats_formated_AUC_CDM19Ala$main %>%
    mutate(
      p.value.anova = formatC(p.value.anova, format = "e", digits = 2),
      p.value = formatC(p.value, format = "e", digits = 2),
      p.value.adj = formatC(p.value.adj, format = "e", digits = 2)
    )  %>%
    select(effect_name, p.value.anova,  condition1, condition2, predicted_mean.1, predicted_mean.2, FC, log2FC, p.value, p.value.adj, sign),
  caption = "Main Contrasts with Predicted Log2FC"
)
Main Contrasts with Predicted Log2FC
effect_name p.value.anova condition1 condition2 predicted_mean.1 predicted_mean.2 FC log2FC p.value p.value.adj sign
label 2.19e-21 dat::Tn dat::Tn_alr1 2.729500 2.567000 1.0633035 0.0885534 7.81e-01 1.00e+00
label 2.19e-21 dat::Tn dat::Tn_EV 2.729500 2.612333 1.0448513 0.0632977 8.41e-01 1.00e+00
label 2.19e-21 dat::Tn dat::Tn_Pdatdat 2.729500 2.421833 1.1270387 0.1725371 6.00e-01 1.00e+00
label 2.19e-21 dat::Tn dat::Tn_PpepVdat 2.729500 16.661167 0.1638241 -2.6097808 2.55e-18 8.42e-17 *
label 2.19e-21 dat::Tn Parent 2.729500 12.746500 0.2141372 -2.2233926 4.59e-15 1.24e-13 *
label 2.19e-21 dat::Tn Parent_EV 2.729500 12.601167 0.2166069 -2.2068487 6.36e-15 1.59e-13 *
label 2.19e-21 dat::Tn_alr1 dat::Tn_EV 2.567000 2.612333 0.9826464 -0.0252557 9.38e-01 1.00e+00
label 2.19e-21 dat::Tn_alr1 dat::Tn_Pdatdat 2.567000 2.421833 1.0599408 0.0839837 8.04e-01 1.00e+00
label 2.19e-21 dat::Tn_alr1 dat::Tn_PpepVdat 2.567000 16.661167 0.1540708 -2.6983342 1.95e-18 6.83e-17 *
label 2.19e-21 dat::Tn_alr1 Parent 2.567000 12.746500 0.2013886 -2.3119460 3.21e-15 9.95e-14 *
label 2.19e-21 dat::Tn_alr1 Parent_EV 2.567000 12.601167 0.2037113 -2.2954021 4.42e-15 1.24e-13 *
label 2.19e-21 dat::Tn_EV dat::Tn_Pdatdat 2.612333 2.421833 1.0786594 0.1092394 7.45e-01 1.00e+00
label 2.19e-21 dat::Tn_EV dat::Tn_PpepVdat 2.612333 16.661167 0.1567917 -2.6730785 2.10e-18 7.15e-17 *
label 2.19e-21 dat::Tn_EV Parent 2.612333 12.746500 0.2049451 -2.2866903 3.55e-15 1.03e-13 *
label 2.19e-21 dat::Tn_EV Parent_EV 2.612333 12.601167 0.2073088 -2.2701464 4.89e-15 1.27e-13 *
label 2.19e-21 dat::Tn_Pdatdat dat::Tn_PpepVdat 2.421833 16.661167 0.1453580 -2.7823179 1.54e-18 5.54e-17 *
label 2.19e-21 dat::Tn_Pdatdat Parent 2.421833 12.746500 0.1899999 -2.3959297 2.34e-15 7.48e-14 *
label 2.19e-21 dat::Tn_Pdatdat Parent_EV 2.421833 12.601167 0.1921912 -2.3793858 3.21e-15 9.95e-14 *
label 2.19e-21 dat::Tn_PpepVdat Parent 16.661167 12.746500 1.3071170 0.3863883 5.34e-07 6.95e-06 *
label 2.19e-21 dat::Tn_PpepVdat Parent_EV 16.661167 12.601167 1.3221924 0.4029321 2.96e-07 4.14e-06 *
label 2.19e-21 Parent Parent_alr1 12.746500 13.254500 0.9616734 -0.0563811 3.89e-01 1.00e+00
label 2.19e-21 Parent Parent_EV 12.746500 12.601167 1.0115333 0.0165438 8.04e-01 1.00e+00
label 2.19e-21 Parent Parent_Pdatdat 12.746500 13.220833 0.9641223 -0.0527120 4.20e-01 1.00e+00
label 2.19e-21 Parent Parent_PpepVdat 12.746500 18.009333 0.7077719 -0.4986436 3.02e-09 5.74e-08 *
label 2.19e-21 Parent Δalr1 12.746500 3.197333 3.9866034 1.9951601 1.33e-14 3.19e-13 *
label 2.19e-21 Parent Δalr1_alr1 12.746500 7.745000 1.6457715 0.7187640 7.79e-09 1.40e-07 *
label 2.19e-21 Parent Δalr1_EV 12.746500 3.318667 3.8408497 1.9414255 1.76e-14 4.06e-13 *
label 2.19e-21 Parent_alr1 Parent_EV 13.254500 12.601167 1.0518471 0.0729249 2.70e-01 1.00e+00
label 2.19e-21 Parent_EV Parent_Pdatdat 12.601167 13.220833 0.9531295 -0.0692558 2.95e-01 1.00e+00
label 2.19e-21 Parent_EV Parent_PpepVdat 12.601167 18.009333 0.6997020 -0.5151875 1.80e-09 3.61e-08 *
label 2.19e-21 Parent_EV Δalr1 12.601167 3.197333 3.9411489 1.9786162 1.87e-14 4.10e-13 *
label 2.19e-21 Parent_EV Δalr1_alr1 12.601167 7.745000 1.6270067 0.7022202 1.33e-08 2.27e-07 *
label 2.19e-21 Parent_EV Δalr1_EV 12.601167 3.318667 3.7970571 1.9248817 2.48e-14 5.22e-13 *
label 2.19e-21 Δalr1 Δalr1_alr1 3.197333 7.745000 0.4128255 -1.2763961 4.30e-08 6.88e-07 *
label 2.19e-21 Δalr1 Δalr1_EV 3.197333 3.318667 0.9634391 -0.0537346 8.36e-01 1.00e+00
label 2.19e-21 Δalr1_alr1 Δalr1_EV 7.745000 3.318667 2.3337686 1.2226615 6.89e-08 1.03e-06 *
figSuppC <- GC_plot_function(GC_OD600_20h_summarized, media_filter = c("CDM19 - ala"), ymin = 0, ymax = 1.5)
figSuppC

# Save the plot
ggsave(filename = file.path(outputs_folder, "figSuppC.png"), plot = figSuppC, width = 10, height = 6, dpi = 300)
saveRDS(figSuppC, file.path(outputs_folder, "figSuppC.rds"))
LDH Assays
CFUs Competition
Source Code
---
format:
  html:
    suppress-bibliography: true
---

# Growth Curves {.unnumbered}

```{r}
# loading packages
library(tidyverse)
library(ggtext)
library(kableExtra)
library(pracma)
library(lme4)
library(lmerTest)
library(emmeans)

source("R/LMM_functions.R")
```

## Data Input 

All data inputs and outputs in this repo are relative to **FOLDER_PATH**. Please, save the provided data frames in RDS format in the `dataframes` folder.

```{r}
# Load the BOX_PATH environment variable
FOLDER_PATH <- Sys.getenv("BOX_PATH") 

# Define subdirectories within the Box folder
dataframes_folder <- file.path(FOLDER_PATH, "dataframes")

outputs_folder <- file.path(FOLDER_PATH, "outputs/GC_OD600")
if (!dir.exists(outputs_folder)) {
  dir.create(outputs_folder, recursive = TRUE)
}
```

```{r}
# Read the provided RDS file
dataframeID <- "DatCol"
GC_OD600_data <- readRDS(file.path(dataframes_folder, paste0(dataframeID, "_GC_OD600.rds")))
```

```{r}
# Filter the data to include only time points up to 20 hours and exclude the Uncolonized control
GC_OD600_20h <- GC_OD600_data %>%
  filter(time <= 20) %>%
  filter(label != "NB")

# Summarize data by calculating mean and SEM for each time point and condition
GC_OD600_20h_summarized <- GC_OD600_20h %>%
  group_by(label, media, time, strain, vector) %>%
  summarise(
    mean_OD600 = mean(OD600),
    sem_OD600 = sd(OD600) / sqrt(n()),  
    .groups = "drop"
  ) 
```

```{r}
GC_plot_function <- function(data, media_filter, ymin = 0, ymax = 1.8) {
  # Filter data based on date and media type
  filtered_data <- data %>%
    filter(media %in% media_filter) %>%
    droplevels()
  
  # Create the growth curve plot
  growth_curve_plot <- ggplot(filtered_data, aes(x = time, y = mean_OD600, color = strain, shape = vector)) +
    geom_line() +
    geom_point(size = 3) +
    
    scale_color_manual(
      values = c(
        Parent  = "#5F05AF",
        "dat::Tn"  = "#2FD612",
        Δalr1 = "#03C0FF"
      ),
      labels = c(
        Parent = "<b><span style='color:#5F05AF'>parent<span></b>",
        "dat::Tn" = "<b><span style='color:#2FD612'><i>dat</i>::Tn<span></b>",
        "Δalr1" = "<b><span style='color:#03C0FF'>Δ  <i>alr1</i><span></b>"
      )
    ) +
    scale_shape_manual(
      values = c(
        None     = 16,  # filled circle
        EV     = 1,   # empty circle
        "PpepV dat"  = 17,  # filled triangle
        "Pdat dat"   = 2,   # empty triangle
        alr1  = 15   # filled square
      ),
      labels = c(
        None = "None",
        EV = "EV",
        "PpepV dat" = "P<i><sub>pepV</sub>dat</i>",
        "Pdat dat" = "P<i><sub>dat</sub>dat</i>",
        alr1 = "P<i><sub>alr1</sub>alr1</i>"
      )
    ) +
    
    geom_errorbar(aes(ymin = mean_OD600 - sem_OD600, ymax = mean_OD600 + sem_OD600), width = 0.2) +
    
    labs(title = paste(paste(media_filter)),
         x = "Time (hours)",
         y = "Mean OD<sub>600</sub> ± SEM") +
    
    scale_y_continuous(limits = c(ymin, ymax), expand = c(0, 0)) +
    theme_bw() +
    theme(
      plot.title = element_text(vjust = -10, hjust = 0.05, size = 14, face = "bold"),
      legend.text = element_markdown(size = 12),
      legend.title = element_markdown(size = 12),
      panel.grid = element_blank(),
      text = element_text(size = 16, color = "black"),
      axis.text = element_markdown(color = "black"),
      axis.title.y = element_markdown()
    ) +
    
    guides(color = guide_legend(override.aes = list(linetype = 0, shape = NA), keywidth = 0))                               
  
  return(growth_curve_plot)
}
```

## Main figure 

```{r, fig.width=10, fig.height=6, fig.align="center"}
figMain <- GC_plot_function(GC_OD600_20h_summarized %>% filter (vector != "None"), media_filter = c("CDM19 - ala"), ymin = 0, ymax = 1.5)
figMain

# Save the plot
ggsave(filename = file.path(outputs_folder, "figMain.png"), plot = figMain, width = 10, height = 6, dpi = 300)
saveRDS(figMain, file.path(outputs_folder, "figMain.rds"))
```

## Stats and Supplemental Figures

The Area Under the Curve (AUC) was calculated for each growth curve to quantitatively compare the growth across different conditions. The AUC was computed using the trapezoidal rule from the `pracma` package.

```{r}
# Calculate AUC for each growth curve using package pracma's trapz function, which performs numerical integration using the trapezoidal rule.
GC_AUC <- GC_OD600_20h %>%
  mutate(
    media = recode(media, "CDM19 - ala" = "CDM19Ala"),
    across(where(is.character), ~ str_replace_all(.x, " ", "")),
    across(where(is.factor), ~ factor(str_replace_all(as.character(.x), " ", "")))
  ) %>%
  group_by(label, media, strain, vector, date) %>%  
  summarise(AUC = trapz(time, OD600), .groups = "drop")

# Make a dataframe for each media condion for stats 
GC_AUC_CDM19Ala <- GC_AUC %>%
  filter(media == "CDM19Ala") %>%
  droplevels()

GC_AUC_TSB <- GC_AUC %>%
  filter(media == "TSB") %>%
  droplevels()

GC_AUC_CDM20 <- GC_AUC %>%
  filter(media == "CDM20") %>%
  droplevels()
```

Data for each media conditions were analyzed separately using a LMM to determine statistical significance with `label` as fixed effects and experimental date as a random effect.
We analyzed pairwise contrasts between Parent with the EV or no vector compared to all other strains. Also we look at all contrast within the strains dat::Tn and Δalr1.
We then adjusted the p-values for multiple comparisons using the Holm method and created a `sign` annotation column to indicate which comparisons are statistically significant with p<0.05. 

### TSB

```{r}
# Run the mixed model with emmeans contrasts for the JE2 dataset.
stats_results_AUC_TSB <- mixed_model_with_emmeans(
  input_data = GC_AUC_TSB, response = "AUC", log_response = FALSE, adjust = "none",
  fixed_effects = c("label"), random_effects1 = "date"
)
stats_formated_AUC_TSB <- format_stats_results(stats_results_AUC_TSB)
stats_formated_AUC_TSB$main = stats_formated_AUC_TSB$main %>%
  filter(condition1 == "Parent" | condition2 == "Parent" | condition1 == "Parent_EV" | condition2 == "Parent_EV" 
         | (grepl("dat::Tn", condition1) & grepl("dat::Tn", condition2))
         | (grepl("Δalr1", condition1) & grepl("Δalr1", condition2))
  ) %>%
  mutate(
    p.value.adj = p.adjust(p.value, method = "holm"),
    sign = case_when(p.value.adj < 0.05 ~ "*", TRUE ~ "")
  )

# Save the stats results
saveRDS(stats_formated_AUC_TSB, file.path(outputs_folder, "stats_results_AUC_TSB.rds"))
```

```{r}
# Render the results
cat("Model Formula:", format(stats_results_AUC_TSB$model_formula))
kable(stats_formated_AUC_TSB$random_effects, caption = "Random Effects")
kable(stats_formated_AUC_TSB$anova, caption = "ANOVA Table")
kable(stats_formated_AUC_TSB$main %>%
    mutate(
      p.value.anova = formatC(p.value.anova, format = "e", digits = 2),
      p.value = formatC(p.value, format = "e", digits = 2),
      p.value.adj = formatC(p.value.adj, format = "e", digits = 2)
    )  %>%
    select(effect_name, p.value.anova,  condition1, condition2, predicted_mean.1, predicted_mean.2, FC, log2FC, p.value, p.value.adj, sign),
  caption = "Main Contrasts with Predicted Log2FC"
)
```

```{r, fig.width=10, fig.height=6, fig.align="center"}
figSuppA <- GC_plot_function(GC_OD600_20h_summarized, media_filter = c("TSB"), ymin = 0, ymax = 1.5)
figSuppA

# Save the plot
ggsave(filename = file.path(outputs_folder, "figSuppA.png"), plot = figSuppA, width = 10, height = 6, dpi = 300)
saveRDS(figSuppA, file.path(outputs_folder, "figSuppA.rds"))
```

### CDM20

```{r}
# Run the mixed model with emmeans contrasts for the JE2 dataset.
stats_results_AUC_CDM20 <- mixed_model_with_emmeans(
  input_data = GC_AUC_CDM20, response = "AUC", log_response = FALSE, adjust = "none",
  fixed_effects = c("label"), random_effects1 = "date"
)
stats_formated_AUC_CDM20 <- format_stats_results(stats_results_AUC_CDM20)
stats_formated_AUC_CDM20$main = stats_formated_AUC_CDM20$main %>%
  filter(condition1 == "Parent" | condition2 == "Parent" | condition1 == "Parent_EV" | condition2 == "Parent_EV" 
         | (grepl("dat::Tn", condition1) & grepl("dat::Tn", condition2))
         | (grepl("Δalr1", condition1) & grepl("Δalr1", condition2))
  ) %>%
  mutate(
    p.value.adj = p.adjust(p.value, method = "holm"),
    sign = case_when(p.value.adj < 0.05 ~ "*", TRUE ~ "")
  )

# Save the stats results
saveRDS(stats_formated_AUC_CDM20, file.path(outputs_folder, "stats_results_AUC_CDM20.rds"))
```

```{r}
# Render the results
cat("Model Formula:", format(stats_results_AUC_CDM20$model_formula))
kable(stats_formated_AUC_CDM20$random_effects, caption = "Random Effects")
kable(stats_formated_AUC_CDM20$anova, caption = "ANOVA Table")
kable(stats_formated_AUC_CDM20$main %>%
    mutate(
      p.value.anova = formatC(p.value.anova, format = "e", digits = 2),
      p.value = formatC(p.value, format = "e", digits = 2),
      p.value.adj = formatC(p.value.adj, format = "e", digits = 2)
    )  %>%
    select(effect_name, p.value.anova,  condition1, condition2, predicted_mean.1, predicted_mean.2, FC, log2FC, p.value, p.value.adj, sign),
  caption = "Main Contrasts with Predicted Log2FC"
)
```

```{r, fig.width=10, fig.height=6, fig.align="center"}
figSuppB <- GC_plot_function(GC_OD600_20h_summarized, media_filter = c("CDM20"), ymin = 0, ymax = 1.5)
figSuppB

# Save the plot
ggsave(filename = file.path(outputs_folder, "figSuppB.png"), plot = figSuppB, width = 10, height = 6, dpi = 300)
saveRDS(figSuppB, file.path(outputs_folder, "figSuppB.rds"))
```

### CDM19 - ala

```{r}
# Run the mixed model with emmeans contrasts for the JE2 dataset.
stats_results_AUC_CDM19Ala <- mixed_model_with_emmeans(
  input_data = GC_AUC_CDM19Ala, response = "AUC", log_response = FALSE, adjust = "none",
  fixed_effects = c("label"), random_effects1 = "date"
)
stats_formated_AUC_CDM19Ala <- format_stats_results(stats_results_AUC_CDM19Ala)
stats_formated_AUC_CDM19Ala$main = stats_formated_AUC_CDM19Ala$main %>%
  filter(condition1 == "Parent" | condition2 == "Parent" | condition1 == "Parent_EV" | condition2 == "Parent_EV" 
         | (grepl("dat::Tn", condition1) & grepl("dat::Tn", condition2))
         | (grepl("Δalr1", condition1) & grepl("Δalr1", condition2))
  ) %>%
  mutate(
    p.value.adj = p.adjust(p.value, method = "holm"),
    sign = case_when(p.value.adj < 0.05 ~ "*", TRUE ~ "")
  )

# Save the stats results
saveRDS(stats_formated_AUC_CDM19Ala, file.path(outputs_folder, "stats_results_AUC_CDM19Ala.rds"))
```

```{r}
# Render the results
cat("Model Formula:", format(stats_results_AUC_CDM19Ala$model_formula))
kable(stats_formated_AUC_CDM19Ala$random_effects, caption = "Random Effects")
kable(stats_formated_AUC_CDM19Ala$anova, caption = "ANOVA Table")
kable(stats_formated_AUC_CDM19Ala$main %>%
    mutate(
      p.value.anova = formatC(p.value.anova, format = "e", digits = 2),
      p.value = formatC(p.value, format = "e", digits = 2),
      p.value.adj = formatC(p.value.adj, format = "e", digits = 2)
    )  %>%
    select(effect_name, p.value.anova,  condition1, condition2, predicted_mean.1, predicted_mean.2, FC, log2FC, p.value, p.value.adj, sign),
  caption = "Main Contrasts with Predicted Log2FC"
)
```

```{r, fig.width=10, fig.height=6, fig.align="center"}
figSuppC <- GC_plot_function(GC_OD600_20h_summarized, media_filter = c("CDM19 - ala"), ymin = 0, ymax = 1.5)
figSuppC

# Save the plot
ggsave(filename = file.path(outputs_folder, "figSuppC.png"), plot = figSuppC, width = 10, height = 6, dpi = 300)
saveRDS(figSuppC, file.path(outputs_folder, "figSuppC.rds"))
```