使用Python处理BAM的方法


Posted in Python onSeptember 28, 2018

在上一篇的文章里我详细介绍了BAM(SAM/CRAM)的格式和一些需要注意的细节,还说了该如何使用samtools在命令行中对其进行操作。但是很多时候这些操作是不能满足我们的实际需要的,比如统计比对率、计算在某个比对质量值之上的read有多少,或者计算PE比对的插入片段长度分布,甚至需要你根据实际情况编写一个新的变异检测算法等。这个时候往往难以直接通过samtools来实现【注】,而是需要编写专门的程序进行计算。因此,在这一篇文章里我们就一起来学习应该如何在程序中借助Pysam来处理BAM文件。

【注】关于统计比对率其实是可以通过samtools stats计算获得的。不过我们这篇文章不是为了争辩samtools能做什么,不能做什么,而是要跟大家讨论该如何编写程序处理BAM。

不过,在开始之前我想稍微再补充一下上一节中提到的CRAM——我习惯将其称为BAM的高压缩格式,因为它和BAM/SAM的格式基本相同,但有四点我们需要注意一下:

CRAM的高压缩是通过借助参考序列和对其他信息的进一步编码来实现的,它相比于BAM有着更高的压缩率,能够节省30%-50%的空间;

CRAM目前的IO效率没有BAM高(压得密嘛),约慢30%,但在不断进步,现在已经更新到了3.x版本了;
CRAM和BAM可以通过samtools或者picard方便地实现互转;
CRAM一定会取代BAM,这话并不是我说的,而是bwa/samtools的作者lh3说的。

什么是Pysam

Pysam是一个专门用来处理(BAM/CRAM/SAM)比对数据和变异数据(VCF和BCF)的Python包。它的核心是htslib——一个高通量数据处理API(来自samtools和bwa的核心,基于C语言),开发者们用Python对它直接进行轻量级包装,因此能够在Python中方便地进行调用,并且保证了它与原生C-API功能上的高度一致。

为什么是Pysam

因为Pysam可以说是最为官方的版本,有比较固定的开发者在维护,它的稳定性和可靠性都很高。虽然还有一些其它的包同样能够处理BAM但其实它们大多绕不开对htslib的使用,但却没有pysam周全。而且Pysam还集成了tabix的接口,所以除了比对数据之外,还能够用于处理所有用tabix构建过索引的文件,总之就是全且可靠。

如果是文本格式的sam的话,其实也可以直接将其当作普通文本文件来处理,不需借助任何程序包(这在早期的数据分析中经常看到这种操作),只是要麻烦很多(必须自己在程序中处理所有细节,包括解析FLAG和CIGAR信息,以前我也干过不少类似的事情),甚至我还看到有人直接在程序中调用samtools view把BAM转换成SAM之后再处理的。。。这样的做法实在不推荐。

所以,只要你用的是Python,那么Pysam真的是目前看来比较好的选择。当然如果你用C/C++那么直接用htslib或者bamtools,如果是Java,那么直接使用htsjdk——htslib的java版本。

如何使用Pysam

使用Python处理BAM的方法

首先,要为我们的Python环境安装这个包,如果已安装过的话可以忽略这一步。有两个方法,pip和bioconda,都比较简单,我们这里以pip——Python的包管理工具来进行:

$ pip install pysam

安装完成之后我们就可以在Python程序中调用pysam了。

读取BAM/CRAM/SAM文件

Pysam中的函数有很多,但是重要的读取函数主要有:

  • AlignmentFile:读取BAM/CRAM/SAM文件
  • VariantFile:读取变异数据(VCF或者BCF)
  • TabixFile:读取由tabix索引的文件;
  • FastaFile:读取fasta序列文件;
  • FastqFile:读取fastq测序序列文件;

等以上几个,其中尤以AlignmentFile和VariantFile为核心。需要我们注意到的地方是,Pysam中的有些函数由于历史原因存在重复,比如名字上只有大小写的差异,但功能却是一样的(比如下图的TabixFile),有些则只是简化了函数名,这些情况用的时候留个心眼就行了。

使用Python处理BAM的方法

另外,这篇文章的目的是介绍如何处理比对文件,所以我打算只介绍AlignmentFile。

读取比对文件前,我建议先使用samtools index为比对文件构建好索引。当然如果是SAM文件就不必了——它是文本文件,索引的作用是让我们可以对文件进行随机读取,而不必总是从头开始。

下面我先用一个例子作为引子:

import pysam
bf = pysam.AlignmentFile('in.bam', 'rb')

我在这个例子里面,先在程序中导入pysam包,然后调用AlignmentFile函数读取'in.bam'文件,并把句柄赋值给了bf,bf其实是一个迭代器——Python中的术语,意思就是适合在for循环中进行遍历的对象。

这样我们就是可以通过bf获取这份比对文件中的内容了。比如我们想把in.bam中每一条read的比对位置(包含染色体编号和位置信息),比对质量值和插入片段长度输出(我们的in.bam来自PE测序数据的结果),那么可以这样做:

import pysam
bf = pysam.AlignmentFile('in.bam', 'rb')
for r in bf:
 print r.reference_name, r.pos, r.mapq, r.isize

是不是很简单!打开in.bam文件之后,用for循环对其从头到尾地遍历,并把每个值都赋给r,r在这里代表的就是比对的read信息,它是一个对象(在Pysam由AlignedSegment定义),通过它就可以获取所有的比对信息,比如上面例子中:

  • r.reference_name代表read比对到的参考序列染色体id;
  • r.pos代表read比对的位置;
  • r.mapq代表read的比对质量值;
  • r.isize代表PE read直接的插入片段长度,有时也称Fragment长度;

这里例子的结果如下:

chrM 160 50 235
chrM 161 30 -283
chrM 314 60 -207
...

另外,由于bf是一个迭代器,我们其实还可以用bf.next()一个一个地对其进行访问,而不必在for循环中遍历,这在一些特殊的情况下,这个做法是非常有用且方便的。

当然,上面这个例子其实非常简单,实际上r变量中还有很多其它关于比对的信息,下面这个截图,就是变量中能够获取到的所有比对相关的信息,有好几十个。

使用Python处理BAM的方法

眼尖的同学可能也发现了,这里面存在一些名字类似的变量,如:r.mapping_quality 和 r.mapq,它们其实都是比对质量值。类似的也还有几个,这都是上面我提到的历史原因所致,不过这种多余变量随着Pysam的维护也正在逐步变少。

此外,Pysam中的位点坐标体系是0-base(意思是染色体的起始位置是从0而不是1开始算的)而不是1-base,所以上面的输出的160,其实真实位置应该要+1,也就是161。

还有,上文我也说过,AlignmentFile除了能够读/写BAM之外,还同样能够读/写CRAM和SAM。区别就在于函数中的第二个参数,比如上面例子中的字符'b'就是用于明确指定BAM文件,'r'字符代表“只读”模式(read首字母)。如果要打开CRAM文件,只需要把b换成c(代表CRAM)就行了,如下:

import pysam
cf = pysam.AlignmentFile('in.cram', 'rc')

那么,如果是SAM文件呢?去掉b或c即可:

import pysam
sf = pysam.AlignmentFile('in.sam', 'r')

读取特定比对区域内的数据

有时候我们并不需要遍历整一份BAM文件,我们可能只想获得区中的某一个区域(比如chrM中301-310中的信息),那么这个时候可以用Alignmen模块中的fetch函数:

import pysam
bf = AlignmentFile('in.bam', 'rb')
for r in bf.fetch('chrM', 300, 310):
 print r
bf.close()

通过fetch函数就可以定位特定区域了,非常方便。不过,这个时候输入文件in.bam就必须要有索引,不然无法实现这种读取操作。最后用完了,要记得关闭文件(bf.close())。

来个稍微难一点的例子

问题:如何输出覆盖在某个位置上,比对质量值大于30的所有碱基?

这个问题包含两个部分:

  • 固定的某个位置(我们这里还是用chrM 301这个位置)
  • read比对质量值必须是大于30。

如何做呢?这个时候我们要用AlignmentFile模块的另一个函数——pileups来协助解决,代码如下:

import pysam
bf = pysam.AlignmentFile("in.bam", "rb" )
for pileupcolumn in bf.pileup("chrM", 300, 301):
  for read in [al for al in pileupcolumn.pileups if al.alignment.mapq>30]:
    if not read.is_del and not read.is_refskip:
     if read.alignment.pos + 1 == 301:
       print read.alignment.reference_name,\
           read.alignment.pos + 1,\
          read.alignment.query_sequence[read.query_position]
bf.close()

这段代码看起来虽然简单,但其实包含了很多信息。总的来说,就是通过pileup获取了所有覆盖到该位置的read,并将其存到pileupcolumn中。然后,对pileupcolumn调用pileups(注意多了一个s)获得一条read中每个比对位置的信息(一条read那么长,并非只覆盖了一个位置),然后通过判断语句留下覆盖到目标位点(301)的碱基。代码中的read.alignment是Pysam中AlignedSegment对象,它包含的内容和上述其它例子中的r是一样的。read.alignment.pos + 1还是0-base的原因。最后结果如下:

chrM 301 A
chrM 301 A
chrM 301 A
chrM 301 C
chrM 301 C
chrM 301 C
chrM 301 C
chrM 301 C
chrM 301 C
chrM 301 C
...

创建BAM/CRAM/SAM文件

最后这个例子,我想告诉大家该如何用Pysam输出BAM/CRAM/SAM格式,具体还是看代码吧,这里想输出结果是BAM文件,所以输出模式是“wb”,例子中我们只输出一条比对结果作为说明。

import pysam

header = {'HD': {'VN': '1.0'},
     'SQ': [{'LN': 1575, 'SN': 'chr1'},
         {'LN': 1584, 'SN': 'chr2'}]
}
tmpfilename = "out.bam"
with pysam.AlignmentFile(tmpfilename, "wb", header=header) as outf:
  a = pysam.AlignedSegment() # 定义一个AlignedSegment对象用于存储比对信息
  a.query_name = "read_28833_29006_6945"
  a.query_sequence="AGCTTAGCTAGCTACCTATATCTTGGTCTTGGCCG"
  a.flag = 99
  a.reference_id = 0
  a.reference_start = 32
  a.mapping_quality = 20
  a.cigar = ((0,10), (2,1), (0,25))
  a.next_reference_id = 0
  a.next_reference_start=199
  a.template_length=167
  a.query_qualities = pysam.qualitystring_to_array("<<<<<<<<<<<<<<<<<<<<<:<9/,&,22;;<<<")
  a.tags = (("NM", 1),
       ("RG", "L1"))
  outf.write(a)

小结

我写这篇文章的目的主要有两个:第一,充实上一篇文章中关于如何操作BAM的内容;第二,介绍Pysam这一个值得使用的包给大家。另外,我上面列举的例子其实都比较偏于基础操作,这可能和我自身对认知的看法有关。我一直认为,只有真正理解并灵活地应用基础操作,才可以灵活地解决一切复杂的问题。

而且,上面几个例子中用到的模块和函数其实都是比较常用的,所以我比较推荐优先掌握它们。这些例子里面用到的数据我已放到github了,感兴趣的同学可以在公众号后台回复“WGS”即可获得,后续也会陆续有其它的代码和数据可供参考。

最后,Pysam的内容其实还有很多,我所介绍的也仅在对于比对数据的处理,其它很多的模块和函数,包括对Fasta,Fastq,VCF,BCF和Tabix文件的处理,我就不进行一一介绍了,建议大家在使用的时候多看看它的完整文档。

以上所述是小编给大家介绍的使用Python处理BAM的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
python之import机制详解
Jul 03 Python
Python中apply函数的用法实例教程
Jul 31 Python
详解Python中映射类型(字典)操作符的概念和使用
Aug 19 Python
python opencv 直方图反向投影的方法
Feb 24 Python
基于numpy.random.randn()与rand()的区别详解
Apr 17 Python
python的scikit-learn将特征转成one-hot特征的方法
Jul 10 Python
对python中数据集划分函数StratifiedShuffleSplit的使用详解
Dec 11 Python
python Selenium实现付费音乐批量下载的实现方法
Jan 24 Python
对python中的控制条件、循环和跳出详解
Jun 24 Python
Django框架组成结构、基本概念与文件功能分析
Jul 30 Python
Django文件存储 自己定制存储系统解析
Aug 02 Python
解决pytorch 保存模型遇到的问题
Mar 03 Python
python版本单链表实现代码
Sep 28 #Python
python实现反转部分单向链表
Sep 27 #Python
python八皇后问题的解决方法
Sep 27 #Python
python判断数字是否是超级素数幂
Sep 27 #Python
Flask实现跨域请求的处理方法
Sep 27 #Python
python环形单链表的约瑟夫问题详解
Sep 27 #Python
transform python环境快速配置方法
Sep 27 #Python
You might like
PHP计算指定日期所在周的开始和结束日期的方法
2015/03/24 PHP
laravel5.6框架操作数据curd写法(查询构建器)实例分析
2020/01/26 PHP
js玩一玩WSH吧
2007/02/23 Javascript
javascript 动态参数判空操作
2008/12/22 Javascript
window.name代替cookie的实现代码
2010/11/28 Javascript
JS代码放在head和body中的区别分析
2011/12/01 Javascript
javascript的创建多行字符串的7种方法
2014/04/29 Javascript
JavaScript中constructor()方法的使用简介
2015/06/05 Javascript
JS使用正则实现去掉字符串左右空格的方法
2016/12/27 Javascript
Bootstrap入门教程一Hello Bootstrap初识
2017/03/02 Javascript
jQuery实现火车票买票城市选择切换功能
2017/09/15 jQuery
对vue v-if v-else-if v-else 的简单使用详解
2018/09/29 Javascript
webpack打包非模块化js的方法
2018/10/24 Javascript
Javascript原生ajax请求代码实例
2020/02/20 Javascript
基于Electron实现桌面应用开发代码实例
2020/07/07 Javascript
解决antd的Form组件setFieldsValue的警告问题
2020/10/29 Javascript
玩转python爬虫之cookie使用方法
2016/02/17 Python
用Python实现随机森林算法的示例
2017/08/24 Python
一文总结学习Python的14张思维导图
2017/10/17 Python
python文件转为exe文件的方法及用法详解
2019/07/08 Python
Python 实用技巧之利用Shell通配符做字符串匹配
2019/08/23 Python
python实现银行管理系统
2019/10/25 Python
Python 内置变量和函数的查看及说明介绍
2019/12/25 Python
python属于跨平台语言码
2020/06/09 Python
生产班组长岗位职责
2014/01/05 职场文书
幼儿园保育员责任书
2014/07/22 职场文书
小学优秀教师材料
2014/12/15 职场文书
免职通知
2015/04/23 职场文书
毕业证明模板
2015/06/19 职场文书
2016年春季运动会广播稿
2015/08/19 职场文书
大学生活委员竞选稿
2015/11/21 职场文书
八年级历史教学反思
2016/02/19 职场文书
pytorch实现手写数字图片识别
2021/05/20 Python
spring boot中nativeQuery的用法
2021/07/26 Java/Android
详解Vue3使用axios的配置教程
2022/04/29 Vue.js
python神经网络Xception模型
2022/05/06 Python