Skip to content

Commit 7b5b2cd

Browse files
Add per-population copy number (PPCN) normalization to anvi-script-gen-function-matrix-across-genomes
Adds a --per-population-copy-number flag that reports an additional matrix in which each function's frequency in a metagenomic assembly is divided by the number of populations estimated to be present in that assembly. Population counts are estimated from single-copy core genes via anvi'o's NumGenomesEstimator (the mode of SCG hits per domain, summed across Bacteria, Archaea, and Eukarya), the same estimate anvi-display-contigs-stats reports. This normalization, introduced by Iva Veseli (Veseli et al. 2025), makes functional capacity comparable across metagenomic assemblies of very different community sizes. The flag works only with an external-genomes file (-e), since the normalization is meaningful for whole metagenomic assemblies (or long-read metagenomes) but not for individual genomes or a genomes storage database. Assemblies whose SCGs are too sparse to estimate a population count are reported as NA rather than dividing by zero. All matrix outputs also lead with their descriptive columns (key, function, accession) before the per-genome columns. Help page and CLI help included.
1 parent c0db1ec commit 7b5b2cd

5 files changed

Lines changed: 229 additions & 72 deletions

File tree

anvio/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,23 @@ def TABULATE(table, header, numalign="right", max_width=0):
13291329
"anvi'o will only use the best hit for any gene that has multiple hits by default. But you can turn "
13301330
"that behavior off explicitly and show anvi'o who is the boss by using this flag."}
13311331
),
1332+
'per-population-copy-number': (
1333+
['--per-population-copy-number'],
1334+
{'default': False,
1335+
'action': 'store_true',
1336+
'help': "Request an additional output matrix that reports the per-population copy number (PPCN) of each "
1337+
"function in every metagenomic assembly (or long-read metagenome). anvi'o divides the frequency of a "
1338+
"function in a given assembly by the number of populations estimated to be present in that same "
1339+
"assembly (estimated from single-copy core genes: the mode of the number of hits to the single-copy "
1340+
"core genes of each domain, summed across Bacteria, Archaea, and Eukarya, just like "
1341+
"`anvi-display-contigs-stats` does). This normalization is useful for metagenomic assemblies, where the "
1342+
"raw frequency of a function scales with the number of populations in the assembly and is therefore "
1343+
"not comparable across samples of vastly different community sizes. It follows the per-population copy "
1344+
"number normalization introduced by Iva Veseli (Veseli et al. 2025). It requires that your "
1345+
"contigs databases have been annotated with single-copy core genes via `anvi-run-hmms`, and it works "
1346+
"only with an external-genomes file (`-e`), since population normalization is meaningful for whole "
1347+
"metagenomic assemblies but not for individual genomes or a genomes storage database."}
1348+
),
13321349

13331350
'include-gc-identity-as-function': (
13341351
['--include-gc-identity-as-function'],

anvio/cli/gen_function_matrix_across_genomes.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ def get_args():
8181
"given function accross genomes. If you set a value, those functions that occur in less number "
8282
"of genomes will be excluded."), type=int)
8383

84+
groupD = parser.add_argument_group('PER-POPULATION COPY NUMBER', "If you are working with metagenomic assemblies, the raw frequency of a "
85+
"function will scale with the number of populations in each assembly, which makes it difficult to "
86+
"compare functions across samples of different community sizes. If you are simply looking at "
87+
"presence/absence, mostly everything will be present everywhere when looking at metagenomic "
88+
"assemblies as a whole. To account for this, anvi'o can "
89+
"report an additional matrix in which function frequencies are normalized by the estimated number "
90+
"of populations in each metagenome (an approach known as per-population copy number, or PPCN).")
91+
groupD.add_argument(*anvio.A('per-population-copy-number'), **anvio.K('per-population-copy-number'))
92+
8493
groupE = parser.add_argument_group('GENES', "By default, anvi'o will look for genes in contigs databases that are identified "
8594
"by `pyrodigal-gv`. But if you have generated your contigs databse with external gene calls, or have "
8695
"otherwise used another gene caller than the default, you can explicitly ask anvi'o to use that "

anvio/docs/artifacts/functions-across-genomes-txt.md

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,64 @@ A TAB-delimited output file that describes the distribution of functions across
22

33
## General format
44

5-
With this file type anvi'o may describe the presence-absence of individual functions across genomes, or the frequency of functions. The header of the last column in a %(functions-across-genomes-txt)s file will be the name of the function annotation source from which the gene functions were recovered.
5+
With this file type anvi'o may describe the presence-absence of individual functions across genomes, or the frequency of functions. The first column contains a unique `key` for each function, and the second column the annotation generated from the annotation source the user chose. The third column is optional and includes the accession ids. The remaining columns hold one value per (meta)genome.
66

77
### Frequency of function hits across genomes
88

99
Here is an example output file that shows the frequency of functions across 9 _Bifidobacterium_ genomes:
1010

11-
|**key**|**B_adolescentis_1_11**|**B_adolescentis_22L**|**B_adolescentis_6**|**B_lactis_Bl_04**|**B_lactis_CNCM_I_2494**|**B_lactis_DSM_10140**|**B_longum_JDM301**|**B_longum_KACC_91563**|**B_longum_NCIMB8809**|**COG20_FUNCTION**|
11+
|**key**|**COG20_FUNCTION**|**B_adolescentis_1_11**|**B_adolescentis_22L**|**B_adolescentis_6**|**B_lactis_Bl_04**|**B_lactis_CNCM_I_2494**|**B_lactis_DSM_10140**|**B_longum_JDM301**|**B_longum_KACC_91563**|**B_longum_NCIMB8809**|
1212
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
13-
|`func_3874a4219ffa`|1|1|1|1|1|1|1|1|1|Chromosomal replication initiation ATPase DnaA (DnaA) (PDB:1L8Q)|
14-
|`func_1530552f6b61`|1|1|1|1|1|1|1|1|1|DNA polymerase III sliding clamp (beta) subunit, PCNA homolog (DnaN) (PDB:1JQJ)|
15-
|`func_060988dd6d3a`|1|1|1|1|1|1|1|1|1|Recombinational DNA repair ATPase RecF (RecF) (PDB:5Z67)|
16-
|`func_46477a763d38`|1|1|1|1|1|1|1|1|1|Predicted nucleic acid-binding protein, contains Zn-ribbon domain (includes truncated derivatives)|
17-
|`func_184ca83fe067`|2|2|2|2|2|2|2|2|2|DNA gyrase/topoisomerase IV, subunit B (GyrB) (PDB:1EI1)|
18-
|`func_2ee67347f04b`|2|2|2|2|2|2|2|2|2|DNA gyrase/topoisomerase IV, subunit A (GyrA) (PDB:1SUU)|
19-
|`func_ff160914972f`|1|0|0|0|0|0|3|1|1|Predicted ATPase, archaeal AAA+ ATPase superfamily|
20-
|`func_598325c18ceb`|1|0|0|0|0|0|1|1|1|Molybdopterin or thiamine biosynthesis adenylyltransferase (ThiF) (PDB:1ZUD) (PUBMED:32239579)|
21-
|`func_8ce5ff84aa42`|14|13|13|10|10|10|22|17|16|Predicted arabinose efflux permease AraJ, MFS family (AraJ) (PDB:4LDS)|
22-
|`func_300e2c6e37e4`|1|1|1|1|1|1|1|1|1|Glutamate dehydrogenase/leucine dehydrogenase (GdhA) (PDB:1B3B) (PUBMED:24391520)|
23-
|`func_a666bc87a03f`|1|1|1|1|1|1|1|1|1|Large-conductance mechanosensitive channel (MscL) (PDB:2OAR)|
24-
|`func_71379d89f0c6`|1|1|1|1|1|1|1|1|1|UTP pyrophosphatase, metal-dependent hydrolase family (YgjP) (PDB:4JIU) (PUBMED:27941785)|
25-
|`func_772efbb7cb2e`|2|2|2|2|2|2|2|2|2|Hemolysin-related protein, contains CBS domains, UPF0053 family (TlyC) (PDB:2NQW)|
26-
|`func_5a5bf9735c8a`|1|1|1|1|1|1|1|1|1|Carbonic anhydrase (CynT) (PDB:1EKJ) (PUBMED:22081392)|
27-
|`func_1ffc26d0034f`|4|4|1|3|3|3|12|13|2|Transposase (or an inactivated derivative) (IS285)|
28-
|`func_0631d9fecae6`|2|1|1|1|1|1|2|1|1|Phosphoenolpyruvate carboxylase (Ppc) (PDB:1FIY)|
29-
|`func_62068e09f9f3`|2|1|1|0|0|0|1|1|1|Chromosome segregation ATPase Smc (Smc) (PDB:5XG3)|
30-
|`func_f4b8779f6ad8`|2|0|0|0|0|0|0|0|0|Peptidoglycan-binding (PGRP) domain of peptidoglycan hydrolases (PGRP) (PDB:4FET)|
31-
|`func_700652d0a2dd`|1|1|1|1|1|1|1|1|1|Uncharacterized membrane protein YjjP, DUF1212 family (YjjP)|
13+
|`func_3874a4219ffa`|Chromosomal replication initiation ATPase DnaA (DnaA) (PDB:1L8Q)|1|1|1|1|1|1|1|1|1|
14+
|`func_1530552f6b61`|DNA polymerase III sliding clamp (beta) subunit, PCNA homolog (DnaN) (PDB:1JQJ)|1|1|1|1|1|1|1|1|1|
15+
|`func_060988dd6d3a`|Recombinational DNA repair ATPase RecF (RecF) (PDB:5Z67)|1|1|1|1|1|1|1|1|1|
16+
|`func_46477a763d38`|Predicted nucleic acid-binding protein, contains Zn-ribbon domain (includes truncated derivatives)|1|1|1|1|1|1|1|1|1|
17+
|`func_184ca83fe067`|DNA gyrase/topoisomerase IV, subunit B (GyrB) (PDB:1EI1)|2|2|2|2|2|2|2|2|2|
18+
|`func_2ee67347f04b`|DNA gyrase/topoisomerase IV, subunit A (GyrA) (PDB:1SUU)|2|2|2|2|2|2|2|2|2|
19+
|`func_ff160914972f`|Predicted ATPase, archaeal AAA+ ATPase superfamily|1|0|0|0|0|0|3|1|1|
20+
|`func_598325c18ceb`|Molybdopterin or thiamine biosynthesis adenylyltransferase (ThiF) (PDB:1ZUD) (PUBMED:32239579)|1|0|0|0|0|0|1|1|1|
21+
|`func_8ce5ff84aa42`|Predicted arabinose efflux permease AraJ, MFS family (AraJ) (PDB:4LDS)|14|13|13|10|10|10|22|17|16|
22+
|`func_300e2c6e37e4`|Glutamate dehydrogenase/leucine dehydrogenase (GdhA) (PDB:1B3B) (PUBMED:24391520)|1|1|1|1|1|1|1|1|1|
23+
|`func_a666bc87a03f`|Large-conductance mechanosensitive channel (MscL) (PDB:2OAR)|1|1|1|1|1|1|1|1|1|
24+
|`func_71379d89f0c6`|UTP pyrophosphatase, metal-dependent hydrolase family (YgjP) (PDB:4JIU) (PUBMED:27941785)|1|1|1|1|1|1|1|1|1|
25+
|`func_772efbb7cb2e`|Hemolysin-related protein, contains CBS domains, UPF0053 family (TlyC) (PDB:2NQW)|2|2|2|2|2|2|2|2|2|
26+
|`func_5a5bf9735c8a`|Carbonic anhydrase (CynT) (PDB:1EKJ) (PUBMED:22081392)|1|1|1|1|1|1|1|1|1|
27+
|`func_1ffc26d0034f`|Transposase (or an inactivated derivative) (IS285)|4|4|1|3|3|3|12|13|2|
28+
|`func_0631d9fecae6`|Phosphoenolpyruvate carboxylase (Ppc) (PDB:1FIY)|2|1|1|1|1|1|2|1|1|
29+
|`func_62068e09f9f3`|Chromosome segregation ATPase Smc (Smc) (PDB:5XG3)|2|1|1|0|0|0|1|1|1|
30+
|`func_f4b8779f6ad8`|Peptidoglycan-binding (PGRP) domain of peptidoglycan hydrolases (PGRP) (PDB:4FET)|2|0|0|0|0|0|0|0|0|
31+
|`func_700652d0a2dd`|Uncharacterized membrane protein YjjP, DUF1212 family (YjjP)|1|1|1|1|1|1|1|1|1|
3232
|(...)|(...)|(...)|(...)|(...)|(...)|(...)|(...)|(...)|(...)|(...)|
3333

3434
### Presence/absence of function hits across genomes
3535

3636
In contrast, here is the presence/absence report for the same data:
3737

38-
|**key**|**B_adolescentis_1_11**|**B_adolescentis_22L**|**B_adolescentis_6**|**B_lactis_Bl_04**|**B_lactis_CNCM_I_2494**|**B_lactis_DSM_10140**|**B_longum_JDM301**|**B_longum_KACC_91563**|**B_longum_NCIMB8809**|**COG20_FUNCTION**|
38+
|**key**|**COG20_FUNCTION**|**B_adolescentis_1_11**|**B_adolescentis_22L**|**B_adolescentis_6**|**B_lactis_Bl_04**|**B_lactis_CNCM_I_2494**|**B_lactis_DSM_10140**|**B_longum_JDM301**|**B_longum_KACC_91563**|**B_longum_NCIMB8809**|
3939
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
40-
|`func_3874a4219ffa`|1|1|1|1|1|1|1|1|1|Chromosomal replication initiation ATPase DnaA (DnaA) (PDB:1L8Q)|
41-
|`func_1530552f6b61`|1|1|1|1|1|1|1|1|1|DNA polymerase III sliding clamp (beta) subunit, PCNA homolog (DnaN) (PDB:1JQJ)|
42-
|`func_060988dd6d3a`|1|1|1|1|1|1|1|1|1|Recombinational DNA repair ATPase RecF (RecF) (PDB:5Z67)|
43-
|`func_46477a763d38`|1|1|1|1|1|1|1|1|1|Predicted nucleic acid-binding protein, contains Zn-ribbon domain (includes truncated derivatives)|
44-
|`func_184ca83fe067`|1|1|1|1|1|1|1|1|1|DNA gyrase/topoisomerase IV, subunit B (GyrB) (PDB:1EI1)|
45-
|`func_2ee67347f04b`|1|1|1|1|1|1|1|1|1|DNA gyrase/topoisomerase IV, subunit A (GyrA) (PDB:1SUU)|
46-
|`func_ff160914972f`|1|0|0|0|0|0|1|1|1|Predicted ATPase, archaeal AAA+ ATPase superfamily|
47-
|`func_598325c18ceb`|1|0|0|0|0|0|1|1|1|Molybdopterin or thiamine biosynthesis adenylyltransferase (ThiF) (PDB:1ZUD) (PUBMED:32239579)|
48-
|`func_8ce5ff84aa42`|1|1|1|1|1|1|1|1|1|Predicted arabinose efflux permease AraJ, MFS family (AraJ) (PDB:4LDS)|
49-
|`func_300e2c6e37e4`|1|1|1|1|1|1|1|1|1|Glutamate dehydrogenase/leucine dehydrogenase (GdhA) (PDB:1B3B) (PUBMED:24391520)|
50-
|`func_a666bc87a03f`|1|1|1|1|1|1|1|1|1|Large-conductance mechanosensitive channel (MscL) (PDB:2OAR)|
51-
|`func_71379d89f0c6`|1|1|1|1|1|1|1|1|1|UTP pyrophosphatase, metal-dependent hydrolase family (YgjP) (PDB:4JIU) (PUBMED:27941785)|
52-
|`func_772efbb7cb2e`|1|1|1|1|1|1|1|1|1|Hemolysin-related protein, contains CBS domains, UPF0053 family (TlyC) (PDB:2NQW)|
53-
|`func_5a5bf9735c8a`|1|1|1|1|1|1|1|1|1|Carbonic anhydrase (CynT) (PDB:1EKJ) (PUBMED:22081392)|
54-
|`func_1ffc26d0034f`|1|1|1|1|1|1|1|1|1|Transposase (or an inactivated derivative) (IS285)|
55-
|`func_0631d9fecae6`|1|1|1|1|1|1|1|1|1|Phosphoenolpyruvate carboxylase (Ppc) (PDB:1FIY)|
56-
|`func_62068e09f9f3`|1|1|1|0|0|0|1|1|1|Chromosome segregation ATPase Smc (Smc) (PDB:5XG3)|
57-
|`func_f4b8779f6ad8`|1|0|0|0|0|0|0|0|0|Peptidoglycan-binding (PGRP) domain of peptidoglycan hydrolases (PGRP) (PDB:4FET)|
58-
|`func_700652d0a2dd`|1|1|1|1|1|1|1|1|1|Uncharacterized membrane protein YjjP, DUF1212 family (YjjP)|
40+
|`func_3874a4219ffa`|Chromosomal replication initiation ATPase DnaA (DnaA) (PDB:1L8Q)|1|1|1|1|1|1|1|1|1|
41+
|`func_1530552f6b61`|DNA polymerase III sliding clamp (beta) subunit, PCNA homolog (DnaN) (PDB:1JQJ)|1|1|1|1|1|1|1|1|1|
42+
|`func_060988dd6d3a`|Recombinational DNA repair ATPase RecF (RecF) (PDB:5Z67)|1|1|1|1|1|1|1|1|1|
43+
|`func_46477a763d38`|Predicted nucleic acid-binding protein, contains Zn-ribbon domain (includes truncated derivatives)|1|1|1|1|1|1|1|1|1|
44+
|`func_184ca83fe067`|DNA gyrase/topoisomerase IV, subunit B (GyrB) (PDB:1EI1)|1|1|1|1|1|1|1|1|1|
45+
|`func_2ee67347f04b`|DNA gyrase/topoisomerase IV, subunit A (GyrA) (PDB:1SUU)|1|1|1|1|1|1|1|1|1|
46+
|`func_ff160914972f`|Predicted ATPase, archaeal AAA+ ATPase superfamily|1|0|0|0|0|0|1|1|1|
47+
|`func_598325c18ceb`|Molybdopterin or thiamine biosynthesis adenylyltransferase (ThiF) (PDB:1ZUD) (PUBMED:32239579)|1|0|0|0|0|0|1|1|1|
48+
|`func_8ce5ff84aa42`|Predicted arabinose efflux permease AraJ, MFS family (AraJ) (PDB:4LDS)|1|1|1|1|1|1|1|1|1|
49+
|`func_300e2c6e37e4`|Glutamate dehydrogenase/leucine dehydrogenase (GdhA) (PDB:1B3B) (PUBMED:24391520)|1|1|1|1|1|1|1|1|1|
50+
|`func_a666bc87a03f`|Large-conductance mechanosensitive channel (MscL) (PDB:2OAR)|1|1|1|1|1|1|1|1|1|
51+
|`func_71379d89f0c6`|UTP pyrophosphatase, metal-dependent hydrolase family (YgjP) (PDB:4JIU) (PUBMED:27941785)|1|1|1|1|1|1|1|1|1|
52+
|`func_772efbb7cb2e`|Hemolysin-related protein, contains CBS domains, UPF0053 family (TlyC) (PDB:2NQW)|1|1|1|1|1|1|1|1|1|
53+
|`func_5a5bf9735c8a`|Carbonic anhydrase (CynT) (PDB:1EKJ) (PUBMED:22081392)|1|1|1|1|1|1|1|1|1|
54+
|`func_1ffc26d0034f`|Transposase (or an inactivated derivative) (IS285)|1|1|1|1|1|1|1|1|1|
55+
|`func_0631d9fecae6`|Phosphoenolpyruvate carboxylase (Ppc) (PDB:1FIY)|1|1|1|1|1|1|1|1|1|
56+
|`func_62068e09f9f3`|Chromosome segregation ATPase Smc (Smc) (PDB:5XG3)|1|1|1|0|0|0|1|1|1|
57+
|`func_f4b8779f6ad8`|Peptidoglycan-binding (PGRP) domain of peptidoglycan hydrolases (PGRP) (PDB:4FET)|1|0|0|0|0|0|0|0|0|
58+
|`func_700652d0a2dd`|Uncharacterized membrane protein YjjP, DUF1212 family (YjjP)|1|1|1|1|1|1|1|1|1|
5959
|(...)|(...)|(...)|(...)|(...)|(...)|(...)|(...)|(...)|(...)|(...)|
60+
61+
### Per-population copy number of functions across metagenomes
62+
63+
When the program that generates this file is run with the `--per-population-copy-number` flag, anvi'o additionally reports the per-population copy number (PPCN) of each function. In this file, the frequency of a function in a given metagenome is divided by the number of populations estimated to be present in that metagenome (which anvi'o estimates from single-copy core genes). This normalization makes the values comparable across metagenomes that describe communities of different sizes.
64+
65+
The layout is identical to the frequency file, except that the values are per-population copy numbers rather than raw counts. If anvi'o was unable to estimate the number of populations for a given metagenome, its values in this file will be reported as `NA`.

anvio/docs/programs/anvi-script-gen-function-matrix-across-genomes.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,33 @@ The command above will result in two files in your work directory, both of which
2020
* MY-GENOMES-FREQUENCY.txt
2121
* MY-GENOMES-PRESENCE-ABSENCE.txt
2222

23+
In each of these files, the first columns describe each function (a unique `key`, the function name, and optionally the function accession ids), and the remaining columns hold one value per genome.
24+
2325
{:.notice}
2426
You can always learn about which functions are in a given %(contigs-db)s using the program %(anvi-db-info)s.
2527

26-
Alternatively you can run it with a %(groups-txt)s that associates sets of genomes with distinct groups,
28+
## Per-population copy number normalization for metagenomic assemblies
29+
30+
If we want to get an idea of differences in functional capacity across different metagenomic assemblies (or long-read sequence metagenomes) but, for some good reasons, we do not have MAGs from these assemblies, or we want to make sure that we make use of all the sequence data we have and not only those reads that are used to reconstruct genomes, we can't just look at the distribution of functions across genomes, because all functions and metabolic pathways will most likely occur nearly everywhere, at least in one population, and population numbers may differ dramatically across samples, and just counting the occurrence of a given function would not provide ecologically meaningful insights. To overcome this, Iva Veseli introduced the [per-population copy number](https://elifesciences.org/reviewed-preprints/89862) normalization. Based on the same principle, adding the flag `--per-population-copy-number` allows you to normalize individual functional annotations within a metagenomic assembly using the SCG-based estimate of population numbers within the sample.
31+
32+
{{ codestart }}
33+
anvi-script-gen-function-matrix-across-genomes -e %(external-genomes)s \
34+
--annotation-source COG20_FUNCTION \
35+
--output-file-prefix MY-METAGENOMES \
36+
--per-population-copy-number
37+
{{ codestop }}
38+
39+
Adding the flag generates an additional output file of type %(functions-across-genomes-txt)s:
40+
41+
* MY-METAGENOMES-PER-POPULATION-COPY-NUMBER.txt
42+
43+
By dividing the frequency of each function in a given metagenomic assembly by the number of populations estimated to be present in that same assembly based on counts of single-copy core genes (SCGs) in each %(contigs-db)s: for each domain-specific SCG set, anvi'o takes the mode of the number of hits across all SCGs, and sums these per-domain estimates across Bacteria, Archaea, and Eukarya.
44+
45+
The script relies on SCG annotations generated by %(anvi-run-hmms)s, and you need an %(external-genomes)s file to let it know which assemblies to compare.
46+
47+
A per-population copy number is an *average number of copies of a function per population*: a value near 1 means the function tends to occur once in every population, a value below 1 means it is present in only a fraction of the populations, and a value above 1 means it tends to occur in multiple copies per population (as is common for, e.g., transposases). If a given metagenome has single-copy core genes that are too sparse to yield a reliable estimate, anvi'o cannot compute its number of populations, and the per-population copy number values for that assembly will be reported as `NA`.
48+
49+
Alternatively, you can run it with a %(groups-txt)s that associates sets of genomes with distinct groups,
2750

2851
{{ codestart }}
2952
anvi-script-gen-function-matrix-across-genomes -i %(internal-genomes)s \

0 commit comments

Comments
 (0)