Package: cnv-from-bam
CNV From BAMcnv_from_bamis a Rust library developed to efficiently calculate dynamic Copy Number Variation (CNV) profiles from sequence alignments contained in BAM files. It seamlessly integrates with Python using PyO3, making it an excellent choice for bioinformatics workflows involving genomic data analysis.FeaturesEfficient Processing: Optimized for handling large genomic datasets in BAM format.Python Integration: Built with PyO3 for easy integration into Python-based genomic analysis workflows.Multithreading Support: Utilizes Rust's powerful concurrency model for improved performance.Dynamic Binning: Bins the genome dynamically based on total read counts and genome length.CNV Calculation: Accurately calculates CNV values for each bin across different contigs.Directory Support: Supports processing of multiple BAM files in a directory. (Requires alignment to the same reference in all BAM files)InstallationTo usecnv_from_bamin your Rust project, add the following to yourCargo.tomlfile:[dependencies]cnv_from_bam="0.1.0"# Replace with the latest versionUsageHere's a quick example of how to use theiterate_bam_filefunction:usecnv_from_bam::iterate_bam_file;usestd::path::PathBuf;letbam_path=PathBuf::from("path/to/bam/file.bam");// Iterate over the BAM file and calculate CNV values for each bin. Number of threads is set to 4 and mapping quality filter is set to 60.// If number of threads is not specified, it defaults to the number of logical cores on the machine.letresult=iterate_bam_file(bam_path,Some(4),Some(60),None,None);// Process the result...The results in this case are returned as a CnvResult, which has the following structure:/// Results struct for python#[pyclass]
#[derive(Debug)]
pub struct CnvResult {
    /// The CNV per contig
    #[pyo3(get)]
    pub cnv: PyObject,
    /// Bin width
    #[pyo3(get)]
    pub bin_width: usize,
    /// Genome length
    #[pyo3(get)]
    pub genome_length: usize,
    /// Variance of the whole genome
    #[pyo3(get)]
    pub variance: f64,
}Whereresult.cnvis a Python dictPyObjectcontaining the Copy Number for each bin ofbin_widthbases for each contig in the reference genome,result.bin_widthis the width of the bins in bases,result.genome_lengthis the total length of the genome andresult.varianceis a measure of the variance across the whole genome.Variance is calculated as the average of the squared differences from the Mean.[!NOTE]Note: Only the main primary mapping alignment start is binned, Supplementary and Secondary alignments are ignored. Supplementary alignments can be included by settingexclude_supplementaryDirectory analysisTo analyse a directory of BAM files, use theiterate_bam_dirfunction:usecnv_from_bam::iterate_bam_dir;usestd::path::PathBuf;letbam_path=PathBuf::from("path/to/bam_directory/");// Iterate over the BAM files in teh directory and calculate CNV values for the whole. Number of threads is set to 4 and mapping quality filter is set to 60.// If number of threads is not specified, it defaults to the number of logical cores on the machine.letresult=iterate_bam_file(bam_path,Some(4),Some(60));This again returns a CnvResult, but this time the CNV values are summed across all BAM files in the directory. The bin width and genome length are calculated based on the first BAM file in the directory.[!NOTE]Note: All BAM files in the directory must be aligned to the same reference genome.Python Integrationcnv_from_bamcan be used in Python using the PyO3 bindings. To install the Python bindings, run:pip install cnv_from_bamThe sameiterate_bam_fileis availabl
e in python, accepting a path to a BAM file or a directory of BAM files, the number of threads (set toNoneto use the optimal number of threads for the machine), and the mapping quality filter.Example simple plot in python, you will needmatplotlibannumpyinstalled (pip install matplotlib numpy)frommatplotlibimportpyplotaspltimportmatplotlibasmplfrompathlibimportPathfromcnv_from_bamimportiterate_bam_fileimportnumpyasnpfig,ax=plt.subplots(nrows=1,ncols=1,figsize=(8,3))total=0bam_path=Path("path/to/bam/file.bam");# Iterate over the BAM file and calculate CNV values for each bin. Number of threads is set to 4 and mapping quality filter is set to 60.# If number of threads is not specified, it defaults to the optimal number of threads for the machine.result=iterate_bam_file(bam_path,_threads=4,mapq_filter=60);forcontig,cnvinresult.cnv.items():ax.scatter(x=np.arange(len(cnv))+total,y=cnv,s=0.1)total+=len(cnv)ax.set_ylim((0,8))ax.set_xlim((0,total))fig.savefig("Example_cnv_plot.png")Should look something like this. Obviously the cnv data is just a dictionary of lists, so you can do whatever you want with it vis a vis matplotlib, seaborn, etc.Iterative useIt is possible to iteratively add bam files to a continuing count. By passing a dictionary to iterate_bam_file, the intermediate mapping start counts are kept in this dictionary.This is limited to parsing files one at a time, rather than by directory.fromcnv_from_bamimportiterate_bam_fileupdate={}bam_path=Path("path/to/bam/file.bam");result=iterate_bam_file(bam_path,_threads=4,mapq_filter=60,copy_numbers=update);bam_path_2=Path("path/to/bam/file2.bam");result=iterate_bam_file(bam_path_2,_threads=4,mapq_filter=60,copy_numbers=update);# Result now contains the copy number as inferred by both BAMSOutputThis is new in version >= 0.3. If you just want raw stdout from rust and no faffing with loggers, use v0.2.Progress BarBy default, a progress bar is displayed, showing the progress of the iteration of each BAM file. To disable the progress bar, set theCIenvironment variable to1in your python script:importosos.environ["CI"]="1"LoggingWe use thelogcrate for logging. By default, the log level is set toINFO, which means that the program will output the progress of the iteration of each BAM file. To disable all but warning and error logging, set the log level toWARNon theiterate_bam_filefunction:importloggingfromcnv_from_bamimportiterate_bam_fileiterate_bam_file(bam_path,_threads=4,mapq_filter=60,log_level=int(logging.WARN))getLevelNameis a function from theloggingmodule that converts the log level to the integer value of the level. These values areLevelValueCRITICAL50ERROR40WARNING30INFO20DEBUG10NOTSET0[!NOTE]
In v0.3 a regression was introduced, whereby keeping the GIL for logging meant that BAM reading was suddenly single threaded again. Whilst it was possible to fix this and keepPyO3-log, I decided to go for truly maximum speed instead. The only drawback to the removal ofPyO3-login (v0.4+) is that log messages will not be handled by python loggers, so they won't be written out by a file handler, for example.DocumentationTo generate the documentation, run:cargodoc--openContributingContributions tocnv_from_bamare welcome!We use pre-commit hooks (particularlycargo-fmtandruff) to ensure that code is formatted correctly and passes all tests before being committed. To install the pre-commit hooks, run:gitclonehttps://github.com/Adoni5/cnv_from_bam.gitcdcnv_from_bam
pipinstall-e.[dev]pre-commitinstall-tpre-commit-tpost-checkout-tpost-merge
pre-commitrun--all-filesChangelogv0.4.3Iterative useIt is possible to iteratively add bam files to a continuing count. By passing a dictionary to iterate_bam_file, the intermediate mapping start counts are kept in this dictionary.
This is limited to parsing files one at a time, rather than by directory.
See example above underiterative use.Catches bug where metadata (mapped and unmapped count) for a reference sequence in a BAI or CSI file would return None, and crash the calculation. As this
was used to calculate the Progress bar total, just skips the offending reference sequence, returning - for both counts. May mean the progress bar can have a lower total than number of reads, but won't matter to final numbers.Adds Cargo tests to CIv0.4.2Returns the contig names naturally sorted, rather than in random order!! For examplechr1, chr2, chr3...chr22,chrM,chrX,chrY!
Huge, will prevent some people getting repeatedly confused about expected CNV vs. Visualised and wasting an hour debugging a non existing issue.Returns variance across the whole genome in the CNV result struct.v0.4.1Addexclude_supplementaryparameter toiterate_bam_file, to exclude supplementary alignments (default True)v0.4.0RemovePyO3-logfor maximum speed. This means that log messages will not be handled by python loggers. Can set log level on call toiterate_bam_filev0.3.0IntroducePyO3-logfor logging. This means that log messages can be handled by python loggers, so they can be written out by a file handler, for example.HAS A LARGE PERFORMANCE ISSUECan disable progress bar display by settingCIenvironment variable to1in python script.v0.2.0Purely rust based BAM parsing, using noodles.Uses a much more sensible number for threading if not provided.Allows iteration of BAMS in a directoryv0.1.0Initial releaseUsesrust-bio/rust-htslibfor BAM parsing. Has to bind C code, is a faff.LicenseThis project is licensed under theMozilla Public License 2.0.