HNOBac Manuscript
  1. Methods
  2. CFUs 48h
  • Introduction
  • Methods
    • RNASeq
    • Cell Counts & TEER
    • CFUs 48h
    • CFUs Epithelial Lines
    • LDH 48h
    • LDH Epithelial Lines
    • Cytokines
    • HEK-Blue
  • R Session Info

Table of contents

  • Data Input and Selection
    • File Paths
    • Data clean-up
    • Saving files
  • Stats and Plots
    • File Paths
    • Analysis for each Temp
    • Analysis 34 vs. 37
    • Merged Summary Files

CFUs 48h

library(tidyverse)
library(ggpubr)
library(ggtext)
library(ggnewscale)
library(scales)
library(lme4)
library(afex)
library(emmeans)
library(gridExtra)

Data Input and Selection

File Paths

# Folder paths
input_path <- "data/input_data/CFUs/"
metadata_path <- "data/metadata/CFUs"

# Create subfolders for output files
dataframes_folder <- "data/dataframes"
if (!file.exists("data/dataframes")) {
  dir.create("data/dataframes", recursive = TRUE)
}

# Load data and metadata
input_data <- read_csv(file.path(input_path, "HNOBac_CFUs_0130_2024.csv")) %>% mutate_if(is.character, factor)
input_data$Time <- as.factor(input_data$Time)
Bac_order <- read_csv(file.path(metadata_path, "Order_Bacteria_CFUs.csv"))

Data clean-up

# Setting zero values to the limit of detection
CFU_data <- input_data %>%
  mutate(
    LOD = CFUs == 0,
    NewCFU = ifelse(LOD, 3.75, CFUs)
  )

# Factor Ordering and Styling
CFU_data <- CFU_data %>%
  mutate(Combined = interaction(bacteria, Time)) %>% 
  mutate(Temp_label = factor(Temp, labels = c("<b><br><span style='color:#5b5b5b;'>34 °C</span></b>",
                                              "<b><br><span style='color:red3;'>37 °C</span></b>")))  
CFU_data <- merge(CFU_data, Bac_order, by = "Combined") 
CFU_data$bacteria_label <- factor(CFU_data$bacteria_label, levels = Bac_order$bacteria_label)
CFU_data$Line <- fct_recode(CFU_data$Line, "HNO918" = "A", "HNO204" = "B", "HNO919" = "C") 

# Calculate log2 fold change between final time points vs. time 0
CFU_FC <- CFU_data %>%
  mutate(Collection_Label = ifelse(Time == 0, "initial", "final")) %>%
  group_by(Date, Line, bacteria) %>%
  reframe(
    Combined = Combined[Collection_Label == "final"],
    Date = Date[Collection_Label == "final"],
    Line = Line[Collection_Label == "final"],
    Temp = Temp[Collection_Label == "final"],
    Time = Time[Collection_Label == "final"],
    bacteria = bacteria[Collection_Label == "final"],
    bacteria_label = bacteria_label[Collection_Label == "final"],
    Temp_label = Temp_label[Collection_Label == "final"],
    log2FC = log2(NewCFU[Collection_Label == "final"]/NewCFU[Collection_Label == "initial"])) %>%
    droplevels() %>%
  mutate(Temp = factor(Temp))

Saving files

# Save data frame as CSV files in the output folder
write_csv(CFU_data, file.path(dataframes_folder, "CFU_values.csv"))
write_csv(CFU_FC, file.path(dataframes_folder, "CFU_FC.csv"))

# Save data frame as R objects in the output folder
saveRDS(CFU_data, file.path(dataframes_folder, "CFU_values.rds"))
saveRDS(CFU_FC, file.path(dataframes_folder, "CFU_FC.rds"))

# Cleaning-up all objects from the environment
rm(list = ls())

# Use this to read the final objects
CFU_data <- readRDS("data/dataframes/CFU_values.rds")
CFU_FC <- readRDS("data/dataframes/CFU_FC.rds")

Stats and Plots

File Paths

# Folder paths
dataframes_path <- "data/dataframes"
metadata_path <- "data/metadata/CFUs"

# Create subfolders for output files
figures_folder <- "data/outputs/CFUs/figures"
if (!file.exists("data/outputs/CFUs/figures")) {
  dir.create("data/outputs/CFUs/figures", recursive = TRUE)
}
stats_folder <- "data/outputs/CFUs/stats"
if (!file.exists("data/outputs/CFUs/stats")) {
  dir.create("data/outputs/CFUs/stats", recursive = TRUE)
}

# Load data and metadata
CFU_data <- readRDS("data/dataframes/CFU_values.rds")
CFU_FC <- readRDS("data/dataframes/CFU_FC.rds")
Bac_order <- read_csv(file.path(metadata_path, "Order_Bacteria_CFUs.csv"))

Analysis for each Temp

# Function to analyze each temp condition
analysis_function <- function(data, each_temp, cutoff_pvalue, cutoff_FC, color_error) {
  
  # Subset the data to the selected temp
  data_subset <- data %>%
    filter(Temp == each_temp) 
  
  data_stats <- data_subset 
  
  # Mixed-effects model with random effects
  model <- lmer(log(NewCFU) ~ bacteria * Time 
                + (1|Line) + (1|Line:Date), 
                data = data_stats)
  #Anova
  anova <- anova(model)
  anova_df <- as.data.frame(anova) %>%
    mutate(sign = case_when(
      `Pr(>F)` < cutoff_pvalue ~ "*",
      TRUE ~ "")) %>%
    mutate_if(is.numeric, ~ format(., digits = 2, scientific = TRUE))
  
  # Calculate Individual contrasts
  emmeans_model <- emmeans(model, ~ bacteria * Time)
  emmeans_time <- pairs(emmeans_model, simple = "Time", adjust = "none")    
  
  # Extract random effects and convert to dataframe (if not singular)
  random_effects_df <- as.data.frame(VarCorr(model)) %>%
    mutate(proportion = round(100 * (vcov / sum(vcov)), 2))
  
  # Adds predictions based on fixed effects, averaged over random effects. It gives a population estimate
  data_stats <- cbind(data_stats, predval = predict(model,re.form = NA, se.fit = TRUE))
  data_summary_df <- data_stats %>%
    group_by(Combined, bacteria_label) %>%
    summarize(mean.real = mean(NewCFU),
              mean.predval = mean(predval.fit), 
              mean.predval.se = mean(predval.se.fit)) %>%
    mutate(exp.mean.predval = exp(mean.predval),
           max = exp(mean.predval + 2*mean.predval.se),
           min = exp(mean.predval - 2*mean.predval.se))
  
  # Extract time contrasts from emmeans_model, convert to dataframe and adjust pvalues. Remove the 0-48h contrasts
  contrasts_time_df <- as.data.frame(summary(emmeans_time)) %>%
    mutate(Temp = each_temp) %>%
    filter(contrast != "Time0 - Time48") %>%
    mutate(p.adj.holm = p.adjust(p.value, method = "holm")) %>%
    mutate(sign = case_when(
      p.adj.holm < cutoff_pvalue ~ "*",
      TRUE ~ ""))
  
  # Edits to the contrast dataframe to include pvalue brackets in plot
  contrasts_time_df <- contrasts_time_df %>%
    separate(contrast, into = c("Time1", "Time2"), sep = " - ") %>%
    mutate(Time1 = sub("Time", "", Time1),
           Time2 = sub("Time", "", Time2)) %>%
    mutate(condition1 = paste(bacteria, Time1, sep = "."),
           condition2 = paste(bacteria, Time2, sep = "."))
  
  contrasts_time_df$group1 <- Bac_order$bacteria_label[match(contrasts_time_df$condition1, Bac_order$Combined)]
  contrasts_time_df$group2 <- Bac_order$bacteria_label[match(contrasts_time_df$condition2, Bac_order$Combined)]
  
  # Calculate fold-change values for each contrast
  contrasts_time_df <- contrasts_time_df %>%
    ungroup() %>%
    left_join(select(data_summary_df, Combined, exp.mean.predval), by = join_by(condition1 == Combined)) %>%
    left_join(select(data_summary_df, Combined, exp.mean.predval), by = join_by(condition2 == Combined), suffix = c(".1", ".2")) %>%
    mutate(FC = exp.mean.predval.1 / exp.mean.predval.2,
           FC = if_else(FC < 1, -1 / FC, FC),
           highlighted = case_when(
             FC <= -cutoff_FC ~ "+",
             FC >= cutoff_FC ~ "-",
             TRUE ~ "")) 
  
  # Select p values to plot and define their location
  contrast_sign <- contrasts_time_df %>%
    filter(sign != "" & highlighted != "") %>%
    mutate(p.adj.holm = format(p.adj.holm, digits = 2, scientific = TRUE))
  
  location <- log10(max(data_subset$NewCFU, na.rm = TRUE)) * 1.1
  
  # Standard Boxplot
  plot_1 <- ggplot() +
    geom_boxplot(data = data_subset, aes(x = bacteria_label, y = NewCFU, fill = bacteria_label)) + 
    
    geom_jitter(data = data_subset, aes(x = bacteria_label, y = NewCFU, shape = Line), 
                fill = "grey50", color = "grey30", size = 2, width = 0.05, stroke = 0.75) +
    
    scale_fill_manual(values = c("#800080","#800080","#800080",
                                 "#1E90FF","#1E90FF","#1E90FF", 
                                 "#927ed1","#927ed1","#927ed1")) +
    
    scale_shape_manual(values = c(21, 22, 24)) +
    
    scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x),
                  labels = trans_format("log10", math_format(10^.x))) +
    labs(x = "Hours post-inoculation",
         y = paste0("CFUs/HNO at ", each_temp, " °C")) +
    theme_bw() +
    theme(panel.grid = element_blank(),
          legend.position = "none",
          text = element_text(size = 20), 
          axis.text.x = element_markdown(), 
          axis.text.y = element_text(color = "black"))
  
  # Plot with predicted means and standard errors of the estimates
  plot_2 <- ggplot() +
    geom_point(data = data_subset, 
                aes(x = bacteria_label, y = NewCFU, fill = bacteria_label, color = bacteria_label, shape = Line, group = Line), 
                position = position_jitterdodge(dodge.width = 0.7, jitter.width = 0.2),
                size = 3, alpha = 0.75, stroke = 0.75) +

    geom_point(data = data_summary_df, aes(x = bacteria_label, y = exp.mean.predval), shape = 3, size = 3, color = color_error) +
    geom_errorbar(data = data_summary_df, aes(x = bacteria_label,
                                              y = exp.mean.predval,
                                              ymax = max,
                                              ymin = min),
                  width = 0.5, color = color_error) +
    
    scale_fill_manual(values = c("#800080","#800080","#800080","#1E90FF","#1E90FF","#1E90FF","#927ed1","#927ed1","#927ed1")) +
    scale_color_manual(values = c("#800080","#800080","#800080","#1E90FF","#1E90FF","#1E90FF","#927ed1","#927ed1","#927ed1")) +
    scale_shape_manual(values = c(21, 22, 24)) +
    
    scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x),
                  labels = trans_format("log10", math_format(10^.x)),
                  expand = c(0.1,0)) +
    
    labs(x = "Hours post-inoculation",
         y = paste0("CFUs/HNO at ", each_temp, " °C")) +
    theme_bw() +
    theme(panel.grid = element_blank(), 
          legend.position = "none",
          text = element_text(size = 20), 
          axis.text.x = element_markdown(), 
          axis.text.y = element_text(color = "black"))
  
  # Conditionally add p-value annotations layer
  if (nrow(contrast_sign) > 0) {
    plot_2 <- plot_2 +
      stat_pvalue_manual(contrast_sign, label = "p.adj.holm", y.position = location,
                         tip.length = 0.02, bracket.shorten = 0.2, vjust = -0.2, bracket.size = 0.3, size = 2.5)
  } else {
    plot_2 <- plot_2
  }
  
  # Arrange plot and tables for summary pdf
  table <- contrasts_time_df %>%
    select(condition1, condition2, p.adj.holm, sign, exp.mean.predval.1, exp.mean.predval.2, FC, highlighted) %>%
    mutate(p.adj.holm = format(p.adj.holm, digits = 2, scientific = TRUE))
  
  Tmin <- ttheme_minimal()
  panel <- ggarrange(plot_1 + theme(plot.margin = unit(c(0.25,0.25,0.25,0.25), "in")), 
                     plot_2 + theme(plot.margin = unit(c(0.25,0.25,0.25,0.25), "in")),
                     tableGrob(anova_df, theme = Tmin), 
                     tableGrob(random_effects_df, theme = Tmin, rows = NULL), 
                     tableGrob(table, theme = Tmin, rows = NULL), 
                     ncol = 1, heights = c(0.7, 0.7, 0.2, 0.2, 0.2),
                     labels = c("  Standard Boxplot ", "Predicted Mean ± 2*SE", "    Anova    ", "Random Effects", "   Contrasts  "))
  panel <- annotate_figure(panel, top = text_grob(
    paste0("Analysis for ", each_temp, "C. P-value: ", cutoff_pvalue, " and FC: ", cutoff_FC),
                                                         face = "bold", size = 14, color = "red"))
  
  # Save files
  ggsave(panel, filename = paste0(figures_folder, "/summaryCFU_", each_temp, ".pdf"), width = 10, height = 15)
  ggsave(plot_2, filename = paste0(figures_folder, "/plotCFU_", each_temp, ".png"), width = 7, height = 6)
  saveRDS(plot_2, file.path(figures_folder, paste0("plotCFU_", each_temp, ".rds")))
  write_csv(anova_df, file.path(stats_folder, paste0("anova_", each_temp, ".csv")))
  write_csv(random_effects_df, file.path(stats_folder, paste0("stats_random_effects_", each_temp, ".csv")))
  write_csv(contrasts_time_df, file.path(stats_folder, paste0("stats_contrasts_", each_temp, ".csv")))
  write_csv(data_summary_df, file.path(stats_folder, paste0("stats_summary_", each_temp, ".csv")))
  
  return(list(
    anova = anova_df,
    random_effects = random_effects_df,
    contrasts_time = contrasts_time_df,
    #data_summary = data_summary_df,
    #data_stats = data_stats,
    plot_1 = plot_1,
    plot_2 = plot_2
  ))
}

34C (Main Data)

analysis_function(CFU_data, each_temp = "34", cutoff_pvalue = 0.05, cutoff_FC = 1, color_error = "black")
$anova
               Sum Sq Mean Sq NumDF   DenDF F value  Pr(>F) sign
bacteria      1.1e+02 5.4e+01     2 1.4e+02 6.0e+00 3.2e-03    *
Time          1.1e+03 5.3e+02     2 1.2e+02 5.9e+01 1.4e-18    *
bacteria:Time 6.1e+02 1.5e+02     4 1.2e+02 1.7e+01 4.8e-11    *

$random_effects
        grp        var1 var2     vcov    sdcor proportion
1 Line:Date (Intercept) <NA> 2.437316 1.561191      21.14
2      Line (Intercept) <NA> 0.000000 0.000000       0.00
3  Residual        <NA> <NA> 9.091124 3.015149      78.86

$contrasts_time
  Time1 Time2 bacteria  estimate       SE       df   t.ratio      p.value Temp
1     0    24      Dpi  6.949409 1.005050 121.5808  6.914493 2.341064e-10   34
2    24    48      Dpi  2.807217 1.005050 121.5808  2.793113 6.066217e-03   34
3     0    24      Sau  6.213689 1.034188 121.5808  6.008280 2.012053e-08   34
4    24    48      Sau -5.797778 1.034188 121.5808 -5.606118 1.316820e-07   34
5     0    24      Spn  3.331496 1.066016 121.5808  3.125183 2.222572e-03   34
6    24    48      Spn  3.651203 1.066016 121.5808  3.425092 8.386723e-04   34
    p.adj.holm sign condition1 condition2
1 1.404639e-09    *      Dpi.0     Dpi.24
2 6.066217e-03    *     Dpi.24     Dpi.48
3 1.006027e-07    *      Sau.0     Sau.24
4 5.267280e-07    *     Sau.24     Sau.48
5 4.445144e-03    *      Spn.0     Spn.24
6 2.516017e-03    *     Spn.24     Spn.48
                                         group1
1  <b><span style='color:#1E90FF;'>0</span></b>
2 <b><span style='color:#1E90FF;'>24</span></b>
3  <b><span style='color:#800080;'>0</span></b>
4 <b><span style='color:#800080;'>24</span></b>
5  <b><span style='color:#927ED1;'>0</span></b>
6 <b><span style='color:#927ED1;'>24</span></b>
                                         group2 exp.mean.predval.1
1 <b><span style='color:#1E90FF;'>24</span></b>        51772618.35
2 <b><span style='color:#1E90FF;'>48</span></b>           49660.37
3 <b><span style='color:#800080;'>24</span></b>        12577274.74
4 <b><span style='color:#800080;'>48</span></b>           25177.68
5 <b><span style='color:#927ED1;'>24</span></b>        36429406.93
6 <b><span style='color:#927ED1;'>48</span></b>         1301972.37
  exp.mean.predval.2         FC highlighted
1          49660.374 1042.53379           -
2           2998.134   16.56376           -
3          25177.682  499.54061           -
4        8297717.186 -329.56637           +
5        1301972.375   27.98017           -
6          33799.051   38.52097           -

$plot_1


$plot_2

37C (Supplemental Data)

analysis_function(CFU_data, each_temp = "37", cutoff_pvalue = 0.05, cutoff_FC = 1, color_error = "red3")
$anova
               Sum Sq Mean Sq NumDF   DenDF F value  Pr(>F) sign
bacteria      2.1e+02 1.1e+02     2 9.3e+01 8.8e+00 3.2e-04    *
Time          7.0e+02 3.5e+02     2 7.6e+01 2.9e+01 3.5e-10    *
bacteria:Time 5.0e+02 1.3e+02     4 7.6e+01 1.1e+01 7.6e-07    *

$random_effects
        grp        var1 var2         vcov        sdcor proportion
1 Line:Date (Intercept) <NA> 2.697102e+00 1.642286e+00      18.41
2      Line (Intercept) <NA> 1.023565e-14 1.011714e-07       0.00
3  Residual        <NA> <NA> 1.195395e+01 3.457449e+00      81.59

$contrasts_time
  Time1 Time2 bacteria  estimate       SE       df    t.ratio      p.value Temp
1     0    24      Dpi  7.416020 1.411498 75.77751  5.2540080 1.324010e-06   37
2    24    48      Dpi  5.003889 1.411498 75.77751  3.5450917 6.765527e-04   37
3     0    24      Sau  3.543578 1.474261 75.77751  2.4036295 1.867823e-02   37
4    24    48      Sau -3.851043 1.474261 75.77751 -2.6121849 1.084271e-02   37
5     0    24      Spn  5.692743 1.474261 75.77751  3.8614207 2.356175e-04   37
6    24    48      Spn -1.027038 1.474261 75.77751 -0.6966457 4.881561e-01   37
    p.adj.holm sign condition1 condition2
1 7.944058e-06    *      Dpi.0     Dpi.24
2 2.706211e-03    *     Dpi.24     Dpi.48
3 3.735645e-02    *      Sau.0     Sau.24
4 3.252813e-02    *     Sau.24     Sau.48
5 1.178087e-03    *      Spn.0     Spn.24
6 4.881561e-01          Spn.24     Spn.48
                                         group1
1  <b><span style='color:#1E90FF;'>0</span></b>
2 <b><span style='color:#1E90FF;'>24</span></b>
3  <b><span style='color:#800080;'>0</span></b>
4 <b><span style='color:#800080;'>24</span></b>
5  <b><span style='color:#927ED1;'>0</span></b>
6 <b><span style='color:#927ED1;'>24</span></b>
                                         group2 exp.mean.predval.1
1 <b><span style='color:#1E90FF;'>24</span></b>        62870846.42
2 <b><span style='color:#1E90FF;'>48</span></b>           37819.24
3 <b><span style='color:#800080;'>24</span></b>        10891104.41
4 <b><span style='color:#800080;'>48</span></b>          314858.65
5 <b><span style='color:#927ED1;'>24</span></b>        39243107.12
6 <b><span style='color:#927ED1;'>48</span></b>          132262.45
  exp.mean.predval.2          FC highlighted
1          37819.236 1662.403947           -
2            253.835  148.991409           -
3         314858.653   34.590456           -
4       14811610.669  -47.042095           +
5         132262.453  296.706331           -
6         369380.033   -2.792781           +

$plot_1


$plot_2

Analysis 34 vs. 37

# Function to compare both temps 
analysis_temps_function <- function(data, cutoff_pvalue, cutoff_FC) {
  
  # Make Combined column
  data_subset <- data %>%
    mutate(Combined.Temp = interaction(Combined, Temp))
  
  # Mixed-effects model with random effects
  model <- lmer(log2FC ~ Combined * Temp
                + (1|Line) + (1|Line:Date), 
                data = data_subset)
  #Anova
  anova <- anova(model)
  anova_df <- as.data.frame(anova) %>%
    mutate(sign = case_when(
      `Pr(>F)` < cutoff_pvalue ~ "*",
      TRUE ~ "")) %>%
    mutate_if(is.numeric, ~ format(., digits = 2, scientific = TRUE))
  
  # Calculate Individual contrasts
  emmeans_model <- emmeans(model, ~ Combined * Temp)
  emmeans_Temp <- pairs(emmeans_model, simple = "Temp", adjust = "none")    
  
  # Extract random effects and convert to dataframe (if not singular)
  random_effects_df <- as.data.frame(VarCorr(model)) %>%
    mutate(proportion = round(100 * (vcov / sum(vcov)), 2))
  
  # Adds predictions based on fixed effects, averaged over random effects. It gives a population estimate
  data_summary_df <- cbind(data_subset, predval = predict(model,re.form = NA, se.fit = TRUE))
  data_summary_df <- data_summary_df %>%
    group_by(Combined.Temp, Temp_label, bacteria_label) %>%
    summarize(mean.real = mean(log2FC),
              mean.predval = mean(predval.fit), 
              mean.predval.se = mean(predval.se.fit)) %>%
    mutate(exp.mean.predval = (mean.predval),
           max = (mean.predval + 2*mean.predval.se),
           min = (mean.predval - 2*mean.predval.se))
  
  # Extract time contrasts from emmeans_model, convert to dataframe and adjust pvalues
  contrasts_temp_df <- as.data.frame(summary(emmeans_Temp)) %>%
    mutate(p.adj.holm = p.adjust(p.value, method = "holm")) %>%
    mutate(sign = case_when(
      p.adj.holm < cutoff_pvalue ~ "*",
      TRUE ~ ""))
  
  # Edits to the contrast dataframe to include pvalue brackets in plot
  contrasts_temp_df <- contrasts_temp_df %>%
    separate(contrast, into = c("Temp1", "Temp2"), sep = " - ") %>%
    mutate(Temp1 = sub("Temp", "", Temp1),
           Temp2 = sub("Temp", "", Temp2)) %>%
    mutate(condition1 = paste(Combined, Temp1, sep = "."),
           condition2 = paste(Combined, Temp2, sep = "."))
  
  # Calculate fold-change values for each contrast
  contrasts_temp_df <- contrasts_temp_df %>%
    ungroup() %>%
    left_join(select(data_summary_df, Combined.Temp, exp.mean.predval), by = join_by(condition1 == Combined.Temp)) %>%
    left_join(select(data_summary_df, Combined.Temp, exp.mean.predval), by = join_by(condition2 == Combined.Temp), suffix = c(".1", ".2")) %>%
    mutate(FC = exp.mean.predval.1 / exp.mean.predval.2,
           FC = if_else(FC < 1, -1 / FC, FC),
           highlighted = case_when(
             FC <= -cutoff_FC ~ "+",
             FC >= cutoff_FC ~ "-",
             TRUE ~ "")) 
  
  # Plot with predicted means and standard errors of the estimates
  plot_2 <- ggplot() +
    geom_point(data = data_subset, 
               aes(x = interaction(bacteria_label, Temp_label, lex.order = T, sep = ""), 
                   y = log2FC, fill = bacteria_label, color = bacteria_label, shape = Line, group = Line), 
               position = position_jitterdodge(dodge.width = 0.7, jitter.width = 0.2),
               size = 3, alpha = 0.75, stroke = 0.75) +
    
    scale_fill_manual(values = c("#800080","#800080","#1E90FF","#1E90FF","#927ed1","#927ed1")) +
    scale_color_manual(values = c("#800080","#800080","#1E90FF","#1E90FF","#927ed1","#927ed1")) +
    scale_shape_manual(values = c(21, 22, 24)) +
    
    new_scale_color() +
    
    geom_point(data = data_summary_df, aes(x = interaction(bacteria_label, Temp_label, lex.order = T, sep = ""), color = Temp_label, y = exp.mean.predval), shape = 3, size = 3) +
    geom_errorbar(data = data_summary_df, aes(x = interaction(bacteria_label, Temp_label, lex.order = T, sep = ""), color = Temp_label,
                                              y = exp.mean.predval,
                                              ymax = max,
                                              ymin = min),
                  width = 0.5) +
    
    scale_color_manual(values = c("#5b5b5b", "red3")) +
    

    
    labs(x = "Hours post-inoculation / Temp Condition",
         y = paste0("log2 (final CFUs/inoculum CFUs)")) +
    theme_bw() +
    theme(panel.grid = element_blank(), 
          legend.position = "none",
          text = element_text(size = 20), 
          axis.text.x = element_markdown(), 
          axis.text.y = element_text(color = "black"))
  
  # Save files
  ggsave(plot_2, filename = paste0(figures_folder, "/plotCFU_", "34vs37", ".png"), width = 8, height = 6)
  saveRDS(plot_2, file.path(figures_folder, paste0("plotCFU_", "34vs37", ".rds")))
  write_csv(anova_df, file.path(stats_folder, paste0("anova_", "34vs37", ".csv")))
  write_csv(random_effects_df, file.path(stats_folder, paste0("stats_random_effects_", "34vs37", ".csv")))
  write_csv(contrasts_temp_df, file.path(stats_folder, paste0("stats_contrasts_", "34vs37", ".csv")))
  write_csv(data_summary_df, file.path(stats_folder, paste0("stats_summary_", "34vs37", ".csv")))
  
  return(list(
    anova = anova_df,
    random_effects = random_effects_df,
    contrasts_temp = contrasts_temp_df,
    data_summary = data_summary_df,
    data_subset = data_subset,
    plot_2 = plot_2
  ))
}
analysis_temps_function(CFU_FC, cutoff_pvalue = 0.05, cutoff_FC = 1)
$anova
               Sum Sq Mean Sq NumDF   DenDF F value  Pr(>F) sign
Combined      2.9e+03 5.8e+02     5 1.4e+02 2.5e+01 1.1e-17    *
Temp          2.0e+00 2.0e+00     1 1.4e+02 8.6e-02 7.7e-01     
Combined:Temp 3.6e+02 7.1e+01     5 1.4e+02 3.0e+00 1.3e-02    *

$random_effects
        grp        var1 var2     vcov    sdcor proportion
1 Line:Date (Intercept) <NA> 18.47029 4.297707      43.76
2      Line (Intercept) <NA>  0.00000 0.000000       0.00
3  Residual        <NA> <NA> 23.74133 4.872508      56.24

$contrasts_temp
  Temp1 Temp2 Combined   estimate       SE       df    t.ratio    p.value
1    34    37   Dpi.24  0.8564197 1.846359 137.1905  0.4638425 0.64349574
2    34    37   Sau.24 -3.6317418 1.917861 137.2512 -1.8936420 0.06037720
3    34    37   Spn.24  3.8756252 1.943445 136.9908  1.9942039 0.04811483
4    34    37   Dpi.48  4.0255469 1.846359 137.1905  2.1802622 0.03094354
5    34    37   Sau.48 -0.8231970 1.917861 137.2512 -0.4292267 0.66843128
6    34    37   Spn.48 -2.8736494 1.943445 136.9908 -1.4786370 0.14153304
  p.adj.holm sign condition1 condition2
1  1.0000000       Dpi.24.34  Dpi.24.37
2  0.2415088       Sau.24.34  Sau.24.37
3  0.2405741       Spn.24.34  Spn.24.37
4  0.1856612       Dpi.48.34  Dpi.48.37
5  1.0000000       Sau.48.34  Sau.48.37
6  0.4245991       Spn.48.34  Spn.48.37
                                          Temp_label.1 exp.mean.predval.1
1 <b><br><span style='color:#5b5b5b;'>34 °C</span></b>         -9.4490159
2 <b><br><span style='color:#5b5b5b;'>34 °C</span></b>         -8.6830934
3 <b><br><span style='color:#5b5b5b;'>34 °C</span></b>         -5.2954233
4 <b><br><span style='color:#5b5b5b;'>34 °C</span></b>        -13.4989741
5 <b><br><span style='color:#5b5b5b;'>34 °C</span></b>         -0.3186682
6 <b><br><span style='color:#5b5b5b;'>34 °C</span></b>        -10.5629956
                                       Temp_label.2 exp.mean.predval.2
1 <b><br><span style='color:red3;'>37 °C</span></b>        -10.3054357
2 <b><br><span style='color:red3;'>37 °C</span></b>         -5.0513516
3 <b><br><span style='color:red3;'>37 °C</span></b>         -9.1710485
4 <b><br><span style='color:red3;'>37 °C</span></b>        -17.5245210
5 <b><br><span style='color:red3;'>37 °C</span></b>          0.5045288
6 <b><br><span style='color:red3;'>37 °C</span></b>         -7.6893462
         FC highlighted
1 -1.090636           +
2  1.718964           -
3 -1.731882           +
4 -1.298211           +
5  1.583241           -
6  1.373718           -

$data_summary
# A tibble: 12 × 9
# Groups:   Combined.Temp, Temp_label [12]
   Combined.Temp Temp_label                bacteria_label mean.real mean.predval
   <fct>         <fct>                     <fct>              <dbl>        <dbl>
 1 Dpi.24.34     <b><br><span style='colo… <b><span styl…   -10.0         -9.45 
 2 Sau.24.34     <b><br><span style='colo… <b><span styl…    -8.96        -8.68 
 3 Spn.24.34     <b><br><span style='colo… <b><span styl…    -4.81        -5.30 
 4 Dpi.48.34     <b><br><span style='colo… <b><span styl…   -14.1        -13.5  
 5 Sau.48.34     <b><br><span style='colo… <b><span styl…    -0.600       -0.319
 6 Spn.48.34     <b><br><span style='colo… <b><span styl…   -10.1        -10.6  
 7 Dpi.24.37     <b><br><span style='colo… <b><span styl…   -10.7        -10.3  
 8 Sau.24.37     <b><br><span style='colo… <b><span styl…    -5.11        -5.05 
 9 Spn.24.37     <b><br><span style='colo… <b><span styl…    -8.21        -9.17 
10 Dpi.48.37     <b><br><span style='colo… <b><span styl…   -17.9        -17.5  
11 Sau.48.37     <b><br><span style='colo… <b><span styl…     0.444        0.505
12 Spn.48.37     <b><br><span style='colo… <b><span styl…    -6.73        -7.69 
# ℹ 4 more variables: mean.predval.se <dbl>, exp.mean.predval <dbl>, max <dbl>,
#   min <dbl>

$data_subset
# A tibble: 170 × 10
   Date     Line  bacteria Combined Temp  Time  bacteria_label Temp_label log2FC
   <fct>    <fct> <fct>    <fct>    <fct> <fct> <fct>          <fct>       <dbl>
 1 1/17/20… HNO2… Dpi      Dpi.24   34    24    <b><span styl… <b><br><s… -15.9 
 2 1/17/20… HNO2… Dpi      Dpi.48   34    48    <b><span styl… <b><br><s… -24.0 
 3 1/17/20… HNO2… Sau      Sau.24   34    24    <b><span styl… <b><br><s…  -5.92
 4 1/17/20… HNO2… Sau      Sau.48   34    48    <b><span styl… <b><br><s…  -2.56
 5 1/17/20… HNO2… Spn      Spn.24   34    24    <b><span styl… <b><br><s…  -2.97
 6 1/17/20… HNO2… Spn      Spn.48   34    48    <b><span styl… <b><br><s… -22.7 
 7 1/24/20… HNO9… Dpi      Dpi.24   34    24    <b><span styl… <b><br><s…  -4.98
 8 1/24/20… HNO9… Dpi      Dpi.48   34    48    <b><span styl… <b><br><s… -10.8 
 9 1/24/20… HNO9… Spn      Spn.24   34    24    <b><span styl… <b><br><s…  -2.86
10 1/24/20… HNO9… Spn      Spn.48   34    48    <b><span styl… <b><br><s…  -4.91
# ℹ 160 more rows
# ℹ 1 more variable: Combined.Temp <fct>

$plot_2

Merged Summary Files

pdf_output <- file.path(figures_folder, "HNOBac_SummaryCFUs.pdf")

# Check if the file exists, and delete if it does
if (file.exists(pdf_output)) {
  file.remove(pdf_output)
}
[1] TRUE
# Now combine the PDFs into a new file
pdf_files <- list.files(figures_folder, pattern = "\\.pdf$", full.names = TRUE)
qpdf::pdf_combine(input = pdf_files, output = pdf_output)
[1] "data/outputs/CFUs/figures/HNOBac_SummaryCFUs.pdf"
Cell Counts & TEER
CFUs Epithelial Lines
Source Code
---
execute:
  message: FALSE
  warning: FALSE
---

# CFUs 48h {.unnumbered}

```{r}
library(tidyverse)
library(ggpubr)
library(ggtext)
library(ggnewscale)
library(scales)
library(lme4)
library(afex)
library(emmeans)
library(gridExtra)
```

## Data Input and Selection

### File Paths

```{r}
# Folder paths
input_path <- "data/input_data/CFUs/"
metadata_path <- "data/metadata/CFUs"

# Create subfolders for output files
dataframes_folder <- "data/dataframes"
if (!file.exists("data/dataframes")) {
  dir.create("data/dataframes", recursive = TRUE)
}

# Load data and metadata
input_data <- read_csv(file.path(input_path, "HNOBac_CFUs_0130_2024.csv")) %>% mutate_if(is.character, factor)
input_data$Time <- as.factor(input_data$Time)
Bac_order <- read_csv(file.path(metadata_path, "Order_Bacteria_CFUs.csv"))
```

### Data clean-up

```{r}
# Setting zero values to the limit of detection
CFU_data <- input_data %>%
  mutate(
    LOD = CFUs == 0,
    NewCFU = ifelse(LOD, 3.75, CFUs)
  )

# Factor Ordering and Styling
CFU_data <- CFU_data %>%
  mutate(Combined = interaction(bacteria, Time)) %>% 
  mutate(Temp_label = factor(Temp, labels = c("<b><br><span style='color:#5b5b5b;'>34 °C</span></b>",
                                              "<b><br><span style='color:red3;'>37 °C</span></b>")))  
CFU_data <- merge(CFU_data, Bac_order, by = "Combined") 
CFU_data$bacteria_label <- factor(CFU_data$bacteria_label, levels = Bac_order$bacteria_label)
CFU_data$Line <- fct_recode(CFU_data$Line, "HNO918" = "A", "HNO204" = "B", "HNO919" = "C") 

# Calculate log2 fold change between final time points vs. time 0
CFU_FC <- CFU_data %>%
  mutate(Collection_Label = ifelse(Time == 0, "initial", "final")) %>%
  group_by(Date, Line, bacteria) %>%
  reframe(
    Combined = Combined[Collection_Label == "final"],
    Date = Date[Collection_Label == "final"],
    Line = Line[Collection_Label == "final"],
    Temp = Temp[Collection_Label == "final"],
    Time = Time[Collection_Label == "final"],
    bacteria = bacteria[Collection_Label == "final"],
    bacteria_label = bacteria_label[Collection_Label == "final"],
    Temp_label = Temp_label[Collection_Label == "final"],
    log2FC = log2(NewCFU[Collection_Label == "final"]/NewCFU[Collection_Label == "initial"])) %>%
    droplevels() %>%
  mutate(Temp = factor(Temp))
```

### Saving files

```{r}
# Save data frame as CSV files in the output folder
write_csv(CFU_data, file.path(dataframes_folder, "CFU_values.csv"))
write_csv(CFU_FC, file.path(dataframes_folder, "CFU_FC.csv"))

# Save data frame as R objects in the output folder
saveRDS(CFU_data, file.path(dataframes_folder, "CFU_values.rds"))
saveRDS(CFU_FC, file.path(dataframes_folder, "CFU_FC.rds"))

# Cleaning-up all objects from the environment
rm(list = ls())

# Use this to read the final objects
CFU_data <- readRDS("data/dataframes/CFU_values.rds")
CFU_FC <- readRDS("data/dataframes/CFU_FC.rds")
```

## Stats and Plots

### File Paths

```{r}
# Folder paths
dataframes_path <- "data/dataframes"
metadata_path <- "data/metadata/CFUs"

# Create subfolders for output files
figures_folder <- "data/outputs/CFUs/figures"
if (!file.exists("data/outputs/CFUs/figures")) {
  dir.create("data/outputs/CFUs/figures", recursive = TRUE)
}
stats_folder <- "data/outputs/CFUs/stats"
if (!file.exists("data/outputs/CFUs/stats")) {
  dir.create("data/outputs/CFUs/stats", recursive = TRUE)
}

# Load data and metadata
CFU_data <- readRDS("data/dataframes/CFU_values.rds")
CFU_FC <- readRDS("data/dataframes/CFU_FC.rds")
Bac_order <- read_csv(file.path(metadata_path, "Order_Bacteria_CFUs.csv"))
```

### Analysis for each Temp

```{r, warning = FALSE}
# Function to analyze each temp condition
analysis_function <- function(data, each_temp, cutoff_pvalue, cutoff_FC, color_error) {
  
  # Subset the data to the selected temp
  data_subset <- data %>%
    filter(Temp == each_temp) 
  
  data_stats <- data_subset 
  
  # Mixed-effects model with random effects
  model <- lmer(log(NewCFU) ~ bacteria * Time 
                + (1|Line) + (1|Line:Date), 
                data = data_stats)
  #Anova
  anova <- anova(model)
  anova_df <- as.data.frame(anova) %>%
    mutate(sign = case_when(
      `Pr(>F)` < cutoff_pvalue ~ "*",
      TRUE ~ "")) %>%
    mutate_if(is.numeric, ~ format(., digits = 2, scientific = TRUE))
  
  # Calculate Individual contrasts
  emmeans_model <- emmeans(model, ~ bacteria * Time)
  emmeans_time <- pairs(emmeans_model, simple = "Time", adjust = "none")    
  
  # Extract random effects and convert to dataframe (if not singular)
  random_effects_df <- as.data.frame(VarCorr(model)) %>%
    mutate(proportion = round(100 * (vcov / sum(vcov)), 2))
  
  # Adds predictions based on fixed effects, averaged over random effects. It gives a population estimate
  data_stats <- cbind(data_stats, predval = predict(model,re.form = NA, se.fit = TRUE))
  data_summary_df <- data_stats %>%
    group_by(Combined, bacteria_label) %>%
    summarize(mean.real = mean(NewCFU),
              mean.predval = mean(predval.fit), 
              mean.predval.se = mean(predval.se.fit)) %>%
    mutate(exp.mean.predval = exp(mean.predval),
           max = exp(mean.predval + 2*mean.predval.se),
           min = exp(mean.predval - 2*mean.predval.se))
  
  # Extract time contrasts from emmeans_model, convert to dataframe and adjust pvalues. Remove the 0-48h contrasts
  contrasts_time_df <- as.data.frame(summary(emmeans_time)) %>%
    mutate(Temp = each_temp) %>%
    filter(contrast != "Time0 - Time48") %>%
    mutate(p.adj.holm = p.adjust(p.value, method = "holm")) %>%
    mutate(sign = case_when(
      p.adj.holm < cutoff_pvalue ~ "*",
      TRUE ~ ""))
  
  # Edits to the contrast dataframe to include pvalue brackets in plot
  contrasts_time_df <- contrasts_time_df %>%
    separate(contrast, into = c("Time1", "Time2"), sep = " - ") %>%
    mutate(Time1 = sub("Time", "", Time1),
           Time2 = sub("Time", "", Time2)) %>%
    mutate(condition1 = paste(bacteria, Time1, sep = "."),
           condition2 = paste(bacteria, Time2, sep = "."))
  
  contrasts_time_df$group1 <- Bac_order$bacteria_label[match(contrasts_time_df$condition1, Bac_order$Combined)]
  contrasts_time_df$group2 <- Bac_order$bacteria_label[match(contrasts_time_df$condition2, Bac_order$Combined)]
  
  # Calculate fold-change values for each contrast
  contrasts_time_df <- contrasts_time_df %>%
    ungroup() %>%
    left_join(select(data_summary_df, Combined, exp.mean.predval), by = join_by(condition1 == Combined)) %>%
    left_join(select(data_summary_df, Combined, exp.mean.predval), by = join_by(condition2 == Combined), suffix = c(".1", ".2")) %>%
    mutate(FC = exp.mean.predval.1 / exp.mean.predval.2,
           FC = if_else(FC < 1, -1 / FC, FC),
           highlighted = case_when(
             FC <= -cutoff_FC ~ "+",
             FC >= cutoff_FC ~ "-",
             TRUE ~ "")) 
  
  # Select p values to plot and define their location
  contrast_sign <- contrasts_time_df %>%
    filter(sign != "" & highlighted != "") %>%
    mutate(p.adj.holm = format(p.adj.holm, digits = 2, scientific = TRUE))
  
  location <- log10(max(data_subset$NewCFU, na.rm = TRUE)) * 1.1
  
  # Standard Boxplot
  plot_1 <- ggplot() +
    geom_boxplot(data = data_subset, aes(x = bacteria_label, y = NewCFU, fill = bacteria_label)) + 
    
    geom_jitter(data = data_subset, aes(x = bacteria_label, y = NewCFU, shape = Line), 
                fill = "grey50", color = "grey30", size = 2, width = 0.05, stroke = 0.75) +
    
    scale_fill_manual(values = c("#800080","#800080","#800080",
                                 "#1E90FF","#1E90FF","#1E90FF", 
                                 "#927ed1","#927ed1","#927ed1")) +
    
    scale_shape_manual(values = c(21, 22, 24)) +
    
    scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x),
                  labels = trans_format("log10", math_format(10^.x))) +
    labs(x = "Hours post-inoculation",
         y = paste0("CFUs/HNO at ", each_temp, " °C")) +
    theme_bw() +
    theme(panel.grid = element_blank(),
          legend.position = "none",
          text = element_text(size = 20), 
          axis.text.x = element_markdown(), 
          axis.text.y = element_text(color = "black"))
  
  # Plot with predicted means and standard errors of the estimates
  plot_2 <- ggplot() +
    geom_point(data = data_subset, 
                aes(x = bacteria_label, y = NewCFU, fill = bacteria_label, color = bacteria_label, shape = Line, group = Line), 
                position = position_jitterdodge(dodge.width = 0.7, jitter.width = 0.2),
                size = 3, alpha = 0.75, stroke = 0.75) +

    geom_point(data = data_summary_df, aes(x = bacteria_label, y = exp.mean.predval), shape = 3, size = 3, color = color_error) +
    geom_errorbar(data = data_summary_df, aes(x = bacteria_label,
                                              y = exp.mean.predval,
                                              ymax = max,
                                              ymin = min),
                  width = 0.5, color = color_error) +
    
    scale_fill_manual(values = c("#800080","#800080","#800080","#1E90FF","#1E90FF","#1E90FF","#927ed1","#927ed1","#927ed1")) +
    scale_color_manual(values = c("#800080","#800080","#800080","#1E90FF","#1E90FF","#1E90FF","#927ed1","#927ed1","#927ed1")) +
    scale_shape_manual(values = c(21, 22, 24)) +
    
    scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x),
                  labels = trans_format("log10", math_format(10^.x)),
                  expand = c(0.1,0)) +
    
    labs(x = "Hours post-inoculation",
         y = paste0("CFUs/HNO at ", each_temp, " °C")) +
    theme_bw() +
    theme(panel.grid = element_blank(), 
          legend.position = "none",
          text = element_text(size = 20), 
          axis.text.x = element_markdown(), 
          axis.text.y = element_text(color = "black"))
  
  # Conditionally add p-value annotations layer
  if (nrow(contrast_sign) > 0) {
    plot_2 <- plot_2 +
      stat_pvalue_manual(contrast_sign, label = "p.adj.holm", y.position = location,
                         tip.length = 0.02, bracket.shorten = 0.2, vjust = -0.2, bracket.size = 0.3, size = 2.5)
  } else {
    plot_2 <- plot_2
  }
  
  # Arrange plot and tables for summary pdf
  table <- contrasts_time_df %>%
    select(condition1, condition2, p.adj.holm, sign, exp.mean.predval.1, exp.mean.predval.2, FC, highlighted) %>%
    mutate(p.adj.holm = format(p.adj.holm, digits = 2, scientific = TRUE))
  
  Tmin <- ttheme_minimal()
  panel <- ggarrange(plot_1 + theme(plot.margin = unit(c(0.25,0.25,0.25,0.25), "in")), 
                     plot_2 + theme(plot.margin = unit(c(0.25,0.25,0.25,0.25), "in")),
                     tableGrob(anova_df, theme = Tmin), 
                     tableGrob(random_effects_df, theme = Tmin, rows = NULL), 
                     tableGrob(table, theme = Tmin, rows = NULL), 
                     ncol = 1, heights = c(0.7, 0.7, 0.2, 0.2, 0.2),
                     labels = c("  Standard Boxplot ", "Predicted Mean ± 2*SE", "    Anova    ", "Random Effects", "   Contrasts  "))
  panel <- annotate_figure(panel, top = text_grob(
    paste0("Analysis for ", each_temp, "C. P-value: ", cutoff_pvalue, " and FC: ", cutoff_FC),
                                                         face = "bold", size = 14, color = "red"))
  
  # Save files
  ggsave(panel, filename = paste0(figures_folder, "/summaryCFU_", each_temp, ".pdf"), width = 10, height = 15)
  ggsave(plot_2, filename = paste0(figures_folder, "/plotCFU_", each_temp, ".png"), width = 7, height = 6)
  saveRDS(plot_2, file.path(figures_folder, paste0("plotCFU_", each_temp, ".rds")))
  write_csv(anova_df, file.path(stats_folder, paste0("anova_", each_temp, ".csv")))
  write_csv(random_effects_df, file.path(stats_folder, paste0("stats_random_effects_", each_temp, ".csv")))
  write_csv(contrasts_time_df, file.path(stats_folder, paste0("stats_contrasts_", each_temp, ".csv")))
  write_csv(data_summary_df, file.path(stats_folder, paste0("stats_summary_", each_temp, ".csv")))
  
  return(list(
    anova = anova_df,
    random_effects = random_effects_df,
    contrasts_time = contrasts_time_df,
    #data_summary = data_summary_df,
    #data_stats = data_stats,
    plot_1 = plot_1,
    plot_2 = plot_2
  ))
}
```

#### 34C (Main Data)


```{r}
#| warning: FALSE
analysis_function(CFU_data, each_temp = "34", cutoff_pvalue = 0.05, cutoff_FC = 1, color_error = "black")
```

#### 37C (Supplemental Data)

```{r}
#| warning: FALSE
analysis_function(CFU_data, each_temp = "37", cutoff_pvalue = 0.05, cutoff_FC = 1, color_error = "red3")
```

### Analysis 34 vs. 37

```{r}
# Function to compare both temps 
analysis_temps_function <- function(data, cutoff_pvalue, cutoff_FC) {
  
  # Make Combined column
  data_subset <- data %>%
    mutate(Combined.Temp = interaction(Combined, Temp))
  
  # Mixed-effects model with random effects
  model <- lmer(log2FC ~ Combined * Temp
                + (1|Line) + (1|Line:Date), 
                data = data_subset)
  #Anova
  anova <- anova(model)
  anova_df <- as.data.frame(anova) %>%
    mutate(sign = case_when(
      `Pr(>F)` < cutoff_pvalue ~ "*",
      TRUE ~ "")) %>%
    mutate_if(is.numeric, ~ format(., digits = 2, scientific = TRUE))
  
  # Calculate Individual contrasts
  emmeans_model <- emmeans(model, ~ Combined * Temp)
  emmeans_Temp <- pairs(emmeans_model, simple = "Temp", adjust = "none")    
  
  # Extract random effects and convert to dataframe (if not singular)
  random_effects_df <- as.data.frame(VarCorr(model)) %>%
    mutate(proportion = round(100 * (vcov / sum(vcov)), 2))
  
  # Adds predictions based on fixed effects, averaged over random effects. It gives a population estimate
  data_summary_df <- cbind(data_subset, predval = predict(model,re.form = NA, se.fit = TRUE))
  data_summary_df <- data_summary_df %>%
    group_by(Combined.Temp, Temp_label, bacteria_label) %>%
    summarize(mean.real = mean(log2FC),
              mean.predval = mean(predval.fit), 
              mean.predval.se = mean(predval.se.fit)) %>%
    mutate(exp.mean.predval = (mean.predval),
           max = (mean.predval + 2*mean.predval.se),
           min = (mean.predval - 2*mean.predval.se))
  
  # Extract time contrasts from emmeans_model, convert to dataframe and adjust pvalues
  contrasts_temp_df <- as.data.frame(summary(emmeans_Temp)) %>%
    mutate(p.adj.holm = p.adjust(p.value, method = "holm")) %>%
    mutate(sign = case_when(
      p.adj.holm < cutoff_pvalue ~ "*",
      TRUE ~ ""))
  
  # Edits to the contrast dataframe to include pvalue brackets in plot
  contrasts_temp_df <- contrasts_temp_df %>%
    separate(contrast, into = c("Temp1", "Temp2"), sep = " - ") %>%
    mutate(Temp1 = sub("Temp", "", Temp1),
           Temp2 = sub("Temp", "", Temp2)) %>%
    mutate(condition1 = paste(Combined, Temp1, sep = "."),
           condition2 = paste(Combined, Temp2, sep = "."))
  
  # Calculate fold-change values for each contrast
  contrasts_temp_df <- contrasts_temp_df %>%
    ungroup() %>%
    left_join(select(data_summary_df, Combined.Temp, exp.mean.predval), by = join_by(condition1 == Combined.Temp)) %>%
    left_join(select(data_summary_df, Combined.Temp, exp.mean.predval), by = join_by(condition2 == Combined.Temp), suffix = c(".1", ".2")) %>%
    mutate(FC = exp.mean.predval.1 / exp.mean.predval.2,
           FC = if_else(FC < 1, -1 / FC, FC),
           highlighted = case_when(
             FC <= -cutoff_FC ~ "+",
             FC >= cutoff_FC ~ "-",
             TRUE ~ "")) 
  
  # Plot with predicted means and standard errors of the estimates
  plot_2 <- ggplot() +
    geom_point(data = data_subset, 
               aes(x = interaction(bacteria_label, Temp_label, lex.order = T, sep = ""), 
                   y = log2FC, fill = bacteria_label, color = bacteria_label, shape = Line, group = Line), 
               position = position_jitterdodge(dodge.width = 0.7, jitter.width = 0.2),
               size = 3, alpha = 0.75, stroke = 0.75) +
    
    scale_fill_manual(values = c("#800080","#800080","#1E90FF","#1E90FF","#927ed1","#927ed1")) +
    scale_color_manual(values = c("#800080","#800080","#1E90FF","#1E90FF","#927ed1","#927ed1")) +
    scale_shape_manual(values = c(21, 22, 24)) +
    
    new_scale_color() +
    
    geom_point(data = data_summary_df, aes(x = interaction(bacteria_label, Temp_label, lex.order = T, sep = ""), color = Temp_label, y = exp.mean.predval), shape = 3, size = 3) +
    geom_errorbar(data = data_summary_df, aes(x = interaction(bacteria_label, Temp_label, lex.order = T, sep = ""), color = Temp_label,
                                              y = exp.mean.predval,
                                              ymax = max,
                                              ymin = min),
                  width = 0.5) +
    
    scale_color_manual(values = c("#5b5b5b", "red3")) +
    

    
    labs(x = "Hours post-inoculation / Temp Condition",
         y = paste0("log2 (final CFUs/inoculum CFUs)")) +
    theme_bw() +
    theme(panel.grid = element_blank(), 
          legend.position = "none",
          text = element_text(size = 20), 
          axis.text.x = element_markdown(), 
          axis.text.y = element_text(color = "black"))
  
  # Save files
  ggsave(plot_2, filename = paste0(figures_folder, "/plotCFU_", "34vs37", ".png"), width = 8, height = 6)
  saveRDS(plot_2, file.path(figures_folder, paste0("plotCFU_", "34vs37", ".rds")))
  write_csv(anova_df, file.path(stats_folder, paste0("anova_", "34vs37", ".csv")))
  write_csv(random_effects_df, file.path(stats_folder, paste0("stats_random_effects_", "34vs37", ".csv")))
  write_csv(contrasts_temp_df, file.path(stats_folder, paste0("stats_contrasts_", "34vs37", ".csv")))
  write_csv(data_summary_df, file.path(stats_folder, paste0("stats_summary_", "34vs37", ".csv")))
  
  return(list(
    anova = anova_df,
    random_effects = random_effects_df,
    contrasts_temp = contrasts_temp_df,
    data_summary = data_summary_df,
    data_subset = data_subset,
    plot_2 = plot_2
  ))
}
```


```{r}
#| warning: FALSE
analysis_temps_function(CFU_FC, cutoff_pvalue = 0.05, cutoff_FC = 1)
```


### Merged Summary Files

```{r}
pdf_output <- file.path(figures_folder, "HNOBac_SummaryCFUs.pdf")

# Check if the file exists, and delete if it does
if (file.exists(pdf_output)) {
  file.remove(pdf_output)
}

# Now combine the PDFs into a new file
pdf_files <- list.files(figures_folder, pattern = "\\.pdf$", full.names = TRUE)
qpdf::pdf_combine(input = pdf_files, output = pdf_output)
```