<p>I wrote a package called sinto (<a href="https://github.com/timoast/sinto" rel="nofollow noreferrer">https://github.com/timoast/sinto</a>) that contains a function to split a BAM file into different files based on the cell barcodes.</p>
<p>The <a href="https://timoast.github.io/sinto/installation.html" rel="nofollow noreferrer">installation</a> can be done via anaconda:</p>
<pre><code>conda create -p conda/envs/sinto python=3.8 sinto
</code></pre>
<p>And a <a href="https://timoast.github.io/sinto/basic_usage.html#filter-cell-barcodes-from-bam-file" rel="nofollow noreferrer">usage example from the documentation</a> is:</p>
<pre><code>sinto filterbarcodes -b my_bam.bam -c my_barcodes.txt --barcodetag &quot;CB&quot;
</code></pre>
<p>Where the <code>my_barcodes.txt</code> file contains cluster names in a tab separated list format:</p>
<pre><code>TGGCAATGTTGAAGCG-1  A
GACCAATCACCATTCC-1  A
CAGGATTCAGAACTTC-1  B
GAACCTAAGAGAGGTA-1  B,A
..
</code></pre>

CC BY-SA 4.0
2020-04-10T16:11:50.047Z
2022-08-09T22:45:04.413Z
2022-08-09T22:45:04.413Z
TimStuart
<p>I wrote a <a href="https://gist.github.com/winni2k/978b33d62fee5e3484ec757de1a00412" rel="nofollow noreferrer">python script</a> based on pysam that is free for anyone to use:</p>

<pre class="lang-py prettyprint-override"><code>"""
MIT License
Copyright (c) 2020 Warren W. Kretzschmar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from pathlib import Path

import pysam


class BamWriter:
    def __init__(self, alignment, barcodes, prefix):
        self.alignment = alignment
        self.prefix = prefix
        self.barcodes = set(barcodes)
        self._out_files = {}

    def write_record_to_barcode(self, rec, barcode):
        if barcode not in self.barcodes:
            return
        if barcode not in self._out_files:
            self._open_file_for_barcode(barcode)
        self._out_files[barcode].write(rec)

    def _open_file_for_barcode(self, barcode):
        self._out_files[barcode] = pysam.AlignmentFile(
            f"{self.prefix}_{barcode}.bam", "wb", template=self.alignment
        )


def main(input_bam, barcodes_file, output_prefix, contigs):
    """Split a 10x barcoded sequencing file into barcode-specific BAMs

    input:
    barcodes_file: a file containing barcodes or a single barcode
    contigs: '.' for all contigs, 'chr1' for the contig 'chr1',
    or '1-5' for chromosomes 1, 2, 3, 4, and 5
    """
    alignment = pysam.AlignmentFile(input_bam)
    if Path(barcodes_file).is_file():
        with open(barcodes_file, "r") as fh:
            barcodes = [l.rstrip() for l in fh.readlines()]
    else:
        barcodes = [barcodes_file]
        print(f"Extracting single barcode: {barcodes}")
    writer = BamWriter(alignment=alignment, barcodes=barcodes, prefix=output_prefix)
    if contigs == ".":
        print("Extracting reads from all contigs")
        recs = alignment.fetch()
    else:
        if "-" in contigs:
            start, end = contigs.split("-")
            print(f"Extracting reads from contigs {start} to {end}")
            recs = (alignment.fetch(str(contig)) for contig in range(start, end + 1))
        elif "," in contigs:
            contigs = contigs.split(",")
            print(f"Extracting reads from contigs {contigs}")
            recs = (alignment.fetch(str(contig)) for contig in contigs)
        else:
            print("Extracting reads for one contig: {contigs}")
            recs = (alignment.fetch(c) for c in [contigs])

    for rec in recs:
        try:
            barcode = rec.get_tag("CB")
            writer.write_record_to_barcode(rec=rec, barcode=barcode)
        except KeyError:
            pass


if __name__ == "__main__":
    import sys

    main(
        input_bam=sys.argv[1],
        barcodes_file=sys.argv[2],
        output_prefix=sys.argv[3],
        contigs=sys.argv[4],
    )
<span class="math-container">```</span>
</code></pre>

CC BY-SA 4.0
2020-04-07T18:44:39.463Z
2020-04-08T09:03:09.250Z
2020-04-08T09:03:09.250Z
winni2k
<p>You can do it <a href="https://kb.10xgenomics.com/hc/en-us/articles/360022448251-How-to-filter-the-BAM-file-produced-by-10x-pipelines-with-a-list-of-barcodes-" rel="nofollow noreferrer">with samtools</a>:</p>
<pre><code># Save the header lines
samtools view -H $BAM_FILE &gt; SAM_header

# Filter alignments using filter.txt. Use LC_ALL=C to set C locale instead of UTF-8
samtools view $BAM_FILE | LC_ALL=C grep -F -f filter.txt &gt; filtered_SAM_body

# Combine header and body
cat SAM_header filtered_SAM_body &gt; filtered.sam

# Convert filtered.sam to BAM format
samtools view -b filtered.sam &gt; filtered.bam
</code></pre>
<p>And 10x Genomics has developed <a href="https://github.com/10XGenomics/subset-bam" rel="nofollow noreferrer">subset-bam</a> a tool that can split a BAM file by barcode.</p>

CC BY-SA 4.0
2022-07-12T00:45:19.410Z
2022-07-12T00:45:19.410Z
Tapper
<p>Hi here have an answer by 10X: <a href="https://kb.10xgenomics.com/hc/en-us/articles/360022448251-How-to-filter-the-BAM-file-produced-by-10x-pipelines-with-a-list-of-barcodes" rel="nofollow noreferrer">https://kb.10xgenomics.com/hc/en-us/articles/360022448251-How-to-filter-the-BAM-file-produced-by-10x-pipelines-with-a-list-of-barcodes</a></p>

CC BY-SA 4.0
2024-12-02T22:03:33.527Z
Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer).
Community
CC BY-SA 4.0
2024-12-02T21:58:55.287Z
2024-12-02T21:58:55.287Z
siqi zhao
<p>If you have access to a lot of cores:</p>
<p>10x has <a href="https://github.com/10XGenomics/subset-bam" rel="nofollow noreferrer">subset-bam</a> a pretty fast way to pull out all the cells with a specific CB/BX if you only need a small number.</p>

CC BY-SA 4.0
2022-07-12T09:34:09.520Z
2022-07-12T09:34:09.520Z
story
<p>I have a BAM file from a single cell sequencing experiment. Each read has had the cell barcode annotated in the <code>CB</code> tag. Some reads do not have a <code>CB</code> tag. For each of an arbitrary set of barcodes, I would like to split out the reads for each of those barcodes into a separate file. Is there a tool that does this?</p>

<sam><single-cell><data-preprocessing>
Is there a command line tool to split a SAM/BAM file by CB (cell barcode) tag?
# Is there a command line tool to split a SAM/BAM file by CB (cell barcode) tag?

(Asked by: username_0 on 2020-04-07)

I have a BAM file from a single cell sequencing experiment. Each read has had the cell barcode annotated in the `CB` tag. Some reads do not have a `CB` tag. For each of an arbitrary set of barcodes, I would like to split out the reads for each of those barcodes into a separate file. Is there a tool that does this?

**Tags:** sam, single-cell, data-preprocessing

---

## 5 answers

### Answer by username_1 on 2020-04-10. Score: 2 | Accepted answer

I wrote a package called sinto (https://github.com/timoast/sinto) that contains a function to split a BAM file into different files based on the cell barcodes.
The installation can be done via anaconda:
conda create -p conda/envs/sinto python=3.8 sinto

And a usage example from the documentation is:
sinto filterbarcodes -b my_bam.bam -c my_barcodes.txt --barcodetag "CB"

Where the my_barcodes.txt file contains cluster names in a tab separated list format:
TGGCAATGTTGAAGCG-1  A
GACCAATCACCATTCC-1  A
CAGGATTCAGAACTTC-1  B
GAACCTAAGAGAGGTA-1  B,A
..


---

### Answer by username_0 on 2020-04-07. Score: 4

I wrote a python script based on pysam that is free for anyone to use:
"""
MIT License
Copyright (c) 2020 Warren W. Kretzschmar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from pathlib import Path

import pysam


class BamWriter:
    def __init__(self, alignment, barcodes, prefix):
        self.alignment = alignment
        self.prefix = prefix
        self.barcodes = set(barcodes)
        self._out_files = {}

    def write_record_to_barcode(self, rec, barcode):
        if barcode not in self.barcodes:
            return
        if barcode not in self._out_files:
            self._open_file_for_barcode(barcode)
        self._out_files[barcode].write(rec)

    def _open_file_for_barcode(self, barcode):
        self._out_files[barcode] = pysam.AlignmentFile(
            f"{self.prefix}_{barcode}.bam", "wb", template=self.alignment
        )


def main(input_bam, barcodes_file, output_prefix, contigs):
    """Split a 10x barcoded sequencing file into barcode-specific BAMs

    input:
    barcodes_file: a file containing barcodes or a single barcode
    contigs: '.' for all contigs, 'chr1' for the contig 'chr1',
    or '1-5' for chromosomes 1, 2, 3, 4, and 5
    """
    alignment = pysam.AlignmentFile(input_bam)
    if Path(barcodes_file).is_file():
        with open(barcodes_file, "r") as fh:
            barcodes = [l.rstrip() for l in fh.readlines()]
    else:
        barcodes = [barcodes_file]
        print(f"Extracting single barcode: {barcodes}")
    writer = BamWriter(alignment=alignment, barcodes=barcodes, prefix=output_prefix)
    if contigs == ".":
        print("Extracting reads from all contigs")
        recs = alignment.fetch()
    else:
        if "-" in contigs:
            start, end = contigs.split("-")
            print(f"Extracting reads from contigs {start} to {end}")
            recs = (alignment.fetch(str(contig)) for contig in range(start, end + 1))
        elif "," in contigs:
            contigs = contigs.split(",")
            print(f"Extracting reads from contigs {contigs}")
            recs = (alignment.fetch(str(contig)) for contig in contigs)
        else:
            print("Extracting reads for one contig: {contigs}")
            recs = (alignment.fetch(c) for c in [contigs])

    for rec in recs:
        try:
            barcode = rec.get_tag("CB")
            writer.write_record_to_barcode(rec=rec, barcode=barcode)
        except KeyError:
            pass


if __name__ == "__main__":
    import sys

    main(
        input_bam=sys.argv[1],
        barcodes_file=sys.argv[2],
        output_prefix=sys.argv[3],
        contigs=sys.argv[4],
    )
```


---

### Answer by username_2 on 2022-07-12. Score: 0

You can do it with samtools:
# Save the header lines
samtools view -H $BAM_FILE > SAM_header

# Filter alignments using filter.txt. Use LC_ALL=C to set C locale instead of UTF-8
samtools view $BAM_FILE | LC_ALL=C grep -F -f filter.txt > filtered_SAM_body

# Combine header and body
cat SAM_header filtered_SAM_body > filtered.sam

# Convert filtered.sam to BAM format
samtools view -b filtered.sam > filtered.bam

And 10x Genomics has developed subset-bam a tool that can split a BAM file by barcode.


---

### Answer by username_3 on 2024-12-02. Score: 0

Hi here have an answer by 10X: <https://kb.10xgenomics.com/hc/en-us/articles/360022448251-How-to-filter-the-BAM-file-produced-by-10x-pipelines-with-a-list-of-barcodes>

> **Comments:**
> **username_4**: Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer).  

---

### Answer by username_5 on 2022-07-12. Score: 0

If you have access to a lot of cores:

10x has [subset-bam](https://github.com/10XGenomics/subset-bam) a pretty fast way to pull out all the cells with a specific CB/BX if you only need a small number.


---

