pysam

pysam は Bowtie や TopHat などのプログラムが出力する SAM/BAM フォーマットのファイルに対して読み込みや編集を行う Python モジュールである。ここでは pysam の使い方をほんの少しだけを取り上げる。このモジュールを利用することで柔軟にファイルを加工したりすることが可能である。詳しくは pysam のマニュアルを参考するよい。

ファイルの読み込み

pysam を利用して SAM/BAM を読み込む際は pysam.AlignmentFile を利用してファイルを開き、for 文で一行ずつ回す方法で行う。pysam.AlignmentFile を利用してファイルを開くとき、SAM ならば読み込みモード "r" で開けばよく、BAM ならばバイナリーの読み込みモード "rb" で開けばよい。

import pysam
bamfile = pysam.AlignmentFile("SRR616268.bam", "rb")

for read in bamfile:
    print(read)

## SRR616268.11783	163	21	15722	40	93M	21	15758	93	CGTAGCCGAACTGAGAGGTTGATCGGCCACATTGGGACTGAGACACGGCCCAAACTCCTACGGGAGGCAGCAGTAGGGAATCTTCCACAATGG	array('B', [68, 70, 70, 70, 70, 70, 72, 72, 72, 72, 72, 72, 72, 69, 71, 69, 71, 72, 67, 70, 71, 72, 72, 72, 72, 71, 72, 72, 72, 72, 72, 71, 72, 72, 72, 72, 71, 72, 72, 72, 72, 72, 72, 70, 71, 72, 72, 72, 70, 70, 68, 68, 66, 67, 66, 67, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 66, 64, 66, 66, 66, 66, 66, 66, 66, 65, 64, 64, 65, 66, 66, 67, 66, 66, 66, 66, 66, 66, 66, 65])	[('AS', 0), ('XN', 0), ('XM', 0), ('XO', 0), ('XG', 0), ('NM', 0), ('MD', '93'), ('YS', -29), ('YT', 'CP')]
## SRR616268.11784	83	21	18352	42	4M3I100M	21	18275	107	ATTTCGGCAGATAAACTCCGAATACCATCGATCTTACTCCGGGAGTCAGACAGTGAGCGATAAGGTCCATTGTCGAAAGGGGAACAGCCCAGATCACCAGTTAAGGT	array('B', [68, 68, 68, 68, 65, 65, 65, 56, 66, 66, 66, 66, 65, 65, 61, 64, 61, 66, 66, 66, 67, 66, 65, 63, 66, 66, 64, 62, 66, 66, 66, 65, 63, 60, 67, 63, 66, 66, 70, 70, 70, 72, 71, 71, 70, 65, 72, 72, 72, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 69, 71, 72, 71, 70, 64, 71, 71, 70, 68, 69, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 69, 72, 71, 71, 70, 69, 69, 70, 67, 68, 63, 70, 70, 70, 68, 68, 68, 68, 68, 65, 65, 65])	[('AS', -20), ('XN', 0), ('XM', 1), ('XO', 1), ('XG', 3), ('NM', 4), ('MD', '1A102'), ('YS', 0), ('YT', 'CP')]
## SRR616268.11784	163	21	18275	42	93M	21	18352	93	AGCTTTAGGGTTAGCCTCGGAGGATGGATCATGGAGGTAGAGCACTGTTTGAACTAGGGGCCCATCAAGGGTTACTGAATTCAGATAAACTCC	array('B', [68, 70, 70, 70, 70, 70, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 70, 71, 72, 72, 71, 72, 72, 72, 72, 72, 72, 69, 71, 72, 66, 70, 69, 70, 71, 72, 70, 71, 72, 72, 72, 72, 72, 72, 69, 71, 72, 70, 70, 70, 70, 70, 68, 68, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 58, 63, 64, 66, 66, 66, 66, 66, 66, 67, 66, 66, 66, 66, 66, 66, 66, 66, 65, 66, 65, 65])	[('AS', 0), ('XN', 0), ('XM', 0), ('XO', 0), ('XG', 0), ('NM', 0), ('MD', '93'), ('YS', -20), ('YT', 'CP')]
## SRR616268.11785	99	8	1416	40	107M	8	1486	107	CTTCATTGTTTCAAAAAGATTATCTAACGTGACATGGTTGGTGTTCTTACGTTCTTCTTCCTGTGCCCGCTTGGCCCGTTCTTCACCAGCAGCACGCGCGTTGAAAT	array('B', [65, 65, 65, 68, 68, 68, 68, 68, 70, 70, 70, 70, 70, 72, 72, 72, 72, 71, 69, 72, 72, 72, 72, 72, 72, 72, 72, 71, 71, 70, 72, 72, 72, 72, 72, 72, 72, 70, 71, 72, 72, 72, 72, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 70, 70, 70, 68, 64, 66, 67, 62, 65, 66, 66, 66, 66, 66, 66, 66, 67, 66, 66, 65, 64, 64, 65, 64, 66, 66, 66, 66, 66, 66, 66, 66, 64, 62, 64, 66, 68, 68, 68])	[('AS', -24), ('XN', 0), ('XM', 4), ('XO', 0), ('XG', 0), ('NM', 4), ('MD', '100G2C0G0T1'), ('YS', 0), ('YT', 'CP')]

bamfile.close()

BAM または SAM ファイルに保存されているその他の情報を取得するメソッドは以下のようなものがある。

import pysam

bamfile = pysam.AlignmentFile("SRR616268.bam", "rb")

bamfile.filename
## b'SRR616268.bam'

bamfile.header
## {'SQ': [{'LN': 899, 'SN': 'ctg09_00003'}, {'LN': 2417, 'SN': 'ctg03_00032'}, {'LN': 4695, 'SN': 'ctg10_00023'}, {'LN': 4886, 'SN': 'ctg20_00036'}, {'LN': 11173, 'SN': 'ctg16_00033'}, {'LN': 14785, 'SN': 'ctg19_00007'}, {'LN': 15226, 'SN': 'ctg08_00002'}, {'LN': 16183, 'SN': 'ctg25_00029'}, {'LN': 20788, 'SN': 'ctg14_00035'}, {'LN': 21848, 'SN': 'ctg26_00057'}, {'LN': 25877, 'SN': 'ctg21_00025'}, {'LN': 34382, 'SN': 'ctg23_00054'}, {'LN': 46078, 'SN': 'ctg07_00012'}, {'LN': 51669, 'SN': 'ctg05_00050'}, {'LN': 64425, 'SN': 'ctg22_00009'}, {'LN': 64885, 'SN': 'ctg18_00021'}, {'LN': 91565, 'SN': 'ctg13_00018'}, {'LN': 109464, 'SN': 'ctg06_00026'}, {'LN': 122299, 'SN': 'ctg17_00030'}, {'LN': 125041, 'SN': 'ctg15_00005'}, {'LN': 131336, 'SN': 'ctg02_00047'}, {'LN': 134838, 'SN': 'ctg24_00011'}, {'LN': 141486, 'SN': 'ctg04_00046'}, {'LN': 222389, 'SN': 'ctg27_00027'}, {'LN': 231011, 'SN': 'ctg11_00044'}, {'LN': 237614, 'SN': 'ctg01_00022'}, {'LN': 465659, 'SN': 'ctg12_00024'}, {'LN': 472701, 'SN': 'ctg28_00010'}], 'PG': [{'PN': 'bowtie2', 'ID': 'bowtie2', 'VN': '2.2.4', 'CL': '"/usr/local/bin/../Cellar/bowtie2/2.2.4/bin/bowtie2-align-s --wrapper basic-0 -x L12Av1 -S SRR616268sub.sam -1 SRR616268sub.qc2_1.fastq -2 SRR616268sub.qc2_2.fastq"'}], 'HD': {'SO': 'coordinate', 'VN': '1.0'}}

bamfile.nreferences
## 28

bamfile.references
## ('ctg09_00003', 'ctg03_00032', 'ctg10_00023', 'ctg20_00036', 'ctg16_00033', 'ctg19_00007', 'ctg08_00002', 'ctg25_00029', 'ctg14_00035', 'ctg26_00057', 'ctg21_00025', 'ctg23_00054', 'ctg07_00012', 'ctg05_00050', 'ctg22_00009', 'ctg18_00021', 'ctg13_00018', 'ctg06_00026', 'ctg17_00030', 'ctg15_00005', 'ctg02_00047', 'ctg24_00011', 'ctg04_00046', 'ctg27_00027', 'ctg11_00044', 'ctg01_00022', 'ctg12_00024', 'ctg28_00010')

bamfile.lengths
## (899, 2417, 4695, 4886, 11173, 14785, 15226, 16183, 20788, 21848, 25877, 34382, 46078, 51669, 64425, 64885, 91565, 109464, 122299, 125041, 131336, 134838, 141486, 222389, 231011, 237614, 465659, 472701)

bamfile.mapped
## 1960816

bamfile.unmapped
## 14626

bamfile.nocoordinate
## 24516

bamfile.close()

リードのソート

ソートされた BAM ファイルからインデックスを作成する際に pysam.index を利用する。。この機能は samtools index コマンドに等しい。

import pysam

pysam.sort("SRR616268.bam", "SRR616268.sorted")
## samtools sort SRR616268.bam SRR616268.sorted

インデックスの作成

BAM ファイルのソートは pysam.sort メソッドで行う。この機能は samtools sort コマンドに等しい。

import pysam

pysam.index("SRR616268.sorted.bam")
## samtools index SRR616268.sorted.bam

条件を満たすリードを出力

pysam.AlignmentFile を利用して開いたファイルからある範囲にあるリードを取得する際に fetch メソッドを利用する。ただし、paired-end リードの場合は予めソートする必要がある。

import pysam
bamfile = pysam.AlignmentFile("SRR616268.sorted.bam", "rb")

bamfile.references
## ('ctg09_00003', 'ctg03_00032', 'ctg10_00023', 'ctg20_00036', 'ctg16_00033', 'ctg19_00007', 'ctg08_00002', 'ctg25_00029', 'ctg14_00035', 'ctg26_00057', 'ctg21_00025', 'ctg23_00054', 'ctg07_00012', 'ctg05_00050', 'ctg22_00009', 'ctg18_00021', 'ctg13_00018', 'ctg06_00026', 'ctg17_00030', 'ctg15_00005', 'ctg02_00047', 'ctg24_00011', 'ctg04_00046', 'ctg27_00027', 'ctg11_00044', 'ctg01_00022', 'ctg12_00024', 'ctg28_00010')

bamfile.count("ctg09_00003", 1, 100)
## 1647

for read in bamfile.fetch("ctg09_00003", 1, 100):
    print(read)

## SRR616268.843433	163	0	99	6	93M	0	142	93	ACGAGTTGCGAGACCGCGAGGTTAAGCTAATCTCTTAAAGCCATTCTCAGTTCGGACTGTAGGCTGCAACTCGCCTACACGAAGTCGGAATC	array('B', [68, 70, 70, 70, 70, 70, 71, 71, 72, 72, 72, 71, 72, 72, 72, 72, 72, 72, 71, 72, 72, 70, 70, 71, 72, 71, 72, 72, 72, 72, 71, 71, 72, 72, 72, 72, 72, 71, 70, 70, 70, 70, 70, 66, 67, 68, 68, 68, 67, 68, 67, 67, 67, 66, 66, 66, 66, 66, 66, 65, 67, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 66, 66, 66, 66, 66, 62, 66, 66, 66, 66, 66, 66, 66, 64, 66, 66, 47])	[('AS', -1), ('XS', -6), ('XN', 1), ('XM', 1), ('XO', 0), ('XG', 0), ('NM', 1), ('MD', '22N70'), ('YS', -30), ('YT', 'CP')]
## SRR616268.864982	163	0	99	6	93M	0	147	93	ACGAGTTGCGAGACCGCGAGGTTAAGCTAATCTCTTAAAGCCATTCTCAGTTCGGACTGTAGGCTGCAACTCGCCTACACGAAGTCGGAATC	array('B', [68, 70, 70, 70, 70, 70, 72, 72, 72, 72, 72, 72, 71, 72, 72, 72, 72, 72, 71, 72, 72, 72, 72, 72, 72, 71, 72, 72, 72, 72, 72, 71, 71, 72, 72, 72, 72, 71, 70, 70, 70, 70, 70, 68, 68, 68, 68, 68, 68, 68, 67, 68, 67, 66, 66, 66, 66, 66, 66, 65, 66, 66, 66, 66, 66, 66, 66, 66, 65, 65, 66, 66, 66, 64, 66, 66, 66, 66, 65, 66, 66, 66, 66, 64, 64, 66, 66, 66, 64, 66, 66, 66, 63])	[('AS', -1), ('XS', -6), ('XN', 1), ('XM', 1), ('XO', 0), ('XG', 0), ('NM', 1), ('MD', '22N70'), ('YS', -26), ('YT', 'CP')]
## SRR616268.411741	83	0	99	2	4M2I101M	0	42	107	GTCATCCTTGCGAGACCGCGAGGTTAAGCTAATCTCTTAAAGCCATTCTCAGTTCGGACTGTAGGCTGCAACTCGCCTACACGAAGTCGGAATCGCTAGTAATCGCG	array('B', [68, 68, 68, 68, 65, 65, 65, 60, 66, 66, 66, 66, 66, 66, 64, 64, 66, 66, 64, 66, 66, 66, 65, 66, 66, 66, 66, 65, 66, 66, 66, 65, 66, 66, 66, 65, 66, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 67, 68, 68, 68, 68, 70, 70, 69, 71, 72, 72, 72, 71, 72, 72, 72, 71, 71, 68, 72, 71, 71, 71, 70, 71, 70, 72, 72, 72, 72, 71, 70, 72, 72, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 72, 70, 69, 70, 70, 70, 68, 68, 68, 68, 68, 65, 65, 65])	[('AS', -36), ('XS', -41), ('XN', 1), ('XM', 5), ('XO', 1), ('XG', 2), ('NM', 7), ('MD', '0A0C0G1G17N82'), ('YS', -9), ('YT', 'CP')]
## SRR616268.446068	83	0	99	6	5M1D102M	0	81	107	ACGTGTGCGAGACCGCGAGGTTAAGCTAATCTCTTAAAGCCATTCTCAGTTCGGACTGTAGGCTGCAACTCGCCTACACGAAGTCGGAATCGCTAGTAATCGCGGAC	array('B', [67, 68, 66, 68, 65, 65, 65, 49, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 65, 65, 65, 63, 63, 66, 66, 66, 64, 65, 61, 66, 66, 66, 65, 65, 66, 66, 65, 61, 66, 63, 64, 63, 62, 57, 63, 68, 68, 70, 63, 69, 68, 70, 72, 71, 70, 67, 67, 63, 62, 57, 63, 55, 70, 71, 69, 71, 68, 66, 68, 61, 69, 69, 71, 69, 72, 71, 72, 72, 72, 72, 71, 70, 64, 71, 70, 70, 70, 70, 70, 68, 68, 66, 68, 68, 65, 65, 65])	[('AS', -21), ('XS', -26), ('XN', 1), ('XM', 3), ('XO', 1), ('XG', 1), ('NM', 4), ('MD', '3A1^T16N84T0'), ('YS', -1), ('YT', 'CP')]
## SRR616268.490935	83	0	99	6	4M3I100M	0	33	107	ACCTTCCGTTGCGAGACCGCGAGGTTAAGCTAATCTCTTAAAGCCATTCTCAGTTCGGACTGTAGGCTGCAACTCGCCTACACGAAGTCGGAATCGCTAGTAATCGC	array('B', [66, 68, 68, 68, 65, 65, 64, 64, 66, 66, 66, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 65, 65, 66, 66, 66, 66, 65, 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 68, 68, 68, 68, 68, 70, 70, 70, 72, 71, 71, 70, 72, 72, 72, 72, 72, 71, 71, 72, 72, 72, 72, 71, 71, 72, 72, 71, 71, 71, 71, 69, 70, 72, 71, 72, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 70, 70, 70, 70, 70, 68, 68, 68, 68, 68, 65, 65, 65])	[('AS', -27), ('XS', -32), ('XN', 1), ('XM', 3), ('XO', 1), ('XG', 3), ('NM', 6), ('MD', '2G0A18N81'), ('YS', -9), ('YT', 'CP')]
## ...

bamfile.close()

リードの情報の取得

ここで SAM / BAM ファイルに保存されている各リード(アラインメント)の情報を取得する方法を紹介する。

import pysam
bamfile = pysam.AlignmentFile("SRR616268.bam", "rb")

for read in bamfile:
    read.cigarstring
    ## '4M3I100M'
    read.cigartuples
    ## [(0, 4), (1, 3), (0, 100)]
    read.flag
    ## 89
    read.get_aligned_pairs()
    ## [(0, 19710), (1, 19711), (2, 19712), (3, 19713), (4, None), (5, None), (6, None), (7, 19714), (8, 19715), (9, 19716), (10, 19717), (11, 19718), (12, 19719), (13, 19720), (14, 19721), (15, 19722), (16, 19723), (17, 19724), (18, 19725), (19, 19726), (20, 19727), (21, 19728), (22, 19729), (23, 19730), (24, 19731), (25, 19732), (26, 19733), (27, 19734), (28, 19735), (29, 19736), (30, 19737), (31, 19738), (32, 19739), (33, 19740), (34, 19741), (35, 19742), (36, 19743), (37, 19744), (38, 19745), (39, 19746), (40, 19747), (41, 19748), (42, 19749), (43, 19750), (44, 19751), (45, 19752), (46, 19753), (47, 19754), (48, 19755), (49, 19756), (50, 19757), (51, 19758), (52, 19759), (53, 19760), (54, 19761), (55, 19762), (56, 19763), (57, 19764), (58, 19765), (59, 19766), (60, 19767), (61, 19768), (62, 19769), (63, 19770), (64, 19771), (65, 19772), (66, 19773), (67, 19774), (68, 19775), (69, 19776), (70, 19777), (71, 19778), (72, 19779), (73, 19780), (74, 19781), (75, 19782), (76, 19783), (77, 19784), (78, 19785), (79, 19786), (80, 19787), (81, 19788), (82, 19789), (83, 19790), (84, 19791), (85, 19792), (86, 19793), (87, 19794), (88, 19795), (89, 19796), (90, 19797), (91, 19798), (92, 19799), (93, 19800), (94, 19801), (95, 19802), (96, 19803), (97, 19804), (98, 19805), (99, 19806), (100, 19807), (101, 19808), (102, 19809), (103, 19810), (104, 19811), (105, 19812), (106, 19813)]
    read.get_reference_positions()
    ## [19710, 19711, 19712, 19713, 19714, 19715, 19716, 19717, 19718, 19719, 19720, 19721, 19722, 19723, 19724, 19725, 19726, 19727, 19728, 19729, 19730, 19731, 19732, 19733, 19734, 19735, 19736, 19737, 19738, 19739, 19740, 19741, 19742, 19743, 19744, 19745, 19746, 19747, 19748, 19749, 19750, 19751, 19752, 19753, 19754, 19755, 19756, 19757, 19758, 19759, 19760, 19761, 19762, 19763, 19764, 19765, 19766, 19767, 19768, 19769, 19770, 19771, 19772, 19773, 19774, 19775, 19776, 19777, 19778, 19779, 19780, 19781, 19782, 19783, 19784, 19785, 19786, 19787, 19788, 19789, 19790, 19791, 19792, 19793, 19794, 19795, 19796, 19797, 19798, 19799, 19800, 19801, 19802, 19803, 19804, 19805, 19806, 19807, 19808, 19809, 19810, 19811, 19812, 19813]
    read.get_tags()
    ## [('AS', -32), ('XN', 0), ('XM', 3), ('XO', 1), ('XG', 3), ('NM', 6), ('MD', '0C0C0C101'), ('YT', 'UP')]
    read.is_read1
    ## True
    read.is_read2
    ## False
    read.is_reverse
    ## True
    read.is_secondary
    ## False
    read.is_unmapped
    ## False
    read.mate_is_reverse
    ## False
    read.mate_is_unmapped
    ## True
    break

bamfile.close()

ファイルの保存

ある BAM / SAM ファイル中の特定のリードを取得して別のファイルに保存することも pysam を利用すれば簡単に行える。次の例では、SRR616268.sorted.bam ファイル中にあるリードのうち、ペアエンドリードの自分自身がマップされていて、かつ相手もマップされているリードだけを取得して別のファイルに保存する方法である。

import pysam

in_bamfile = pysam.AlignmentFile("SRR616268.sorted.bam", "rb")
out_bamfile = pysam.AlignmentFile("SRR616268.mapped.bam", "wb", template = in_bamfile)

for read in bamfile:
    if (not read.is_unmapped) and (not read.mate_is_unmapped):
        out_bamfile.write(read)


in_bamfile.close()
out_bamfile.close()

このように作成した SRR616268.mapped.bam ファイルのサイズは SRR616268.sorted.bam よりも大きくなる場合がある。一度 SRR616268.mapped.bam をソートすれば、サイズが小さくなる。