Create the dtaframe for OD590 across timepoints
biofilm <- tribble(
~Week, ~Population, ~Biological_Replicate, ~Technical_Replicate, ~OD590,
0, "Plasmid-Free", 1, 1, 0.2548,
0, "Plasmid-Free", 1, 2, 0.1868,
0, "Plasmid-Free", 1, 3, 0.2458,
0, "Plasmid-Free", 1, 4, 0.3248,
0, "Plasmid-Free", 1, 5, 0.4168,
0, "Plasmid-Free", 1, 6, 0.4228,
0, "Plasmid-Free", 2, 1, 0.3768,
0, "Plasmid-Free", 2, 2, 0.3618,
0, "Plasmid-Free", 2, 3, 0.3938,
0, "Plasmid-Free", 2, 4, 0.4498,
0, "Plasmid-Free", 2, 5, 0.4728,
0, "Plasmid-Free", 2, 6, 0.4728,
0, "Plasmid-Free", 3, 1, 0.2028,
0, "Plasmid-Free", 3, 2, 0.2598,
0, "Plasmid-Free", 3, 3, 0.3638,
0, "Plasmid-Free", 3, 4, 0.2988,
0, "Plasmid-Free", 3, 5, 0.3998,
0, "Plasmid-Free", 3, 6, 0.8288,
0, "Plasmid-Carrying", 1, 1, 0.0638,
0, "Plasmid-Carrying", 1, 2, 0.0738,
0, "Plasmid-Carrying", 1, 3, 0.1028,
0, "Plasmid-Carrying", 1, 4, 0.0738,
0, "Plasmid-Carrying", 1, 5, 0.0548,
0, "Plasmid-Carrying", 1, 6, 0.0668,
0, "Plasmid-Carrying", 2, 1, 0.6158,
0, "Plasmid-Carrying", 2, 2, 0.1258,
0, "Plasmid-Carrying", 2, 3, 0.1788,
0, "Plasmid-Carrying", 2, 4, 0.2178,
0, "Plasmid-Carrying", 2, 5, 0.3678,
0, "Plasmid-Carrying", 2, 6, 0.2208,
0, "Plasmid-Carrying", 3, 1, 0.3528,
0, "Plasmid-Carrying", 3, 2, 0.1358,
0, "Plasmid-Carrying", 3, 3, 0.3578,
0, "Plasmid-Carrying", 3, 4, 0.1828,
0, "Plasmid-Carrying", 3, 5, 0.2538,
0, "Plasmid-Carrying", 3, 6, 0.2068,
1, "Plasmid-Free", 1, 1, 0.297,
1, "Plasmid-Free", 1, 2, 0.175,
1, "Plasmid-Free", 1, 3, 0.223,
1, "Plasmid-Free", 1, 4, 0.124,
1, "Plasmid-Free", 1, 5, 0.243,
1, "Plasmid-Free", 1, 6, 0.304,
1, "Plasmid-Free", 2, 1, 0.4,
1, "Plasmid-Free", 2, 2, 0.187,
1, "Plasmid-Free", 2, 3, 0.165,
1, "Plasmid-Free", 2, 4, 0.226,
1, "Plasmid-Free", 2, 5, 0.193,
1, "Plasmid-Free", 2, 6, 0.438,
1, "Plasmid-Free", 3, 1, 0.499,
1, "Plasmid-Free", 3, 2, 0.287,
1, "Plasmid-Free", 3, 3, 0.298,
1, "Plasmid-Free", 3, 4, 0.352,
1, "Plasmid-Free", 3, 5, 0.41,
1, "Plasmid-Free", 3, 6, 0.456,
1, "Plasmid-Carrying", 1, 1, 0.551,
1, "Plasmid-Carrying", 1, 2, 0.113,
1, "Plasmid-Carrying", 1, 3, 0.112,
1, "Plasmid-Carrying", 1, 4, 0.235,
1, "Plasmid-Carrying", 1, 5, 0.149,
1, "Plasmid-Carrying", 1, 6, 0.358,
1, "Plasmid-Carrying", 2, 1, 1.339,
1, "Plasmid-Carrying", 2, 2, 0.781,
1, "Plasmid-Carrying", 2, 3, 0.702,
1, "Plasmid-Carrying", 2, 4, 0.764,
1, "Plasmid-Carrying", 2, 5, 0.702,
1, "Plasmid-Carrying", 2, 6, 0.727,
1, "Plasmid-Carrying", 3, 1, 0.18,
1, "Plasmid-Carrying", 3, 2, 0.173,
1, "Plasmid-Carrying", 3, 3, 0.227,
1, "Plasmid-Carrying", 3, 4, 0.272,
1, "Plasmid-Carrying", 3, 5, 0.184,
1, "Plasmid-Carrying", 3, 6, 0.153,
2, "Plasmid-Free", 1, 1, 0.282,
2, "Plasmid-Free", 1, 2, 0.389,
2, "Plasmid-Free", 1, 3, 0.285,
2, "Plasmid-Free", 1, 4, 0.441,
2, "Plasmid-Free", 1, 5, 0.304,
2, "Plasmid-Free", 1, 6, 0.444,
2, "Plasmid-Free", 2, 1, 0.237,
2, "Plasmid-Free", 2, 2, 0.344,
2, "Plasmid-Free", 2, 3, 0.333,
2, "Plasmid-Free", 2, 4, 0.243,
2, "Plasmid-Free", 2, 5, 0.24,
2, "Plasmid-Free", 2, 6, 0.445,
2, "Plasmid-Free", 3, 1, 0.78,
2, "Plasmid-Free", 3, 2, 0.845,
2, "Plasmid-Free", 3, 3, 0.748,
2, "Plasmid-Free", 3, 4, 0.946,
2, "Plasmid-Free", 3, 5, 0.931,
2, "Plasmid-Free", 3, 6, 0.904,
2, "Plasmid-Carrying", 1, 1, 0.513,
2, "Plasmid-Carrying", 1, 2, 0.563,
2, "Plasmid-Carrying", 1, 3, 0.681,
2, "Plasmid-Carrying", 1, 4, 0.431,
2, "Plasmid-Carrying", 1, 5, 0.388,
2, "Plasmid-Carrying", 1, 6, 0.256,
2, "Plasmid-Carrying", 2, 1, 0.224,
2, "Plasmid-Carrying", 2, 2, 0.376,
2, "Plasmid-Carrying", 2, 3, 0.231,
2, "Plasmid-Carrying", 2, 4, 0.297,
2, "Plasmid-Carrying", 2, 5, 0.287,
2, "Plasmid-Carrying", 2, 6, 0.352,
2, "Plasmid-Carrying", 3, 1, 0.185,
2, "Plasmid-Carrying", 3, 2, 0.168,
2, "Plasmid-Carrying", 3, 3, 0.338,
2, "Plasmid-Carrying", 3, 4, 0.273,
2, "Plasmid-Carrying", 3, 5, 0.24,
2, "Plasmid-Carrying", 3, 6, 0.294,
3, "Plasmid-Free", 1, 1, 0.39,
3, "Plasmid-Free", 1, 2, 0.274,
3, "Plasmid-Free", 1, 3, 0.253,
3, "Plasmid-Free", 1, 4, 0.334,
3, "Plasmid-Free", 1, 5, 0.362,
3, "Plasmid-Free", 1, 6, 0.272,
3, "Plasmid-Free", 2, 1, 0.343,
3, "Plasmid-Free", 2, 2, 0.176,
3, "Plasmid-Free", 2, 3, 0.173,
3, "Plasmid-Free", 2, 4, 0.128,
3, "Plasmid-Free", 2, 5, 0.143,
3, "Plasmid-Free", 2, 6, 0.262,
3, "Plasmid-Free", 3, 1, 0.717,
3, "Plasmid-Free", 3, 2, 0.558,
3, "Plasmid-Free", 3, 3, 0.524,
3, "Plasmid-Free", 3, 4, 0.6,
3, "Plasmid-Free", 3, 5, 0.62,
3, "Plasmid-Free", 3, 6, 0.688,
3, "Plasmid-Carrying", 1, 1, 0.131,
3, "Plasmid-Carrying", 1, 2, 0.254,
3, "Plasmid-Carrying", 1, 3, 0.219,
3, "Plasmid-Carrying", 1, 4, 0.077,
3, "Plasmid-Carrying", 1, 5, 0.066,
3, "Plasmid-Carrying", 1, 6, 0.159,
3, "Plasmid-Carrying", 2, 1, 1.107,
3, "Plasmid-Carrying", 2, 2, 0.951,
3, "Plasmid-Carrying", 2, 3, 0.717,
3, "Plasmid-Carrying", 2, 4, 0.558,
3, "Plasmid-Carrying", 2, 5, 0.737,
3, "Plasmid-Carrying", 2, 6, 0.906,
3, "Plasmid-Carrying", 3, 1, 1.838,
3, "Plasmid-Carrying", 3, 2, 1.376,
3, "Plasmid-Carrying", 3, 3, 0.598,
3, "Plasmid-Carrying", 3, 4, 0.19,
3, "Plasmid-Carrying", 3, 5, 0.809,
3, "Plasmid-Carrying", 3, 6, 1.261,
4, "Plasmid-Free", 1, 1, 0.661,
4, "Plasmid-Free", 1, 2, 0.856,
4, "Plasmid-Free", 1, 3, 1.063,
4, "Plasmid-Free", 1, 4, 0.903,
4, "Plasmid-Free", 1, 5, 0.77,
4, "Plasmid-Free", 1, 6, 1.394,
4, "Plasmid-Free", 2, 1, 0.355,
4, "Plasmid-Free", 2, 2, 0.282,
4, "Plasmid-Free", 2, 3, 0.378,
4, "Plasmid-Free", 2, 4, 0.488,
4, "Plasmid-Free", 2, 5, 0.324,
4, "Plasmid-Free", 2, 6, 0.445,
4, "Plasmid-Free", 3, 1, 0.385,
4, "Plasmid-Free", 3, 2, 0.382,
4, "Plasmid-Free", 3, 3, 0.472,
4, "Plasmid-Free", 3, 4, 0.344,
4, "Plasmid-Free", 3, 5, 0.426,
4, "Plasmid-Free", 3, 6, 0.829,
4, "Plasmid-Carrying", 1, 1, 0.019,
4, "Plasmid-Carrying", 1, 2, 0.026,
4, "Plasmid-Carrying", 1, 3, 0.042,
4, "Plasmid-Carrying", 1, 4, 0.043,
4, "Plasmid-Carrying", 1, 5, 0.048,
4, "Plasmid-Carrying", 1, 6, 0.026,
4, "Plasmid-Carrying", 2, 1, 0.147,
4, "Plasmid-Carrying", 2, 2, 0.155,
4, "Plasmid-Carrying", 2, 3, 0.108,
4, "Plasmid-Carrying", 2, 4, 0.156,
4, "Plasmid-Carrying", 2, 5, 0.147,
4, "Plasmid-Carrying", 2, 6, 0.123,
4, "Plasmid-Carrying", 3, 2, 0.121,
4, "Plasmid-Carrying", 3, 3, 0.075,
4, "Plasmid-Carrying", 3, 4, 0.132,
4, "Plasmid-Carrying", 3, 5, 0.164,
4, "Plasmid-Carrying", 3, 6, 0.283,
5, "Plasmid-Free", 1, 1, 1.025,
5, "Plasmid-Free", 1, 2, 0.948,
5, "Plasmid-Free", 1, 3, 0.867,
5, "Plasmid-Free", 1, 4, 0.935,
5, "Plasmid-Free", 1, 5, 0.705,
5, "Plasmid-Free", 1, 6, 0.883,
5, "Plasmid-Free", 2, 1, 0.647,
5, "Plasmid-Free", 2, 2, 0.44,
5, "Plasmid-Free", 2, 3, 0.438,
5, "Plasmid-Free", 2, 4, 0.496,
5, "Plasmid-Free", 2, 5, 0.439,
5, "Plasmid-Free", 2, 6, 0.679,
5, "Plasmid-Free", 3, 1, 0.683,
5, "Plasmid-Free", 3, 2, 0.214,
5, "Plasmid-Free", 3, 3, 0.539,
5, "Plasmid-Free", 3, 4, 0.546,
5, "Plasmid-Free", 3, 5, 0.544,
5, "Plasmid-Free", 3, 6, 1.088,
5, "Plasmid-Carrying", 1, 1, 0,
5, "Plasmid-Carrying", 1, 2, 0.023,
5, "Plasmid-Carrying", 1, 3, 0.073,
5, "Plasmid-Carrying", 1, 4, 0.033,
5, "Plasmid-Carrying", 1, 5, 0.048,
5, "Plasmid-Carrying", 1, 6, 0.037,
5, "Plasmid-Carrying", 2, 1, 0.356,
5, "Plasmid-Carrying", 2, 2, 0.356,
5, "Plasmid-Carrying", 2, 3, 0.474,
5, "Plasmid-Carrying", 2, 4, 0.415,
5, "Plasmid-Carrying", 2, 5, 0.456,
5, "Plasmid-Carrying", 2, 6, 0.378,
5, "Plasmid-Carrying", 3, 1, 0.189,
5, "Plasmid-Carrying", 3, 2, 0.145,
5, "Plasmid-Carrying", 3, 3, 0.153,
5, "Plasmid-Carrying", 3, 4, 0.142,
5, "Plasmid-Carrying", 3, 5, 0.132,
5, "Plasmid-Carrying", 3, 6, 0.221
)
# Make factors
biofilm <- biofilm %>%
mutate(
Week = factor(Week, levels = 0:5),
Population = factor(Population, levels = c("Plasmid-Free", "Plasmid-Carrying")),
Biological_Replicate = factor(Biological_Replicate),
Technical_Replicate = factor(Technical_Replicate)
)
bio_means <- biofilm %>%
group_by(Week, Population, Biological_Replicate) %>%
summarise(
mean_OD590 = mean(OD590, na.rm = TRUE),
.groups = "drop"
)
# Then summarize across biological replicates
summary_df <- bio_means %>%
group_by(Week, Population) %>%
summarise(
mean_OD590 = mean(mean_OD590, na.rm = TRUE),
sd_OD590 = sd(mean_OD590, na.rm = TRUE),
n = n(),
se_OD590 = sd_OD590 / sqrt(n),
.groups = "drop"
)
# -----------------------------
#Main figure: raw data + bio replicate means + overall mean ± SE
# -----------------------------
p_main <- ggplot() +
# raw technical replicates
geom_jitter(
data = biofilm,
aes(x = Week, y = OD590, color = Population),
width = 0.12, alpha = 0.18, size = 1.5
) +
# biological replicate means (dashed lines)
geom_line(
data = bio_means,
aes(x = Week, y = mean_OD590,
group = interaction(Population, Biological_Replicate),
color = Population),
alpha = 0.3,
linewidth = 0.8,
linetype = "dashed"
) +
geom_point(
data = bio_means,
aes(x = Week, y = mean_OD590, color = Population),
alpha = 0.7,
size = 2
) +
# overall means (solid, bold)
geom_line(
data = summary_df,
aes(x = Week, y = mean_OD590,
color = Population,
group = Population),
linewidth = 1.6
) +
geom_point(
data = summary_df,
aes(x = Week, y = mean_OD590, color = Population),
size = 3
) +
geom_errorbar(
data = summary_df,
aes(x = Week,
ymin = mean_OD590 - se_OD590,
ymax = mean_OD590 + se_OD590,
color = Population),
width = 0.15,
linewidth = 0.8
) +
# custom colors
scale_color_manual(values = c(
"Plasmid-Free" = "aquamarine4",
"Plasmid-Carrying" = "violetred1"
)) +
labs(
title = "Biofilm Biomass of Evolved Populations in Antibiotic-Free Conditions",
subtitle = "Technical replicates shown as points; Biological replicates shown as dashed lines",
x = "Week",
y = "OD590"
) +
theme_classic(base_size = 13) +
theme(legend.position = "none")
p_main
p_reps <- ggplot(bio_means,
aes(x = Week, y = mean_OD590,
group = Biological_Replicate,
color = Population)) +
geom_line(linewidth = 1) +
geom_point(size = 2.5) +
facet_wrap(~Population) +
labs(
title = "Biological Replicate Means Across Time",
x = "Week",
y = "Mean OD590"
) +
theme_classic(base_size = 13) +
scale_color_manual(values = c(
"Plasmid-Free" = "aquamarine4",
"Plasmid-Carrying" = "violetred1"
)) +
theme(legend.position = "none")
p_reps
combined_plot <- p_main / p_reps +
plot_layout(heights = c(2, 1))
combined_plot

``` r
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats 1.0.1 ✔ readr 2.2.0
## ✔ lubridate 1.9.5 ✔ stringr 1.6.0
## ✔ purrr 1.2.1 ✔ tibble 3.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
diff_df <- tribble(
~Kanamycin, ~Week, ~MBIC,
0,0,-0.2793333333,
1,0,0.1675,
2,0,0.3301666666,
4,0,0.2676111112,
8,0,0.0395,
16,0,0.0837222222,
32,0,0.04016666667,
64,0,0.02927777778,
128,0,0.03483333333,
256,0,0.03527777777,
0,1,0.0787777778,
1,1,0.0323333333,
2,1,0.0276666666,
4,1,0.0067777778,
8,1,0.0155555555,
16,1,-0.1133333333,
32,1,-0.0685555555,
64,1,-0.03344444445,
128,1,-0.02577777777,
256,1,-0.018,
0,2,0.1776666667,
1,2,0.2084444445,
2,2,0.2651111111,
4,2,0.218,
8,2,0.0973333333,
16,2,0.1723333334,
32,2,-0.0907777778,
64,2,-0.00644444444,
128,2,0.02044444445,
256,2,0.03855555556,
0,3,0.0641666667,
1,3,-0.0809444445,
2,3,0.0393888889,
4,3,-0.2166111111,
8,3,-0.2213888889,
16,3,-0.3880555555,
32,3,-0.7551666667,
64,3,-0.0242777777,
128,3,0.01038888889,
256,3,-0.06983333333,
0,4,-0.1016666666,
1,4,-0.1762222222,
2,4,-0.0924444444,
4,4,-0.0251111111,
8,4,0.0854444445,
16,4,-0.0152222222,
32,4,0.0376666667,
64,4,0.086,
128,4,0.1392222223,
256,4,0.01188888889,
0,5,0.0066888889,
1,5,0.1715777778,
2,5,0.1016888888,
4,5,0.0485777778,
8,5,0.0323555556,
16,5,-0.2773111111,
32,5,-0.1920888889,
64,5,0.0291333333,
128,5,0.0216888889,
256,5,0.00913333334
)
diff_df <- diff_df %>%
mutate(
Week = factor(Week),
Kanamycin = factor(Kanamycin)
)
head(diff_df)
summary(diff_df)
## Kanamycin Week MBIC
## 0 : 6 0:10 Min. :-0.7551667
## 1 : 6 1:10 1st Qu.:-0.0422222
## 2 : 6 2:10 Median : 0.0246778
## 4 : 6 3:10 Mean : 0.0001676
## 8 : 6 4:10 3rd Qu.: 0.0800139
## 16 : 6 5:10 Max. : 0.3301667
## (Other):24
diff_df <- diff_df %>%
mutate(
Kanamycin_num = as.numeric(as.character(Kanamycin))
)
p_heat <- ggplot(diff_df, aes(x = Week, y = Kanamycin, fill = MBIC)) +
geom_tile(color = "white", linewidth = 0.6) +
scale_fill_gradient2(
low = "aquamarine4",
mid = "white",
high = "violetred1",
midpoint = 0,
name = "Δ Biomass (PC − PF)"
) +
labs(
title = "Context-Dependent Effect of Plasmid Carriage on Biofilm Tolerance",
subtitle = "Pink favors Plasmid Carrying, Green favors Plasmid Free",
x = "Week",
y = "Kanamycin (µg/mL)"
) +
theme_classic(base_size = 13) +
theme(
panel.grid.major = element_line(color = "gray90", linewidth = 0.3),
panel.grid.minor = element_blank()
)
p_heat
library(tidyverse)
diff_summary <- diff_df %>%
group_by(Week) %>%
summarise(
mean_diff = mean(MBIC, na.rm = TRUE),
sd_diff = sd(MBIC, na.rm = TRUE),
n = n(),
se_diff = sd_diff / sqrt(n),
.groups = "drop"
)
diff_summary <- diff_summary %>%
mutate(
Week_num = as.numeric(as.character(Week)),
direction = case_when(
mean_diff > 0 ~ "Plasmid Carrying Advantage",
mean_diff < 0 ~ "Plasmid Free Advantage",
TRUE ~ "Neutral"
)
)
diff_summary <- diff_summary %>%
mutate(Week_num = as.numeric(as.character(Week)))
p_lollipop <- ggplot(diff_summary, aes(x = Week_num, y = mean_diff, color = direction)) +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray75", linewidth = 0.7) +
geom_errorbar(aes(ymin = mean_diff - se_diff, ymax = mean_diff + se_diff),
width = 0.12, linewidth = 1.1) +
geom_point(size = 3.5) +
scale_color_manual(values = c(
"Plasmid Carrying Advantage" = "violetred1",
"Plasmid Free Advantage" = "#3B7A57",
"Neutral" = "gray30"
)) +
scale_x_continuous(breaks = 0:5) +
labs(
title = "Overall Effect of Plasmid Carriage on Biofilm Tolerance",
subtitle = expression(Delta*"MBIC = Plasmid-Carrying - Plasmid-Free"),
x = "Week",
y = "Mean Difference in MBIC (PC - PF)"
) +
theme_classic(base_size = 13) +
theme(legend.position = "none")
p_lollipop
combined_plot <- p_lollipop / p_heat +
plot_layout(height = c(1, 1))
combined_plot
