pybedtools
repository·master·Indexed 43 days ago
https://github.com/daler/pybedtoolsA Pythonic wrapper for the BEDTools suite that enables genomic interval manipulation and 'genome algebra' within Python workflows. It provides the BedTool class for wrapping BEDTools programs and the Interval class for high-performance genomic interval representation. Features include operator overloading for set operations (intersections, unions, and differences), feature-by-feature transformations via BedTool.each, and a contrib module for advanced analyses and plotting.
What's inside pybedtools
- pybedtools is a Python wrapper for the BEDTools suite of programs, which are used for genomic interval manipulation (genome algebra). It allows you to perform feature-level genomic manipulations directly within Python by wrapping all BEDTools programs and arguments. It supports all formats supported by BEDTools, including gzipped BED and GFF files.
Use pybedtools.contrib for advanced analyses
masterThe
pybedtools.contribmodule provides high-level tools for common genomic workflows, including plotting, bigWig/bigBed manipulation, and intersection matrices.# Examples of contrib modules: # - pybedtools.contrib.plotting: Track, TrackCollection, binary_heatmap, etc. # - pybedtools.contrib.bigwig: bam_to_bigwig, bedgraph_to_bigwig, wig_to_bigwig # - pybedtools.contrib.bigbed: bigbed, bigbed_to_bed # - pybedtools.contrib.IntersectionMatrix: For large-scale intersections # - pybedtools.contrib.venn_maker: For Venn diagrams # - pybedtools.contrib.long_range_interaction: For cis/trans interactionsWhat is a BedTool?
masterInpybedtools, aBedToolis the central abstraction. It represents a genomic interval file (such as BED, GFF, GTF, VCF, SAM, or BAM) and acts as a wrapper around the underlying BEDTools command-line programs. Most operations in the library are performed onBedToolobjects.Use default arguments for BEDTool methods
masterTo simplify usage,
pybedtoolsautomatically handles common BEDTools input flags (-i,-a, and-b):- Single Input (
-i): For methods like.merge(),pybedtoolsassumes the input file is the one the currentBedToolobject points to. You do not need to pass theiargument explicitly. - Two Inputs (
-aand-b): For methods like.intersect(), theBedToolobject itself is treated as-a, and the first non-keyword argument provided is treated as-b. - Argument Types: The first non-keyword argument for
-bcan be either a filename (string) or anotherBedToolobject.
Methods that use these defaults will include a note in their docstring: "For convenience, the file this BedTool object points to is passed as '-i'" or "This method accepts either a BedTool or a file name as the first unnamed argument".
# Default behavior: 'a' is used as input (-i) result1 = a.merge(d=100, s=True) # Explicit behavior (identical to above) result2 = a.merge(i=a.fn, d=100, s=True) # Two-input behavior: 'a' is -a, 'b' is -b result3 = a.intersect(b) # Using a filename or BedTool object for the second input result5 = a.intersect(b=b.fn) result6 = a.intersect(b=b)- Single Input (
Iterating over BAM BedTools
masterWhen you iterate over a file-basedBedToolthat points to a BAM file,pybedtoolsinternally callssamtools view. The resulting lines are passed to anIntervalIterator, which automatically detects the format (GFF, BED, or SAM) and yields intervals accordingly.Use BedTool objects as streaming iterators
masterTo save disk space and memory, you can use
BedToolobjects as streaming generators. Instead of loading all data into memory or writing to disk, data is created one line at a time.To enable streaming, pass
stream=Trueas a keyword argument to operations like.intersect().Warning: Streaming iterators are consumed when you read their features. For example, calling
len()on a streamingBedToolwill consume the iterator, making subsequent calls return 0.Note on Rendering: Because many BEDTools programs require the second input (
-b) to be a file,pybedtoolsmay automatically "render" a streamingBedToolto a temporary file if it is passed as a secondary argument in an operation.a = pybedtools.example_bedtool('a.bed') b = pybedtools.example_bedtool('b.bed') # Create a streaming BedTool c = a.intersect(b, stream=True) # len(c) consumes the iterator print(len(c)) # returns length print(len(c)) # returns 0Specify genomes for BEDTools operations
masterCertain
pybedtoolsmethods (likesloporshuffle) require genome information to prevent out-of-range coordinates. You can provide genome information using two different keyword arguments:g: Accepts a filename (string) or a dictionary.genome: Accepts an assembly name (string), a dictionary, or a genomepy genome name.
Key distinction: If you provide a string to
g, it is interpreted as a filename. If you provide a string togenome,pybedtoolswill look for it in the internalgenome_registry, attempt to find it viagenomepy, or automatically download it from UCSC.# Using 'g' with a dictionary a.slop(b=100, g={'chr1': (1, 10000)}) # Using 'genome' with an assembly name a.slop(b=100, genome='hg19')Handle non-interval output files
masterSome
BEDToolscommands (likegroupby) can produce output files that do not follow standard interval formats (e.g., BED, GFF, GTF, BAM, SAM, VCF).Limitations:
- You cannot use
.saveas()on these results becausepybedtoolscannot wrap an unsupported file format into a newBedToolobject. - You cannot iterate over these results directly (e.g.,
[i for i in d]) because the lines cannot be converted intoIntervalobjects.
Workarounds:
- To save the file: Use the
output='filename'keyword argument within the command that generates the non-interval data (e.g.,c.groupby(..., output='counts.txt')). Note: This behavior depends on the underlyingbedtoolsversion. - To read the data: Use standard Python file handling to iterate over the lines of the file using the
.fnattribute of theBedToolobject to get the filename.
# To iterate over lines of a non-interval file: for line in open(d.fn): featuretype, count = line.strip().split()- You cannot use
Coordinate systems and Interval.start normalization
masterDifferent file formats use different coordinate systems:
- BED files: 0-based, stop position is not included.
- GFF, GTF, and VCF files: 1-based, stop position is included.
pybedtoolsnormalizes these so you can treat allIntervalobjects identically:Interval.startis always 0-based: Even if the source is a 1-based GFF file,feature.startwill return the 0-based coordinate.len(interval)is always correct: It always returnsstop - start.Interval.fieldspreserves original format: Thefieldslist (and string indexing) always contains the original strings from the file. For a GFF file,feature[3]will return the 1-based string coordinate, whilefeature.startreturns the 0-based integer.
Summary Table:
Feature Interval.startInterval[index](fromfields)BED 0-based (int) 0-based (string) GFF/GTF/VCF 0-based (int) 1-based (string) # GFF (1-based) vs BED (0-based) comparison # GFF line: chr1, start 51, stop 300 gff = pybedtools.create_interval_from_list(["chr1", "fake", "mRNA", "51", "300", ".", "+", ".", "ID=mRNA1;"]) # BED line representing same region: chr1, start 50, stop 300 bed = pybedtools.create_interval_from_list(["chr1", "50", "300", "mRNA1", ".", "+"]) # Both have the same 0-based start attribute assert gff.start == bed.start # 50 # The GFF string field remains 1-based assert gff[3] == '51' # Length is consistent assert len(gff) == len(bed) # 250Understand default arguments in BedTool methods
masterWhen calling methods on a
BedToolobject (such as.intersect(),.subtract(), or.closest()),pybedtoolsautomatically assumes that the file associated with the currentBedToolinstance (stored in the.fnattribute) is the first input file (the-aargument inbedtoolscommand line terms).For methods that require a second file, the first unnamed argument passed to the method is automatically treated as the second input file (the
-bargument). If you pass anotherBedToolobject as that argument,pybedtoolswill automatically use its.fnattribute.>>> import pybedtools >>> exons = pybedtools.example_bedtool('a.bed') >>> snps = pybedtools.example_bedtool('b.bed') >>> # The following are all equivalent: >>> x1 = exons.intersect(snps) >>> x2 = exons.intersect(a=exons.fn, b=snps.fn) >>> x3 = exons.intersect(b=snps.fn) >>> x4 = exons.intersect(snps, a=exons.fn) >>> x1 == x2 == x3 == x4 TrueCoordinate system conventions in pybedtools
masterWhen working with
pybedtools, it is critical to understand how coordinate systems are handled to avoid off-by-one errors. The library uses two different approaches depending on the data type:- Integer values: All
Intervalobjects representing start/stop positions use 0-based coordinates regardless of the original file format. This ensures consistency across different genomic formats. - String values: When coordinates are represented as strings, they retain the format-specific convention (e.g., 1-based for GFF files and 0-based for BED files).
- Integer values: All
How BedTool.each differs from BedTool.filter and map
masterWhen transforming genomic data, choose between
each,filter, andmapbased on your desired output:BedTool.each(func): Use this when you want to modify the data within the intervals (e.g., updating the score field) and want the result to remain aBedToolobject for further processing (chaining).BedTool.filter(func): Use this when you want to subset the data. The function must returnTrueorFalse.map(func, bedtool_obj): Use the standard Pythonmapif you want to apply a function to each interval but do not need the result to be aBedToolobject (e.g., extracting a list of lengths).
# Returns a BedTool object (chainable) a.intersect(b).each(normalize_count) # Returns a BedTool object (subsetting) a.filter(lambda x: float(x[4]) < 1e-5) # Returns an iterator of the function results (not a BedTool) feature_lengths = map(len, a)