Parsing#
The Parser reads variants into a site-frequency spectrum. When the input contains no monomorphic sites, a TargetSiteCounter recovers the number of mutational target sites, so that the monomorphic bins, and the overall scale of the spectrum, are correct. Sites can be split into categories with a Stratification (see Site stratification).
Parse site-frequency spectra from VCF files, VCF-Zarr stores, or tskit tree sequences. |
|
Class for counting the number of target sites when parsing an input that does not contain monomorphic sites. |
Parser#
- class Parser(source: str | os.PathLike | 'tskit.TreeSequence' | VariantReader | None = None, n: int | Dict[str, int] | List[int] = None, pops: Dict[str, List[str]] | None = None, gff: str | None = None, fasta: str | None = None, info_ancestral: str = 'AA', info_ancestral_prob: str = 'AA_prob', skip_non_polarized: bool = True, stratifications: List[Stratification] = [], annotations: List[Annotation] = [], filtrations: List[Filtration] = None, include_samples: List[str] = None, exclude_samples: List[str] = None, max_sites: int = inf, seed: int | None = 0, cache: bool = True, aliases: Dict[str, List[str]] = {}, target_site_counter: TargetSiteCounter = None, subsample_mode: Literal['random', 'probabilistic'] = 'probabilistic', polarize_probabilistically: bool = False, two_sfs: bool = False, d: int = 1000, two_sfs_offset: int = 0, vcf: str | os.PathLike | 'tskit.TreeSequence' | VariantReader | None = None)[source]#
Bases:
MultiHandlerParse site-frequency spectra from VCF files, VCF-Zarr stores, or tskit tree sequences.
By default, the parser looks at the
AAtag in the VCF file’s info field to retrieve the correct polarization. Polymorphic sites for which this tag is not well-defined are by default ignored (seeskip_non_polarized).This class also offers on-the-fly annotation of the sites such as site degeneracy and ancestral allele state. This is done by providing a list of annotations to the parser which are applied in the order they are provided.
The parser also allows to filter sites based on site properties which is done by passing a list of filtrations. By default, we filter out poly-allelic sites as sites are assumed to be at most bi-allelic.
In addition, the parser allows to stratify the SFS by providing a list of stratifications. This is useful to obtain the SFS separately for different types of sites.
To correctly determine the number of target sites when parsing an input that does not contain monomorphic sites, we can use a
TargetSiteCounter. This class is used in conjunction with the parser and samples sites from the given FASTA file that are found in between variants on the same contig that were parsed from the input.Note that we assume the sites in the input to be sorted by position in ascending order (per contig).
Example usage:
import sfsutils as su # Parse selected and neutral SFS from human chromosome 1. p = su.Parser( source="https://ngs.sanger.ac.uk/production/hgdp/hgdp_wgs.20190516/" "hgdp_wgs.20190516.full.chr21.vcf.gz", fasta="http://ftp.ensembl.org/pub/release-109/fasta/homo_sapiens/" "dna/Homo_sapiens.GRCh38.dna.chromosome.21.fa.gz", gff="http://ftp.ensembl.org/pub/release-109/gff3/homo_sapiens/" "Homo_sapiens.GRCh38.109.chromosome.21.gff3.gz", aliases=dict(chr21=['21']), # mapping for contig names n=10, # SFS sample size # we use a target site counter to infer the number of target sites. target_site_counter=su.TargetSiteCounter( n_samples=1000000, # determine number of target sites by looking at total length of coding sequences n_target_sites=su.Annotation.count_target_sites( "http://ftp.ensembl.org/pub/release-109/gff3/homo_sapiens/" "Homo_sapiens.GRCh38.109.chromosome.21.gff3.gz" )['21'] ), # add degeneracy annotation for sites annotations=[ su.DegeneracyAnnotation() ], filtrations=[ # exclude non-SNPs as we infer monomorphic sites with target site counter su.SNPFiltration(), # filter out sites not in coding sequences su.CodingSequenceFiltration() ], # stratify by 4-fold/0-fold degeneracy stratifications=[su.DegeneracyStratification()], info_ancestral='AA_ensembl' ) sfs = p.parse() sfs.plot()
Methods:
Initialize the parser.
Count the number of sites in the source.
Download a file from a URL.
Download the file if it is a URL.
Get all aliases for the given contig alias including the primary alias.
Get the contig from the FASTA file.
Get the names of the contigs in the FASTA file.
Return the file extension of a URL.
Return a progress bar for the number of sites.
Return a truncated SHA1 hash of a string.
Check if the given path is a URL.
Load a FASTA file into a dictionary.
Open a VCF file for streaming.
Remove overlapping coding sequences.
If the given file is gzipped, unzip it and return the path to the unzipped file.
Parse the site-frequency spectrum from the configured source (VCF, VCF-Zarr, or tree sequence).
- __init__(source: str | os.PathLike | 'tskit.TreeSequence' | VariantReader | None = None, n: int | Dict[str, int] | List[int] = None, pops: Dict[str, List[str]] | None = None, gff: str | None = None, fasta: str | None = None, info_ancestral: str = 'AA', info_ancestral_prob: str = 'AA_prob', skip_non_polarized: bool = True, stratifications: List[Stratification] = [], annotations: List[Annotation] = [], filtrations: List[Filtration] = None, include_samples: List[str] = None, exclude_samples: List[str] = None, max_sites: int = inf, seed: int | None = 0, cache: bool = True, aliases: Dict[str, List[str]] = {}, target_site_counter: TargetSiteCounter = None, subsample_mode: Literal['random', 'probabilistic'] = 'probabilistic', polarize_probabilistically: bool = False, two_sfs: bool = False, d: int = 1000, two_sfs_offset: int = 0, vcf: str | os.PathLike | 'tskit.TreeSequence' | VariantReader | None = None)[source]#
Initialize the parser.
- Parameters:
source (str | os.PathLike | ‘tskit.TreeSequence’ | VariantReader | None) – The variant source: a path to a VCF file (gzipped or a URL), a path to a VCF-Zarr store (a
.vczor.zarrdirectory), a tskit tree sequence (a.treesfile or an in-memorytskit.TreeSequence), or a pre-builtVariantReader. A non-path, non-tree-sequence source must be aVariantReader(it exposes the sample names, contig names and site count the parser needs, and supports a fresh iteration pass); a bare iterable of sites or a rawcyvcf2.VCFobject is not accepted and raises aTypeError. VCF-Zarr requires the optionalzarrpackage and tree sequences the optionaltskitpackage.gff (
str|None) – The path to the GFF file, possibly gzipped or a URL. This file is optional and depends on the stratifications, annotations and filtrations that are used.fasta (
str|None) – The path to the FASTA file, possibly gzipped or a URL. This file is optional and depends on the annotations and filtrations that are used.n (
Union[int,Dict[str,int],List[int]]) – The size of the resulting SFS. We down-sample to this number by drawing without replacement from the set of all available genotypes per site. Sites with fewer thanngenotypes are skipped. For a joint (multi-population) SFS (seepops), this is the per-population sample size, given either as a singleintapplied to every population, a list aligned withpops(in insertion order), or a dictionary keyed by population name.pops (
Optional[Dict[str,List[str]]]) – Mapping of population name to the list of sample names making up that population. When given,parse()returns aJointSpectraholding the joint SFS across these populations (oneJointSFSper stratification type) instead of the one-dimensionalSpectra. The joint SFS is obtained by down-projecting each population independently and accumulating the outer product of the per-population projections, which is the exact hypergeometric down-projection under independent sampling within populations. WhenNone(default), a single-population, one-dimensional SFS is parsed as before. Note thatpopssupersedesinclude_samples/exclude_samples. Atarget_site_counteris supported: monomorphic sites map to the all-ancestral origin of the joint SFS, which is scaled to the target-site count.info_ancestral (
str) – The tag in the INFO field that contains ancestral allele information. Consider using an ancestral allele annotation if this information is not available yet.info_ancestral_prob (
str) – The tag in the INFO field that holds the per-site probability thatinfo_ancestralnames the true ancestral allele, used whenpolarize_probabilisticallyis set.skip_non_polarized (
bool) – Whether to skip poly-morphic sites that are not polarized, i.e., without a valid info tag providing the ancestral allele. IfFalse, we use the reference allele as ancestral allele (only recommended if working with folded spectra).stratifications (
List[Stratification]) – List of stratifications to use.annotations (
List[Annotation]) – List of annotations to use.filtrations (
List[Filtration]) – List of filtrations to use. By default, we usePolyAllelicFiltration.include_samples (
List[str]) – List of sample names to consider when determining the SFS. IfNone, all samples are used. Note that this restriction does not apply to the annotations and filtrations.exclude_samples (
List[str]) – List of sample names to exclude when determining the SFS. IfNone, no samples are excluded. Note that this restriction does not apply to the annotations and filtrations.max_sites (
int) – Maximum number of sites to parse from the input, which must be positive.seed (
int|None) – Seed for the random number generator. UseNonefor no seed.cache (
bool) – Whether to cache files downloaded from URLs.aliases (
Dict[str,List[str]]) – Dictionary of aliases for the contigs in the input, e.g.{'chr1': ['1']}. This is used to match the contig names in the input with the contig names in the FASTA file and GFF file.target_site_counter (
TargetSiteCounter) – The target site counter, used to recover the number of monomorphic sites when the input contains only polymorphic sites. IfNone, no target sites are added. It applies to the one-dimensional SFS (sampling monomorphic sites from the FASTA), the joint SFS (scaling the all-ancestral corner to the target-site count), and the unstratified two-SFS (extrapolating the monomorphic-involving pairs from the target-site count, which makes the resulting cov/corr only approximate).subsample_mode (
Literal['random','probabilistic']) – The subsampling mode. Forrandom, we draw once without replacement from the set of all available genotypes per site. Forprobabilistic, we add up the hypergeometric distribution for all sites. This will produce a smoother SFS, especially when a small number of sites is considered.polarize_probabilistically (
bool) – Whether to probabilistically polarize sites. In addition to theAAtag (seeinfo_ancestral), we use theAA_probtag (seeinfo_ancestral_prob) to polarize sites probabilistically. For example, if the ancestral allele isAwith a probability of 0.8 and the derived allele isG, we assign 0.8 probability mass to the ancestral allele and 0.2 to the derived allele. This should enhance accuracy, especially for small datasets. Whenever the ancestral probability tag is not present, we assume a probability of 1 for the ancestral allele.two_sfs (
bool) – Whether to parse the two-dimensional (two-site) SFS instead of the ordinary SFS. WhenTrue,parse()returns a squareTwoSFSwhose entry(i, j)counts pairs of sites, on the same contig and within the distance window of one another, where one site hasiand the otherjderived alleles (down-projected ton). As for the one-dimensional SFS, monomorphic sites are retained (contributing to the zero-frequency row and column); add aSNPFiltrationto restrict the spectrum to segregating sites. Each site’s contribution is the outer product of the two per-site down-projection vectors, so it is exact whensubsample_mode='random'at the full sample size and smoother under'probabilistic'. The matrix is symmetrized. Not compatible withpops. Stratifications are supported (counting only within-stratum pairs, into aTwoSpectra). The unstratified two-SFS accepts atarget_site_counter, which extrapolates the monomorphic-involving pairs from the target-site count; the resultingcov()/corr()are then only approximate, so a real all-sites input (monomorphic sites retained) is preferred for an accurate covariance, whilefpmi()needs no monomorphic sites.d (
int) – The width (in base pairs) of the distance window over which the two sites of a pair are separated whentwo_sfs=True. Together withtwo_sfs_offsetit defines the window(two_sfs_offset, two_sfs_offset + d]; with the defaulttwo_sfs_offset=0this is simply the maximum separation. Restricting the window restricts the spectrum to (approximately) linked pairs.two_sfs_offset (
int) – The minimum genomic distance (in base pairs, exclusive) between the two sites of a pair whentwo_sfs=True; pairs are formed for separations in(two_sfs_offset, two_sfs_offset + d]. Defaults to0(pairs at any separation up tod).vcf (str | os.PathLike | ‘tskit.TreeSequence’ | VariantReader | None) – Deprecated alias for
source, kept for backward compatibility. Provide eithersourceorvcf, not both.
- target_site_counter: TargetSiteCounter | None#
The target site counter
- pops: Dict[str, List[str]] | None#
Mapping of population name to sample names for the joint SFS, or
Nonefor a single-population SFS
- skip_non_polarized: bool#
Whether to skip sites that are not polarized, i.e., without a valid info tag providing the ancestral allele
- stratifications: List[Stratification]#
List of stratifications to use
- annotations: List[Annotation]#
List of annotations to use
- filtrations: List[Filtration]#
List of filtrations to use
- n_no_ancestral: int#
The number of sites that were skipped because they had no valid ancestral allele
- sfs: Dict[str, np.ndarray]#
Dictionary of SFS indexed by (stratification) type. Each value is a 1-D array of length
n + 1for a single-population SFS, or a joint SFS array of shape_joint_shapewhenpopsis given.
- subsample_mode: Literal['random', 'probabilistic']#
The subsampling mode
- two_sfs_offset: int#
Minimum genomic distance (bp, exclusive) between the two sites of a pair for the two-SFS
- property contig_lengths: Dict[str, int] | None#
The declared length (bp) of each contig of the source: the
##contigheaders of a VCF, thecontig_lengtharray of a VCF-Zarr store, or the length of the genome of a tree sequence. Only the contigs whose length is known are present, and a source declaring none givesNone. The span of the observed variants understates the region on a sparsely covered contig, so this is what a spectrum extrapolating monomorphic sites should be sized against.- Returns:
The per-contig lengths, or
Nonewhere the source declares none.
- classmethod download_file(url: str, cache: bool = True, desc: str = 'Downloading file')#
Download a file from a URL.
- get_contig(aliases, rewind: bool = True, notify: bool = True)#
Get the contig from the FASTA file. The contig is looked up in the header index and read by seeking to it, so the sites may visit the contigs in any order at the same cost.
Note that
pyfaidxwould be more efficient here, but there were problems when running it in parallel.- Parameters:
- Return type:
SeqRecord- Returns:
The contig.
- Raises:
LookupError – Where the FASTA carries none of the aliases.
- get_contig_names()#
Get the names of the contigs in the FASTA file.
- static get_filename(url: str)#
Return the file extension of a URL.
- Parameters:
url (
str) – The URL to get the file extension from.- Returns:
The file extension.
- get_pbar(desc: str = 'Processing sites', total: int | None = 0)#
Return a progress bar for the number of sites.
- load_fasta(file: str)#
Load a FASTA file into a dictionary.
- Parameters:
file (
str) – The path to The FASTA file path, possibly gzipped or a URL- Return type:
FastaIterator- Returns:
Iterator over the sequences.
- load_vcf()#
Open a VCF file for streaming.
- Return type:
cyvcf2.VCF
- Returns:
The VCF reader.
- static remove_overlaps(df: DataFrame)#
Remove overlapping coding sequences.
- Parameters:
df (
DataFrame) – The coding sequences.- Return type:
DataFrame- Returns:
The coding sequences without overlaps.
- static unzip_if_zipped(file: str)#
If the given file is gzipped, unzip it and return the path to the unzipped file. If the file is not gzipped, return the path to the original file.
- Parameters:
file (
str) – The path to the file.- Returns:
The path to the unzipped file, or the original file if it was not gzipped.
- vcf#
The variant source (a path or a tskit TreeSequence object)
- rng#
Random generator instance
- gff#
The GFF file path
- aliases#
The contig mappings
- parse()[source]#
Parse the site-frequency spectrum from the configured source (VCF, VCF-Zarr, or tree sequence).
The return type is fixed by the parsing mode, and this mapping is a stable part of the API. Every mode returns a collection keyed by stratification type (a single
'all'key when no stratifications were given), so the return type is consistent across modes:one-dimensional SFS (the default): a
Spectraof one-dimensional spectra;multi-population (
popsgiven): aJointSpectraof joint spectra, keyed by (sorted) stratification type;two-SFS (
two_sfsset, with or without stratifications): aTwoSpectraof per-type two-SFS matrices. Without stratifications this holds the single'all'entry, so the two-SFS is reached asparse()['all'].
- Return type:
- Returns:
A
Spectra,JointSpectra, orTwoSpectraas described above.
TargetSiteCounter#
- class TargetSiteCounter(n_target_sites: int, n_samples: int = 100000)[source]#
Bases:
objectClass for counting the number of target sites when parsing an input that does not contain monomorphic sites. This class is used in conjunction with
Parserand samples sites from the given fasta file that are found in between variants on the same contig that were parsed from the input. Ideally, we obtain the SFS by parsing inputs that contain both mono- and polymorphic sites. This is because we need to know about the number of mutational opportunities for synonymous and non-synonymous sites which contain plenty of information on the strength of selection. It is recommended to use aSNPFiltrationwhen using this class to avoid biasing the result by monomorphic sites present in the input.Warning
This class is not compatible with stratifications based on info tags that are pre-defined in the input, as opposed to those added dynamically using the
annotationsargument of the parser. We also need to stratify mono-allelic sites which, in this case, won’t be present in the input so that they have no info tags when sampling from the FASTA file, and are thus ignored by the stratifications. However, using theannotationsargument of the parser, the info tags the stratifications are based on are added on-the-fly, also for monomorphic sites sampled from the FASTA file.Note
With the unstratified two-SFS (
two_sfs=True) this counter extrapolates the monomorphic-involving pairs from the target-site count. That anchors the marginal only approximately, so the resultingcov()/corrare approximate (a real all-sites input is preferred);fpmi()needs no monomorphic sites. The stratified two-SFS with a counter is not supported.Methods:
- __init__(n_target_sites: int, n_samples: int = 100000)[source]#
Initialize counter.
- Parameters:
n_target_sites (
int) – The total number of sites (mono- and polymorphic) that would be present in the input if it contained monomorphic sites. This number should be considerably larger than the number of polymorphic sites in the input. this value is not extremely important for downstream inference, the ratio of synonymous to non-synonymous sites being more informative, but the order of magnitude should be correct, in any case.n_samples (
int) – The number of sites to sample from the fasta file. Many sampled sites will not be valid as they are non-coding. To obtain good estimates, a few thousand sites should be sampled per type of site (depending on the stratifications used).