In this document, we will go through a few steps used in my study of the potato microbiota as explained during the BioData Basel meetup on April, 9th 2026. A specificity of this project is that we isolated bacterial strains from the same plants we took microbiota samples, increasing the chance that the observed Amplicon Sequence Variant (ASV) correspond to strains. This gave us possibility of a testing hypotheses on the role of some strains based on the changes observed in the plant’s microbiota.
Mixing metagenomics and culturomics is challenging and sometimes messy. Unfortunately the “messy” nature of the task is rarely reported and deserves more attention. The goal of this presentation is to show you some concrete examples of the challenges we meet with the current technology.
For clarity reasons, I show only here a small fraction of the work we did. If you want the whole story, the preprint and the link to all the data and codes are available on BioRxiv
In the first part I show you how we detected differentially abundant strains between infected and uninfected plants using the package edgeR, a package normally used to detect differentially expressed genes in RNAseq studies.
In the second part I show you an example of how to represent the differential abundance in the whole rhizosphere. I will also show you the limits of the aggregation per Phylum/Order/Genus that we see frequently in microbiome papers.
In the third part we see how to create a custom BLAST database and query it with the goal of detecting the ASV corresponding to each bacterial strain we isolated.
This whole document is supposed to be read along with the attached pptx file.
I show you how we detected differentially abundant strains between infected and uninfected plants using the package edgeR, a package normally used to detect differentially expressed genes in RNAseq studies.
## Import ASV table
asvtab_filtered <- read.csv( "data/asvmat_br.csv",
check.names = FALSE,
row.names = 1)
## Turn it into a matrix
asvmat_BR <- as.matrix(asvtab_filtered)
asvtab_filtered |>
head() |>
kable() |>
kable_styling() |>
scroll_box(width = "750px")
| 2_BR15_1C3 | 2_BR15_1C4 | 2_BR15_1C5 | 2_BR15_1V1 | 2_BR15_1V3 | 2_BR15_1V5 | 2_BR15_2C2 | 2_BR15_2C4 | 2_BR15_2C5 | 2_BR15_2V1 | 2_BR15_2V4 | 2_BR15_2V5 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| asv000003 | 189 | 584 | 181 | 397 | 194 | 472 | 709 | 312 | 348 | 475 | 817 | 428 |
| asv000004 | 114 | 85 | 51 | 0 | 0 | 226 | 80 | 115 | 237 | 6 | 279 | 12 |
| asv000005 | 12 | 23 | 23 | 0 | 0 | 40 | 5 | 21 | 40 | 3 | 23 | 5 |
| asv000006 | 199 | 662 | 175 | 371 | 170 | 454 | 627 | 277 | 312 | 458 | 794 | 416 |
| asv000007 | 114 | 100 | 60 | 0 | 0 | 183 | 89 | 117 | 265 | 8 | 214 | 15 |
| asv000008 | 24 | 32 | 12 | 0 | 0 | 27 | 4 | 15 | 31 | 0 | 27 | 10 |
## Import phylogeny tab
phylo_filtered <- read.csv( "data/phylogeny_br.csv",
check.names = FALSE,
row.names = 1)
## Turn it into a matrix
phylo_BR <- as.matrix(phylo_filtered)
phylo_filtered |>
head() |>
kable() |>
kable_styling()
| Kingdom | Phylum | Class | Order | Family | Genus | |
|---|---|---|---|---|---|---|
| asv000003 | Bacteria | Proteobacteria | Alphaproteobacteria | Sphingomonadales | Sphingomonadaceae | Sphingobium |
| asv000004 | Bacteria | Actinobacteriota | Actinobacteria | Streptomycetales | Streptomycetaceae | Streptomyces |
| asv000005 | Bacteria | Actinobacteriota | Actinobacteria | Streptosporangiales | Streptosporangiaceae | Nonomuraea |
| asv000006 | Bacteria | Proteobacteria | Alphaproteobacteria | Sphingomonadales | Sphingomonadaceae | Sphingobium |
| asv000007 | Bacteria | Actinobacteriota | Actinobacteria | Streptomycetales | Streptomycetaceae | Streptomyces |
| asv000008 | Bacteria | Actinobacteriota | Actinobacteria | Streptosporangiales | Streptosporangiaceae | Nonomuraea |
## Import sample metadata
meta_BR <- read.csv("data/metadata_br.csv",
check.names = FALSE,
row.names = 1)
meta_BR |>
head() |>
kable() |>
kable_styling() |>
scroll_box(width = "750px")
| gen | cultiv | comp | time | treat_batch | treat | rep | plant | sample_harvest | dna_extract_batch | tube_nb | dna_extract_date | qubit1 | qubit2 | qubit_mean | pcr_batch | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2_BR15_1C3 | 2 | B | R | 15 | 1 | C | 3 | BC06 | 13/04/2020 | 1 | 51 | 16/12/2020 | 8.00 | 8.48 | 8.24 | 1 |
| 2_BR15_1C4 | 2 | B | R | 15 | 1 | C | 4 | BC07 | 13/04/2020 | 2 | 52 | 23/12/2020 | 6.04 | 6.12 | 6.08 | 2 |
| 2_BR15_1C5 | 2 | B | R | 15 | 1 | C | 5 | BC08 | 13/04/2020 | 3 | 53 | 28/12/2020 | 9.88 | 10.10 | 9.99 | 3 |
| 2_BR15_1V1 | 2 | B | R | 15 | 1 | V | 1 | BV02 | 13/04/2020 | 1 | 54 | 16/12/2020 | 9.24 | 9.40 | 9.32 | 1 |
| 2_BR15_1V3 | 2 | B | R | 15 | 1 | V | 3 | BV22 | 13/04/2020 | 2 | 55 | 23/12/2020 | 8.32 | 8.44 | 8.38 | 2 |
| 2_BR15_1V5 | 2 | B | R | 15 | 1 | V | 5 | BV30 | 13/04/2020 | 4 | 56 | 29/12/2020 | 9.00 | 9.20 | 9.10 | 4 |
## Import reference sequences
sequences <- read.csv("data/ref_sequences_br.csv",
check.names = FALSE)
Here we combine the matrices into a DGE object to be analysed by edgeR.
## Create DGEList objects
dge_br <- DGEList(counts = asvmat_BR,
genes = phylo_BR,
samples = meta_BR)
## Convert type of the "treatment" column to factor
dge_br$samples$treat <- factor(dge_br$samples$treat,
levels = c("C", "V"))
## TMM normalization (Trimmed Mean of the M-values)
dge_br <- edgeR::calcNormFactors(dge_br)
## Create Multidimensional Scaling plot
plotMDS(dge_br
, labels = rownames(dge_br$samples)
, col = as.numeric(dge_br$samples$treat)
, cex = 0.5)
The color represents the infection status (red is infected).
Most of the variability is linked to the infection status.
## Design the model's formula for the Generalized Linear Model
design <- model.matrix(~ treat , data = dge_br$samples )
## Estimate the variability of counts of each ASV (necessary for negative binomial GLM)
glmest <- estimateGLMRobustDisp(dge_br, design = design)
## Fits negative binomial GLM. prior.count adds a pseudocount to stabilize estimates for low-occurring ASVs.
fit_glm <- glmFit(glmest, design = design, prior.count = 0.125)
## GLM Likelihood Ratio test, checks if the infection status has an effect on the counts of each ASV.
## Calculates AND ADJUSTS p-value.
lrt_treat <- glmLRT(fit_glm, coef = 2)
## Sorts ASV per significance
tt_treat <- as.data.frame(topTags(lrt_treat, n = Inf ))
## Show tt_treat
tt_treat |>
head() |>
kable() |>
kable_styling() |>
scroll_box(width = "750px")
| Kingdom | Phylum | Class | Order | Family | Genus | logFC | logCPM | LR | PValue | FDR | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| asv000561 | Bacteria | Bacteroidota | Bacteroidia | Chitinophagales | Saprospiraceae | Phaeodactylibacter | -9.575301 | 10.85905 | 135.92952 | 0 | 0 |
| asv000547 | Bacteria | Bacteroidota | Bacteroidia | Chitinophagales | Saprospiraceae | Phaeodactylibacter | -8.939168 | 10.87846 | 113.19125 | 0 | 0 |
| asv001179 | Bacteria | Bacteroidota | Bacteroidia | Flavobacteriales | Flavobacteriaceae | Flavobacterium | -9.623207 | 10.90245 | 112.89278 | 0 | 0 |
| asv001158 | Bacteria | Bacteroidota | Bacteroidia | Flavobacteriales | Flavobacteriaceae | Flavobacterium | -9.498248 | 10.78260 | 111.82150 | 0 | 0 |
| asv001588 | Bacteria | Proteobacteria | Gammaproteobacteria | Xanthomonadales | NA | NA | -8.260053 | 9.61971 | 85.29664 | 0 | 0 |
| asv000839 | Bacteria | Bacteroidota | Bacteroidia | Chitinophagales | Saprospiraceae | NA | 9.190066 | 10.50159 | 69.46067 | 0 | 0 |
This table shows how differentially abundant each ASV is between the infected and uninfected plants. They are ordered accoding to the p-value.
## Export differentially expressed ASVs counts
write.csv(tt_treat, file = "data/all_ASV.csv")
We can then plot the results as a volcano plot.
## Summarize differential abundance (used for the volcano plot below)
treat_summary <- t(
summary( de <- decideTests(lrt_treat)) )
## Create volcano plot
plotMD( lrt_treat, main = "Differential abundance in the rhizosphere", cex=0.5 )
abline( h=c(-1,1), col = "blue" )
text( x = 12, y = -4, labels = paste0("Up: ",treat_summary[3],
"\n NotSign: ", treat_summary[2],
"\n Down: ", treat_summary[1]) )
In the plot above, each point represents one ASV. The x axis represents the average abundance (log scale), and the y axis represents the log-fold change between the infected and uninfected plants. The red dots represent the ASV that became more abundance upon infection, the blue dots are the ASV that became less abundant, the black dots did not change significantly.
Here I show you an example of how to represent the differential abundance in the whole rhizosphere.
Some more data wrangling is necessary between DA ASV detection and this step:
we will skip to focus on tree building and comments.
## phylogenetic trees
tree <- readRDS("data/tree_objects/ASV_phylo.rds")
This is a phylogenetic tree, aggregated at the order level.
## Phylogenetic information
tree_info <- readRDS("data/tree_objects/tree_info.rds") |>
select(-Family, -Genus)
tree_info |>
head() |>
kable() |>
kable_styling(full_width = FALSE)
| Kingdom | Phylum | Class | Order | |
|---|---|---|---|---|
| Micropepsales | Bacteria | Proteobacteria | Alphaproteobacteria | Micropepsales |
| Rhizobiales | Bacteria | Proteobacteria | Alphaproteobacteria | Rhizobiales |
| Parvibaculales | Bacteria | Proteobacteria | Alphaproteobacteria | Parvibaculales |
| Rhodospirillales | Bacteria | Proteobacteria | Alphaproteobacteria | Rhodospirillales |
| Ferrovibrionales | Bacteria | Proteobacteria | Alphaproteobacteria | Ferrovibrionales |
| Dongiales | Bacteria | Proteobacteria | Alphaproteobacteria | Dongiales |
## List of orders belonging to each phylum (used for colors)
taxalist <- readRDS("data/tree_objects/taxa_list.rds")
map(taxalist, head, 3)[1:5]
## $Proteobacteria
## [1] "Micropepsales" "Rhizobiales" "Parvibaculales"
##
## $Bdellovibrionota
## [1] "Bdellovibrionales" "na_Bdellovibrion." "Bacteriovoracales"
##
## $Dependentiae
## [1] "Babeliales"
##
## $Myxococcota
## [1] "Blfdi19" "Myxococcales" "mle1-27"
##
## $Acidobacteriota
## [1] "Subgroup 7" "PAUC26f" "Bryobacterales"
This is a list of the Orders included in each Phylum.
## Abundance matrix (used for annotations)
abund_matrix <- readRDS("data/tree_objects/abund_matrix.rds")
abund_matrix |>
head() |>
kable() |>
kable_styling(full_width = FALSE)
| B | I | |
|---|---|---|
| Micropepsales | 369782.747 | 438629.875 |
| Rhizobiales | 2671773.800 | 2441710.203 |
| Parvibaculales | 1978.973 | 1320.667 |
| Rhodospirillales | 112384.401 | 130058.304 |
| Ferrovibrionales | 8435.939 | 3225.957 |
| Dongiales | 52407.014 | 49983.107 |
This is the matrix of abundance.
## Local indicator (described above,used for more annotations)
DA_proportion_matrix <- readRDS("data/tree_objects/abund_ratio_matrix.rds")
DA_proportion_matrix |>
head() |>
kable() |>
kable_styling(full_width = FALSE)
| B | I | |
|---|---|---|
| Micropepsales | 0.1506374 | 0.0126441 |
| Rhizobiales | 0.1733440 | 0.0242824 |
| Parvibaculales | NA | NA |
| Rhodospirillales | 0.0542960 | 0.1678773 |
| Ferrovibrionales | NA | NA |
| Dongiales | NA | NA |
This is the matrix showing the ratio of differential abundance present in each Order.
## Global indicator (described above, used for even more annotations)
logfc_direction_matrix <- readRDS("data/tree_objects/logfc_matrix.rds")
logfc_direction_matrix |>
head() |>
kable() |>
kable_styling(full_width = FALSE)
| B | I | |
|---|---|---|
| Rhizobiales | 96703.519 | -11361.976 |
| Micropepsales | -7315.006 | -1324.272 |
| na_Proteobacteria | 2625.931 | -2210.329 |
| Parvibaculales | NA | NA |
| Caulobacterales | 45797.812 | -76891.229 |
| Rhodospirillales | -2419.906 | 4781.282 |
This is the matrix showing the average log-fold change of the significantly differentially abundant ASVs.
## Make basic tree
p <- ggtree(tree,
ladderize = TRUE,
layout="fan",
open.angle = 2.5) %<+% tree_info +
geom_tiplab(size = 2.5,
align = TRUE,
linesize = 0.5,
linetype = "dotted",
show.legend = FALSE)+
labs(title = "Generation 2, rhizosphere")+
theme(legend.title = element_text(size = 16),
legend.text = element_text(size = 14))
## rotate tree and add colors
q <- p |> rotate(230) |> rotate(225)|> rotate(228)|> rotate(224)|> rotate(215)|> rotate(217)|> rotate(219)|> rotate(205)|> rotate(201) |> rotate(213)|> rotate(226)|> rotate(214) |> rotate(194) |> rotate(189) |> rotate(190) |> rotate(163) |> rotate(164) |>
groupOTU(taxalist, "Phylum") +
aes(color = Phylum) +
scale_color_manual(values = c("deeppink2", "orangered", "orange", "olivedrab3", "limegreen","springgreen4", "lightseagreen", "steelblue2", "royalblue1", "grey60","purple1", "orchid2", "hotpink1"))
r <- gheatmap(q,
abund_matrix,
offset=0.88,
width =0.15,
color = "white",
colnames_angle = 45,
font.size = 2.5,
colnames_offset_y = 0,
colnames_offset_x = 0
) +
scale_fill_gradientn(
colors=c("#FFFFFF", "#FDE725FF",
"#94D840FF", "#3CBC75FF", "#1F968BFF", "#2D718EFF",
"#3F4788FF", "#440154FF"),
limits = c(0, 6e6),
name = "TMM-normalized \nAbundance",
na.value = "grey"
) +
new_scale_fill()
## Add density heatmap
s <- gheatmap(r,
DA_proportion_matrix,
offset=0.66,
width = 0.15,
color = "white",
colnames_angle = 45,
font.size = 2.5,
colnames_offset_y = 0,
colnames_offset_x = 0
)+
scale_fill_gradientn(colors = c("white", "#3F007D"),
limits = c(0, 1),
name = "Ratio",
na.value = "white"
) +
new_scale_fill()
## Add averaged logfc heatmap
t <- gheatmap(s,
logfc_direction_matrix,
offset=0.45,
width =0.15,
color = "white",
colnames_angle = 45,
font.size = 2.5,
colnames_offset_y = 0,
colnames_offset_x = 0
)+
scale_fill_gradientn(colors=c("#053061", "#2166AC", "#FFFFFF", "#B2182B", "#530019"),
values= scales::rescale(c(-3.5e5, -1e5, 0, 1e5, 2.95e5)),
limits = c(-3.5e5, 2.95e5),
name = "Weighted Averaged \nLog-fold Change",
na.value = "grey78"
)
t
To read the tree properly, please open the generated pdf file below.
This tree and the information present in it is complicated, but it is the best I can do to represent such a complex dataset. For us, this tree has been a great source of information and it takes some time to get into it and understand each information. Once it’s done though, a glance is enough to compare our results with results presented in many articles.
One of the reasons the ggtree package is great is because of the fantastic tutorials available on internet. I learned everything from this page.
–> See the script blast_db_creation_query.sh
## Import output
alignment <- read.csv("save_me/ASV_isolates_alignment.csv")
## Get the list of strains of interest
strain_list <- alignment |>
## Remove short alignments
filter(length > 400) |>
## Pull strain names only
pull(stitle) |>
## remove duplicate strains
unique()
## Print complete list of strain of interest
cat(sprintf("In the end, we have %d strains of interest to phenotype:\n",
length(strain_list)))
## In the end, we have 458 strains of interest to phenotype:
## Get list of perfect matches
perfect_match <- alignment |>
## Filter
filter(length > 400, # Remove short alignments
pident == 100 # Keep only perfect matches
) |>
## Keep only one match for each strain
distinct(saccver, .keep_all = TRUE) |>
## Select columns
select(strain = saccver, ASV = qaccver, length)
## Print
perfect_match |>
kable(label = "List of perfect matches") |>
kable_styling(full_width = FALSE) |>
scroll_box(height = "400px")
| strain | ASV | length |
|---|---|---|
| BR014A | asv000047 | 414 |
| BS236 | asv000047 | 414 |
| BS222 | asv000047 | 414 |
| BS177 | asv000047 | 414 |
| BS139 | asv000047 | 414 |
| BS200 | asv000047 | 414 |
| BS232 | asv000047 | 414 |
| BR066 | asv000047 | 414 |
| BS199 | asv000047 | 414 |
| BS150 | asv000047 | 414 |
| BL096 | asv000047 | 414 |
| BL074 | asv000047 | 414 |
| AS035 | asv000047 | 414 |
| AS001 | asv000047 | 414 |
| AR039 | asv000047 | 414 |
| AS013 | asv000047 | 414 |
| BR101 | asv000049 | 413 |
| BR010 | asv000049 | 413 |
| BS192 | asv000049 | 413 |
| BS181 | asv000049 | 413 |
| AR150 | asv000049 | 413 |
| BS130 | asv000051 | 415 |
| BS119 | asv000051 | 415 |
| AS029 | asv000068 | 416 |
| BR107 | asv000068 | 416 |
| BL049 | asv000068 | 416 |
| BS196 | asv000068 | 416 |
| AS040 | asv000068 | 416 |
| AS022 | asv000068 | 416 |
| AR057 | asv000068 | 416 |
| AR042 | asv000068 | 416 |
| AR005 | asv000068 | 416 |
| AR002 | asv000068 | 416 |
| AR001 | asv000068 | 416 |
| BS132 | asv000070 | 416 |
| BS180 | asv000077 | 416 |
| AR037 | asv000077 | 416 |
| AL004 | asv000077 | 416 |
| BR134 | asv000077 | 416 |
| AR055 | asv000077 | 416 |
| AR206 | asv000077 | 416 |
| AS085 | asv000086 | 413 |
| BR157 | asv000086 | 413 |
| AS050 | asv000086 | 413 |
| AR097 | asv000086 | 413 |
| BS107 | asv000134 | 416 |
| BS079 | asv000216 | 416 |
| BS179 | asv000216 | 416 |
| BS187 | asv000216 | 416 |
| AR095 | asv000216 | 416 |
| AS146 | asv000244 | 416 |
| BS239 | asv000330 | 416 |
| AL025 | asv000330 | 416 |
| AL010 | asv000330 | 416 |
| AL040 | asv000330 | 416 |
| BS140 | asv000330 | 416 |
| BR077 | asv000330 | 416 |
| AR117 | asv000330 | 416 |
| BR121 | asv000372 | 416 |
| AR021 | asv000372 | 416 |
| BS231 | asv000375 | 409 |
| BS103 | asv000375 | 409 |
| BR094 | asv000375 | 409 |
| BR144 | asv000375 | 409 |
| AS108 | asv000395 | 412 |
| BS088 | asv000395 | 412 |
| AR010 | asv000395 | 412 |
| AS010 | asv000395 | 412 |
| AR051 | asv000395 | 412 |
| AR046 | asv000395 | 412 |
| AS121b | asv000395 | 412 |
| AS106 | asv000395 | 412 |
| BR081 | asv000395 | 412 |
| AL005 | asv000438 | 413 |
| BR133 | asv000438 | 413 |
| AR009 | asv000438 | 413 |
| BR112 | asv000438 | 413 |
| AS056 | asv000475 | 417 |
| AS006 | asv000475 | 417 |
| AR008 | asv000475 | 417 |
| AR068 | asv000512 | 414 |
| AR093 | asv000512 | 414 |
| AS024 | asv000668 | 413 |
| AR076 | asv000668 | 413 |
| BR034 | asv000668 | 413 |
| AR144 | asv000668 | 413 |
| BS038 | asv000668 | 413 |
| BS037 | asv000668 | 413 |
| BS158 | asv000668 | 413 |
| AS182 | asv000668 | 413 |
| AS181 | asv000668 | 413 |
| BS061 | asv000668 | 413 |
| AS026 | asv000668 | 413 |
| AR190 | asv000668 | 413 |
| AR178 | asv000668 | 413 |
| AR176 | asv000668 | 413 |
| AR143 | asv000668 | 413 |
| BR109 | asv000670 | 409 |
| AR071 | asv000701 | 415 |
| AR168 | asv000701 | 415 |
| AR125 | asv000701 | 415 |
| AR088 | asv000701 | 415 |
| AS123 | asv000843 | 416 |
| AS093 | asv001155 | 413 |
| AR201 | asv001155 | 413 |
| AR132 | asv001155 | 413 |
| BS131 | asv001219 | 414 |
| BS118 | asv001219 | 414 |
| AL091 | asv001219 | 414 |
| AL090 | asv001219 | 414 |
| BS146 | asv001219 | 414 |
| BL080 | asv001219 | 414 |
| BS134 | asv001219 | 414 |
| BR088 | asv001219 | 414 |
| BL052 | asv001219 | 414 |
| BL046 | asv001219 | 414 |
| AS096 | asv001219 | 414 |
| AS094 | asv001219 | 414 |
| AS132 | asv001219 | 414 |
| AS144 | asv001219 | 414 |
| AS021 | asv001219 | 414 |
| BL047a | asv001872 | 412 |
| AS133 | asv001872 | 412 |
| BR013 | asv001884 | 409 |
| BS071 | asv002046 | 417 |
| BR099 | asv002046 | 417 |
| BR015 | asv002046 | 417 |
| AS178 | asv002046 | 417 |
| AR179 | asv002046 | 417 |
| AR177 | asv002046 | 417 |
| BS237 | asv002208 | 417 |
| BS027 | asv002231 | 414 |
| BS229 | asv002231 | 414 |
| BR012 | asv002231 | 414 |
| BR046 | asv002231 | 414 |
| AR203b | asv002231 | 414 |
| BS113 | asv002388 | 416 |
| BR136 | asv002388 | 416 |
| AS027 | asv002573 | 413 |
| AS180 | asv002573 | 413 |
| AR032 | asv002573 | 413 |
| AS080 | asv002573 | 413 |
| BS157 | asv002615 | 412 |
| AL028 | asv002748 | 414 |
| AS092 | asv002849 | 416 |
| AS057 | asv002849 | 416 |
| BS172 | asv002982 | 415 |
| AR073 | asv003008 | 415 |
| AR089 | asv003008 | 415 |
| AR024 | asv003008 | 415 |
| AR202 | asv003008 | 415 |
| BS141 | asv003045 | 417 |
| AL039 | asv003204 | 417 |
| AL034 | asv003204 | 417 |
| AR061 | asv003204 | 417 |
| BL047b | asv003269 | 414 |
| BL047c | asv003269 | 414 |
| BR050 | asv003333 | 409 |
| BR049 | asv003333 | 409 |
| BS123 | asv003333 | 409 |
| BR156 | asv003333 | 409 |
| AR118 | asv003333 | 409 |
| AR149 | asv003603 | 416 |
| BS080 | asv003647 | 416 |
| BS138 | asv004665 | 417 |
| AS058 | asv005149 | 417 |
| BL071 | asv005149 | 417 |
| BL067 | asv005149 | 417 |
| BR153 | asv005149 | 417 |
| BR150 | asv005149 | 417 |
| BR126 | asv005149 | 417 |
| AS064 | asv005149 | 417 |
| AS054 | asv005149 | 417 |
| AR083 | asv005149 | 417 |
| AL064 | asv005149 | 417 |
| AS070 | asv005149 | 417 |
| AR116 | asv005149 | 417 |
| AR081 | asv005149 | 417 |
| AR077 | asv005149 | 417 |
| AR079 | asv005979 | 414 |
| AR078 | asv005979 | 414 |
| BR127 | asv007669 | 416 |
## Report on perfect matches
cat(sprintf("%d ASVs are perfectly matched by at least a strain, with a total of %d strains.\n",
length(unique(perfect_match$ASV)),
nrow(perfect_match)))
## 45 ASVs are perfectly matched by at least a strain, with a total of 182 strains.
## Get list with 1 mismatch (simplified)
mismatch <- alignment |>
## Filter
filter(length > 400, # Remove short alignments
pident != 100, # Keep only imperfect matches
!(saccver %in% perfect_match$strain), # Remove strains that have a perfect match
) |>
## Keep only one match for each strain
distinct(saccver, .keep_all = TRUE) |>
## Select columns
select(strain = saccver, ASV = qaccver, mismatch)
## Print
mismatch |>
kable(label = "List of strains with a mismatch") |>
kable_styling(full_width = FALSE) |>
scroll_box(height = "400px")
| strain | ASV | mismatch |
|---|---|---|
| BS227 | asv000004 | 1 |
| BS195a | asv000004 | 1 |
| BS101 | asv000004 | 1 |
| BS217 | asv000004 | 1 |
| BS195B | asv000004 | 1 |
| BS166 | asv000004 | 1 |
| BS089 | asv000004 | 1 |
| BR163 | asv000004 | 1 |
| BR111 | asv000004 | 1 |
| BR104 | asv000004 | 1 |
| BR097 | asv000004 | 1 |
| BR048 | asv000004 | 1 |
| BL087 | asv000004 | 1 |
| AS136 | asv000004 | 1 |
| AS113 | asv000004 | 1 |
| AS111 | asv000004 | 1 |
| AS104 | asv000004 | 1 |
| AS099 | asv000004 | 1 |
| AS076 | asv000004 | 1 |
| AS073 | asv000004 | 1 |
| AS041 | asv000004 | 1 |
| AS037 | asv000004 | 1 |
| AS032 | asv000004 | 1 |
| AS004 | asv000004 | 1 |
| AR083D | asv000004 | 1 |
| AR083C | asv000004 | 1 |
| AR072 | asv000004 | 1 |
| AR069 | asv000004 | 1 |
| AR064 | asv000004 | 1 |
| AR062 | asv000004 | 1 |
| AR036 | asv000004 | 1 |
| AR029 | asv000004 | 1 |
| AR027 | asv000004 | 1 |
| AR018 | asv000004 | 1 |
| AR013 | asv000004 | 1 |
| AR012 | asv000004 | 1 |
| BR070 | asv000004 | 1 |
| AR065 | asv000004 | 1 |
| BS220 | asv000004 | 1 |
| BS185 | asv000004 | 1 |
| BS093c | asv000004 | 1 |
| BS093b | asv000004 | 1 |
| BS086 | asv000004 | 1 |
| BR117 | asv000004 | 1 |
| AS131 | asv000004 | 1 |
| AS114 | asv000004 | 1 |
| AS078 | asv000004 | 1 |
| AS020 | asv000004 | 1 |
| AR109 | asv000004 | 1 |
| AR108 | asv000004 | 1 |
| AR003 | asv000004 | 1 |
| BS197b | asv000004 | 1 |
| BS188 | asv000004 | 1 |
| BS167 | asv000004 | 1 |
| BS112 | asv000004 | 1 |
| BS096 | asv000004 | 1 |
| BS090 | asv000004 | 1 |
| BS084 | asv000004 | 1 |
| BS082 | asv000004 | 1 |
| BS238 | asv000004 | 1 |
| BS226 | asv000004 | 1 |
| BS207 | asv000004 | 1 |
| BS205 | asv000004 | 1 |
| BS160 | asv000004 | 1 |
| BS159 | asv000004 | 1 |
| BS152 | asv000004 | 1 |
| BS149 | asv000004 | 1 |
| BS128 | asv000004 | 1 |
| BR079 | asv000004 | 1 |
| BR068 | asv000004 | 1 |
| BR061 | asv000004 | 1 |
| BR045 | asv000004 | 1 |
| BR044 | asv000004 | 1 |
| BR165 | asv000004 | 1 |
| BR164 | asv000004 | 1 |
| BR159 | asv000004 | 1 |
| BR143 | asv000004 | 1 |
| BR142 | asv000004 | 1 |
| BR132 | asv000004 | 1 |
| BL038 | asv000004 | 1 |
| AS079 | asv000004 | 1 |
| AS053 | asv000004 | 1 |
| AS002 | asv000004 | 1 |
| AS116 | asv000004 | 1 |
| AS115 | asv000004 | 1 |
| AR094 | asv000004 | 1 |
| AR091 | asv000004 | 1 |
| AR082 | asv000004 | 1 |
| AR006 | asv000004 | 1 |
| AR044 | asv000004 | 1 |
| AR033 | asv000004 | 1 |
| AR030 | asv000004 | 1 |
| AR019 | asv000004 | 1 |
| AR113 | asv000004 | 1 |
| AR101 | asv000004 | 1 |
| AL071 | asv000004 | 1 |
| AL045 | asv000004 | 1 |
| BS078 | asv000004 | 1 |
| BR113 | asv000004 | 1 |
| AS105 | asv000004 | 1 |
| AS098a | asv000004 | 1 |
| AS008 | asv000004 | 1 |
| AR124 | asv000004 | 1 |
| AR115 | asv000004 | 1 |
| AR085 | asv000004 | 1 |
| AR043 | asv000004 | 1 |
| AR038b | asv000004 | 1 |
| AL083 | asv000004 | 1 |
| BS211 | asv000004 | 1 |
| AS100 | asv000004 | 1 |
| AR092 | asv000004 | 1 |
| BR085 | asv000004 | 1 |
| BR074 | asv000004 | 1 |
| AS098b | asv000004 | 1 |
| AR050 | asv000004 | 1 |
| BS215a | asv000004 | 1 |
| BS202 | asv000004 | 1 |
| BS155 | asv000004 | 1 |
| BS093a | asv000004 | 1 |
| BR131 | asv000004 | 1 |
| AS121c | asv000004 | 1 |
| AS121a | asv000004 | 1 |
| AS014 | asv000004 | 1 |
| AS007 | asv000004 | 1 |
| AR092b | asv000004 | 1 |
| AR083b | asv000004 | 1 |
| AR015 | asv000004 | 1 |
| AR004 | asv000004 | 1 |
| AL074 | asv000004 | 1 |
| BR122 | asv000049 | 1 |
| AS044 | asv000051 | 1 |
| AR060 | asv000051 | 1 |
| BR138 | asv000051 | 1 |
| AS176 | asv000051 | 1 |
| AS063 | asv000051 | 1 |
| AS062 | asv000051 | 1 |
| BR154 | asv000051 | 1 |
| BR098 | asv000068 | 1 |
| AR025 | asv000077 | 1 |
| AR017 | asv000086 | 1 |
| BS213 | asv000149 | 1 |
| AS112 | asv000149 | 1 |
| BL040B | asv000149 | 1 |
| BL040A | asv000149 | 1 |
| AS118 | asv000149 | 1 |
| AS097 | asv000149 | 1 |
| AR020 | asv000149 | 1 |
| BR092a | asv000149 | 1 |
| BR092b | asv000149 | 1 |
| BS234 | asv000149 | 1 |
| BS145 | asv000149 | 1 |
| BR060 | asv000149 | 1 |
| BR114 | asv000149 | 1 |
| AL067 | asv000149 | 1 |
| BR057 | asv000149 | 1 |
| BR051 | asv000149 | 1 |
| BS214 | asv000149 | 1 |
| AR170 | asv000149 | 1 |
| BS106 | asv000216 | 1 |
| BS105 | asv000216 | 1 |
| BS144 | asv000216 | 1 |
| BR130 | asv000216 | 1 |
| AL076 | asv000216 | 1 |
| BS184 | asv000248 | 1 |
| BS102 | asv000285 | 1 |
| AR070 | asv000575 | 1 |
| AR066 | asv000575 | 1 |
| BS240 | asv000668 | 1 |
| AS071 | asv000701 | 1 |
| BR087 | asv000701 | 1 |
| BR078 | asv000701 | 1 |
| BR075 | asv000701 | 1 |
| BR137 | asv000701 | 1 |
| BR135 | asv000701 | 1 |
| BR093 | asv000701 | 1 |
| AR114 | asv000701 | 1 |
| BR058 | asv000701 | 1 |
| AR040 | asv001199 | 1 |
| AS048 | asv001199 | 1 |
| BS216c | asv001199 | 1 |
| BS216a | asv001199 | 1 |
| BS197a | asv001199 | 1 |
| AS005 | asv001199 | 1 |
| BS133 | asv001219 | 1 |
| AS018 | asv001219 | 1 |
| AR098 | asv001277 | 1 |
| BR065 | asv001327 | 1 |
| BS115 | asv001631 | 1 |
| BS235 | asv001977 | 1 |
| AS086 | asv002271 | 1 |
| AR067 | asv002271 | 1 |
| AL048 | asv002271 | 1 |
| AL043 | asv002271 | 1 |
| AL016 | asv002271 | 1 |
| AL006 | asv002271 | 1 |
| BS163 | asv002271 | 1 |
| BS110 | asv002271 | 1 |
| BR145 | asv002271 | 1 |
| AS011 | asv002271 | 1 |
| AR111 | asv002271 | 1 |
| BS203 | asv002271 | 1 |
| BS189 | asv002271 | 1 |
| BS143 | asv002271 | 1 |
| BR120 | asv002271 | 1 |
| BR103 | asv002271 | 1 |
| BR067 | asv002271 | 1 |
| BR128 | asv002271 | 1 |
| AS047 | asv002271 | 1 |
| AS038 | asv002271 | 1 |
| AS030 | asv002271 | 1 |
| AS019 | asv002271 | 1 |
| AR105 | asv002271 | 1 |
| AR049 | asv002271 | 1 |
| AR048 | asv002271 | 1 |
| BR155 | asv002271 | 1 |
| BR089 | asv002271 | 1 |
| AS095 | asv002271 | 1 |
| AS081 | asv002271 | 1 |
| AS077 | asv002271 | 1 |
| AS075 | asv002271 | 1 |
| AS015 | asv002271 | 1 |
| AS012 | asv002271 | 1 |
| AR103 | asv002271 | 1 |
| AR084 | asv002271 | 1 |
| BR064 | asv002346 | 1 |
| AR016 | asv002346 | 1 |
| BR119 | asv002346 | 1 |
| BR095 | asv002346 | 1 |
| BR129 | asv002346 | 1 |
| BS161 | asv002346 | 1 |
| BS083 | asv002346 | 1 |
| BR106 | asv002346 | 1 |
| BR100 | asv002346 | 1 |
| AR063 | asv002448 | 1 |
| BS148 | asv002488 | 1 |
| AR122 | asv003008 | 1 |
| AS109 | asv003126 | 1 |
| BS233 | asv003607 | 1 |
| BS228 | asv003607 | 1 |
| AS127 | asv003607 | 1 |
| AS107 | asv003607 | 1 |
| BR162 | asv003607 | 1 |
| AS049 | asv003625 | 1 |
| BR071 | asv003647 | 1 |
| BS124 | asv003647 | 1 |
| BS219c | asv003681 | 1 |
| BS219b | asv003681 | 1 |
| BS129b | asv003681 | 1 |
| BS170 | asv003790 | 1 |
| BS092 | asv003945 | 1 |
| AS028 | asv004156 | 1 |
| AR075 | asv004400 | 1 |
| AS061 | asv004651 | 1 |
| AR056 | asv004651 | 1 |
| BR052 | asv004651 | 1 |
| AR135 | asv004651 | 1 |
| BS230 | asv004743 | 1 |
| AS052 | asv004743 | 1 |
| BS135 | asv004823 | 1 |
| BS137b | asv005080 | 1 |
| AS145 | asv005080 | 1 |
| AS140A | asv005080 | 1 |
| BS136 | asv005080 | 1 |
| AS143 | asv005080 | 1 |
| AS142 | asv005080 | 1 |
| AS141 | asv005080 | 1 |
| AS139a | asv005080 | 1 |
| AR034 | asv005080 | 1 |
| BS218 | asv005080 | 1 |
| BS137a | asv005080 | 1 |
| BS140b | asv005080 | 1 |
| AS140b | asv005080 | 1 |
| AS060 | asv005242 | 1 |
| AS087 | asv005242 | 1 |
| AR022 | asv006178 | 1 |
| AL013 | asv006178 | 1 |
## Report on perfect matches
cat(sprintf("%d ASVs are matched by at least a strain with a mismatch, with a total of %d strains.\n",
length(unique(mismatch$ASV)),
nrow(mismatch)))
## 39 ASVs are matched by at least a strain with a mismatch, with a total of 276 strains.
We did not have the means to screen all the strains of interest, so we chosed based on the following criteria:
If you want the whole story, the preprint and the link to all the data and codes are available on BioRxiv.
sessionInfo()
## R version 4.5.2 (2025-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=English_United Kingdom.utf8
## [2] LC_CTYPE=English_United Kingdom.utf8
## [3] LC_MONETARY=English_United Kingdom.utf8
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United Kingdom.utf8
##
## time zone: Europe/Paris
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] ggnewscale_0.5.2 ggtreeExtra_1.20.1 ggtree_4.0.4 pheatmap_1.0.13
## [5] RColorBrewer_1.1-3 viridis_0.6.5 viridisLite_0.4.3 edgeR_4.8.2
## [9] limma_3.66.0 kableExtra_1.4.0 knitr_1.51 reshape2_1.4.5
## [13] lubridate_1.9.5 forcats_1.0.1 stringr_1.6.0 dplyr_1.2.0
## [17] purrr_1.2.1 readr_2.2.0 tidyr_1.3.2 tibble_3.3.1
## [21] ggplot2_4.0.2 tidyverse_2.0.0
##
## loaded via a namespace (and not attached):
## [1] ggiraph_0.9.6 tidyselect_1.2.1 farver_2.1.2
## [4] S7_0.2.1 fastmap_1.2.0 lazyeval_0.2.2
## [7] fontquiver_0.2.1 digest_0.6.39 timechange_0.4.0
## [10] lifecycle_1.0.5 statmod_1.5.1 tidytree_0.4.7
## [13] magrittr_2.0.4 compiler_4.5.2 rlang_1.1.7
## [16] sass_0.4.10 tools_4.5.2 yaml_2.3.12
## [19] labeling_0.4.3 htmlwidgets_1.6.4 plyr_1.8.9
## [22] xml2_1.5.2 aplot_0.2.9 withr_3.0.2
## [25] grid_4.5.2 gdtools_0.5.0 scales_1.4.0
## [28] MASS_7.3-65 cli_3.6.5 rmarkdown_2.31
## [31] treeio_1.34.0 generics_0.1.4 rstudioapi_0.18.0
## [34] tzdb_0.5.0 ape_5.8-1 cachem_1.1.0
## [37] parallel_4.5.2 ggplotify_0.1.3 yulab.utils_0.2.4
## [40] vctrs_0.7.1 jsonlite_2.0.0 fontBitstreamVera_0.1.1
## [43] gridGraphics_0.5-1 hms_1.1.4 patchwork_1.3.2
## [46] systemfonts_1.3.1 locfit_1.5-9.12 jquerylib_0.1.4
## [49] glue_1.8.0 stringi_1.8.7 gtable_0.3.6
## [52] pillar_1.11.1 rappdirs_0.3.4 htmltools_0.5.9
## [55] R6_2.6.1 textshaping_1.0.5 evaluate_1.0.5
## [58] lattice_0.22-7 ggfun_0.2.0 fontLiberation_0.1.0
## [61] bslib_0.10.0 Rcpp_1.1.1 svglite_2.2.2
## [64] gridExtra_2.3 nlme_3.1-168 xfun_0.56
## [67] fs_1.6.6 pkgconfig_2.0.3
We know what we want about the ASVs: Are they differentially abundant or not? We will now export their sequences as a FASTA file for the next step of the analysis: Finding the corresponding strains.
## Get the list of all the ASVs
all_asv <- rownames(tt_treat)
## Get their DNA sequence
all_seq <- sequences |>
filter(asv %in% all_asv) |>
column_to_rownames("asv")
all_seq |>
head() |>
kable() |>
kable_styling()|>
scroll_box(width = "750px")
## Create a function to export the sequences as fasta
export_to_fasta <- function(df, output_file) {
# Extract row names and sequences
# We replace spaces with underscores because BLAST+ reads sequence IDs up to the first space
strain_names <- gsub(" ", "_", rownames(df))
# Assuming the sequence is in the first column
sequences <- as.character(df[, 1])
# Create the >Header lines
headers <- paste0(">", strain_names)
# Interleave headers and sequences (Row 1: Header, Row 2: Seq, Row 3: Header...)
fasta_lines <- c(rbind(headers, sequences))
# Write the lines to the specified file
writeLines(fasta_lines, output_file)
cat("Successfully exported", length(strain_names), "sequences to", output_file, "\n")
}
## Save the sequences of all ASV as fasta
export_to_fasta(all_seq, "data/all_ASV.fasta")