2-Site SFS

2-Site SFS#

The two-site (or two-locus) SFS summarises pairs of linked sites: its entry \((i, j)\) counts pairs of sites, separated by up to d base pairs, at which one site carries \(i\) and the other \(j\) derived alleles. Its departure from the outer product of the one-dimensional spectrum reflects linkage and the shape of the underlying genealogies, which makes it a sensitive probe of departures from the standard (Kingman) coalescent, such as the multiple mergers produced by strong skew in reproductive success (see Fenton et al. [2]).

Setting two_sfs to True makes the Parser pair sites within d base pairs. As with the other parsing modes, parse() returns a TwoSpectra collection keyed by type; without stratifications the single unstratified two-SFS is reached as ["all"], a square TwoSFS. The example uses a synthetic all-sites dataset, monomorphic sites included, simulated under the standard Kingman coalescent.

import sfsutils as su

two_sfs = su.Parser(
    source="resources/msprime/two_sfs_kingman.all.vcf.gz",
    n=10, two_sfs=True, d=1000
).parse()["all"]
INFO:Parser: Using stratification: [all].
INFO:Parser: Loading VCF file
Parser>Counting sites: 600000it [00:00, 4724077.84it/s]
Parser>Processing sites: 100%|██████████| 600000/600000 [03:53<00:00, 2574.77it/s]
INFO:PolyAllelicFiltration: Filtered out 0 sites.
INFO:Parser: Included 600000 out of 600000 sites in total from the input.
INFO:Parser: Counted 299700000 site pairs within 1000 bp.

The raw two-site SFS is the symmetric matrix of pair counts, shown here as a heatmap with the monomorphic rows and columns omitted.

two_sfs.plot(title="pair counts");
../../_images/ee457c3d1674b117e35b82e406c87aaaefe45d9be85ee1632c78916b86159615.png

Beyond the raw pair counts, cov() and corr() give the class-resolved branch-length covariance and correlation \(\mathrm{Cov}(L_i, L_j)\) of the two linked sites, the deviation of the joint class distribution from independence. Because they normalise over the whole spectrum so that the marginal is the site-frequency spectrum, they require the monomorphic sites: parse an all-sites input.

two_sfs.corr().plot(title="correlation");
../../_images/12cc71e8a256272defce9abf845f5b0a07a3a7e456cbac1b35570ae22b0adeae.png

The spectrum can also be folded when the ancestral state is unknown, and the diagonals masked, as for any TwoSFS.

two_sfs.fold().plot(title="folded");
../../_images/a8c4d8d4d7f882da785cf45ce6f8e0906f0fb66650e9acd99409b68f19833fdc.png

Detecting multiple mergers (MMCs)#

Under the Kingman coalescent, mutations at different low frequencies are negatively correlated [3]. Multiple-merger genealogies instead generate positive associations between low-frequency mutations [1], the opposite sign. To see the contrast, we parse a second synthetic all-sites dataset, simulated under a Beta(1.3) multiple-merger coalescent, and compare its correlation with the Kingman one.

beta = su.Parser(
    source="resources/msprime/two_sfs_beta.all.vcf.gz",
    n=10, two_sfs=True, d=1000
).parse()["all"]
INFO:Parser: Using stratification: [all].
INFO:Parser: Loading VCF file
Parser>Counting sites: 600000it [00:00, 4429316.13it/s]
Parser>Processing sites: 100%|██████████| 600000/600000 [04:02<00:00, 2471.72it/s]
INFO:PolyAllelicFiltration: Filtered out 0 sites.
INFO:Parser: Included 600000 out of 600000 sites in total from the input.
INFO:Parser: Counted 299700000 site pairs within 1000 bp.
fig, axes = plt.subplots(1, 2, figsize=(9, 3.6))
two_sfs.corr().plot(ax=axes[0], title="Kingman", show=False)
beta.corr().plot(ax=axes[1], title="Beta(1.3)", show=False)
fig.tight_layout();
../../_images/e9a0a9d7881311bd52dc9c908041d0dbd30cf40318c89e6c84121f044537050c.png

The contrast in the low-frequency block, negative under Kingman and positive under the multiple-merger coalescent, is the signal that two-site spectra exploit to detect departures from Kingman coalescence.

Because cov() and corr() normalise over the full spectrum so that the marginal is the site-frequency spectrum, they require the real monomorphic sites of an all-sites input; a polymorphic-only (SNP) spectrum leaves them undefined and raises. The ratio statistic fpmi() needs no monomorphic sites and remains available for polymorphic-only data. The spectrum can also be folded when the ancestral state is unknown, and the diagonals masked, as for any TwoSFS.