使用ASMap构建高密度遗传图谱

在我大学的时候,构建遗传图谱靠的是人工跑胶,然后看胶图统计基因型。当时我用的SSCP(单分子构象多态性)技术区分单个碱基存在差异的等位基因,要放在4度过夜12小时,然后第二天银染显色,放在一个医学看片的设备上读条带。

现在测序便宜了,简化基因组测序随随便便就能获得成千上万的分子标记。然而标记多有标记多的烦恼,就是以前的作图软件不好用了。以前的暴力穷举的方法在海量标记面前,几乎不可能在有限的时间里完成构图任务,而且更加麻烦的是高通量的SNP标记还容易出错。

R语言中有一些建图软件包,不过都有一些问题。qtl包的建图算法太老,onemap的算法是SERIATION, RECORD, RCD, UG, 但是代码不是C/C++写的,所以运行速度太让人着急。

在2017年,出现了一个新的R包,ASMap,它其实就是将MSTmap移植到了R语言中。MSTmap是很久之前就提出的高通量作图方法,只不过需要编译,有点不太友好。

ASMap支持如下群体:

  • BC: 回交群体
  • DH: 双单倍体群体
  • ARIL: 高世代重组近交系, 理论上是已经纯合的遗传群体(除了部分剩余杂合位点), 属于永久性群体
  • RILn: 低世代重组近交系,n=2时,就是F2群体。

算法说明简分为两个部分:

  • 聚类算法: 如果分子标记 $m_j$ 和$m_k$ 来自于两个不同的连锁群,那么$P_{jk}=0.5$, 并且标记间的加权距离(hamming distance)为 $E(d_{djk}) = n/2$。 根据定义,MSTmap使用Hoeffding不等式确定标记是否来自于同一个连锁群

  • 标记排序: 先根据遗传距离对标记进行分箱,也就是将共分离的标记当做一个对象。之后排序问题就视作 TSP(旅行商问题), 也就是寻找所有标记间最短的路径。

可用函数

图谱构建函数: 一共有两个,注意p.value的调整

  • mstmap.data.frame()
  • mstmap.cross(): 从qtl包的对象进行转换,对于高世代的RIL群体,要用conver2riself转换类型, 对于BCnFn群体要用conver2bcsft转换类型

标记调整函数:

  • pullCross(): 抽取异常分子标记
  • pushCross(): 检查完毕后,将符合要求的标记放回原处
  • pp.init(): 调整偏分离的阈值

可视化诊断:

  • profileGen(): 统计基因型中交换数,双交换数,缺失情况。 适用于对遗传图谱质量进行评估
  • profileMark: 统计标记的偏分离,缺失比例,等位基因分布,双交换数
  • heatMap(): 以标记间RF(重组率)绘制热图

使用案例

内容翻译自: R Package ASMap: Effcient Genetic Linkage Map Construction and Diagnosis

第一步: 环境准备

加载环境和数据,其中数据集为300个群体,3023个标记的回交群体

1
2
3
4
library("qtl")
library("ASMap")
data("mapBCu")
summary(mapBCu)

如果是自己的数据集,有两种方法可以构建,一种是用mstmap.cross 转换qtl包中read.cross得到的对象,如下

1
2
3
4
mapthis <- read.cross("csv", "http://www.rqtl.org/tutorials", "mapthis.csv",
estimate.map=FALSE)
mapthis <- convert2bcsft(mapthis, BC.gen = 0, F.gen = 2, estimate.map = FALSE)
mapthis <- mstmap.cross(mapthis, id = "id")

或者用mstmap.data.frame, 要调整如下的参数:

  • object: 数据框, 行对应的标记名, 列是每个个体对应的基因型. , 构建数据框时,一定要保证字符串不能被当做是因子(stringsAsFactors=FALSE)。
  • pop.type: 群体类型,“BH”,”BC”, “RILn”(低世代的近交系,RIL2可以认为是F2), “ARIL”(高世代的近交系)
  • dist.fun: 计算遗传距离的函数, 默认是”kosambi”, 不需要更改。
  • p.value: 和群体大小有关,群体越大,p值要越小,也就是要更严格。

对于低世代的近交系,亲本基因型编码为”A”或”a”, “B”或”b”, 杂合基因型编码为”X”. 所谓的缺失值编码为”U”或”-“

mstmap.data.frame这一步运行会比较慢,这是由于它会预先对数据进行分箱,减少标记量,提高后续的运算效率。

运行案例

第二步: 预处理

在正式构建遗传图谱之间,我们需要谨慎的按照如下清单检查我们用于构图的基因型/分子标记的质量,包括但不限于

  • 检查每个个体对应的标记缺失比例,以及每个标记在所有个体中的缺失失比例。缺失比例过高说明标记或个体存在问题
  • 检查群体间是否存在标记高度相似的两个个体。
  • 检查过度偏分离(segregation distortion)的标记。 高度偏分离的标记可能不会定位到唯一的座位上。
  • 检查交换等位基因的标记。这些标记在构建图谱期间,难以聚类,或者和其他标记有关联,因此需要在分析前修复他们的匹配。
  • 检查共分离的标记。 共分离的标记会严重影响构图时的计算效率,所以可以先暂时把它们忽略掉。

检查每个个体的基因型缺失情况

1
plot.missing(mapBcu)

缺失分布

水平的黑线表示一些个体中,大部分的标记都没有对应的基因型,因此我们将这些个体移除。

1
2
sg <- statGen(mapBCu, bychr=FALSE, stat.type = "miss")
mapBC1 <- subset(mapBCu, ind=sg$miss < 1600)

从图谱构建的角度来看,高度相关的个体会增强标记的偏分离. 因此对于基因型几乎一模一样的个体,最好在建立图谱前移除

1
2
gc <- genClones(mapBC1, tol=0.95)
gc$cgd

从R返回的结果中,可以发现有将近11组的基因型几乎一样。但是第一组的BC045和BC039由于存在大量的基因型缺失,不足以说明两者相同,同样情况的还有BC052和BC045,以及BC168和BC045, 可以排除这三项。

1
2
cgd <- gc$cgd[-c(1,4,5)]
mapBC2 <- fixClones(mapBC1, cgd, consensus = TRUE)

接下来,可以进一步检查特定位点的观测等位基因频率和期望频率的偏差。这有可能是基因型考察错误,也有可能意味着该区域受到潜在的生物学和遗传学机制影响。这一步,同时检查分子标记的偏分离情况,不同基因型的占比,和缺失率。

1
2
3
profileMark(mapBC2, stat.type = c("seg.dist", "prop","miss"),
crit.val = "bonf", layout = c(1,4), type="l")

基因型总体情况

对于高度的偏分离的标记,我们应该直接忽略掉

1
2
3
mm <- statMark(mapBC2, stat.type = "marker")$marker$AB
dm <- markernames(mapBC2)[(mm > 0.98) | (mm < 0.2)]
mapBC3 <- drop.markers(mapBC2, dm)

而对于不偏分离不怎么严重的标记,最好的方法是先把他们放在一边,等图谱构建完成之后再把它们放回到遗传图谱中。

1
2
3
4
pp <- pp.init(miss.thresh = 0.1, seg.thresh = "bonf")
mapBC3 <- pullCross(mapBC3, type = "missing", pars=pp)
mapBC3 <- pullCross(mapBC3, type="seg.distortion", pars=pp)
mapBC3 <- pullCross(mapBC3, type="co.located")

第三步: 构图

构图通常不是一次性完成的工作,需要多次迭代。先进行第一次尝试,运行时间取决于计算机性能和标记数目

1
2
mapBC4 <- mstmap(mapBC3, bychr = FALSE, trace = TRUE, p.value = 1e-12)
chrlen(mapBC4) #每个连锁群的长度

对构建出图谱进行可视化检查

1
heatMap(mapBC4, lmax = 70)

热图

热图分为两个部分看。上三角展示的是标记间的重组率,下三角则是标记间的LOD值,两者模式要是一样,那就说明图谱还是靠谱的。中间的正方形为连锁不平衡区(LD block), 一般在连锁不平衡区极少发生交换,而不同的连锁不平衡区交换频繁。

但是光看热图还是不够的,我们还得保证遗传图谱里个体的交换次数不应该太多。也就是,每个个体能发生的交换是有限的,比如说如果染色体长度为200cM,那么在回交后代中,每个个体的重组次数应该不多于14次。

水稻每条染色体平均0.5~1次交换。人类每次减数分裂平均30次。

1
2
pg <- profileGen(mapBC4, bychr = FALSE, stat.type = c("xo", "dxo", "miss"),
id="Genotype", xo.lambda = 14, layout=c(1,3),lty=2)

交换数分析

从上图可以发现,一些个体的重组次数明显过高,而且这些个体的双交换次数明显多于其他个体,缺失率也是如此。

我们需要将这些个体剔除,对遗传图谱进行升级

1
2
3
mapBC5 <- subsetCross(mapBC4, ind = !pg$xo.lambda)
mapBC6 <- mstmap(mapBC5, bychr = TRUE, trace = TRUE, p.value = 1e-12)
chrlen(mapBC6)

和上次相比,每个连锁群的长度明显减少,一般而言遗传图谱越小,图谱越可靠。

1
2
profileMark(mapBC6, stat.type = c("seg.dist", "prop", "dxo", "recomb"),
layout = c(1, 5), type = "l")

交换数分析

最后结果表明,每个个体的双交换次数都没有超过1 个。

第四步: 加入前提剔除的标记

对于前期放在一边的标记,可以在图谱构建完成后放回到完成的图谱中,同时要仔细的检查。

先将515个缺失率在10%~20%的标记塞到图谱上。这一步是将当前的标记和图谱上的标记计算重组率,选择最近位置插入。然后展示长度明显小于其他连锁群的4个连锁群,判断是否能够将他们进行合并。

1
2
3
pp <- pp.init(miss.thresh = 0.22, max.rf = 0.3)
mapBC6 <- pushCross(mapBC6, type = "missing", pars = pp)
heatMap(mapBC6, chr = c("L.3", "L.5", "L.8", "L.9"), lmax = 70)

热图诊断

从图中,我们可以发现,L.3和L.5之间有明显的连锁,L8和L.9有明显的连锁。我们使用mergeCross()将这两个连锁群进行合并,这个时候的mstmap要保证bychr=TRUE, 对不同的连锁群分别计算标记距离,而不是从聚类开始。

1
2
3
4
5
mlist <- list("L.3" = c("L.3", "L.5"), "L.8" = c("L.8", "L.9"))
mapBC6 <- mergeCross(mapBC6, merge = mlist)
names(mapBC6$geno) <- paste("L.", 1:7, sep = "")
mapBC7 <- mstmap(mapBC6, bychr = TRUE, trace = TRUE, p.value = 2)
chrlen(mapBC7)

这样子就得到了更加优化的图谱。但是L1, L2, L.4的连锁群距离有一些轻微的提高,说明可能存在过多的交换现象

1
2
pg1 <- profileGen(mapBC7, bychr = FALSE, stat.type = c("xo", "dxo","miss"), 
id = "Genotype", xo.lambda = 14, layout = c(1,3), lty = 2)

交换分析

我们发现新引入的部分个体是导致该现象的元凶,这就需要我们将这些株系次移除掉,然后重建图谱

1
2
3
mapBC8 <- subsetCross(mapBC7, ind=!pg1$xo.lambda)
mapBC9 <- mstmap(mapBC8, bychr = TRUE, trace = TRUE, p.value = 2)
chrlen(mapBC9)

移除的株系不会对连锁群的数目造成影响,连锁图谱只会在组内进行重建。大部分连锁群的长度都有了一定程度的下降。继续观察一下标记的遗传图谱的表现情况

1
2
profileMark(mapBC9, stat.type = c("seg.dist", "prop"), layout = c(1, 5),
type = "l")

频率变化

从图中可以发现,在L.2里面有一个诡异的突起(偏分离), 需要被移除。当然我们也能看到在L.3, L.5, L.7中,新增的缺失率10%~20%的标记,有一定程度的偏分离。剩下的295个标记为”seg.distortion”的标记说不定能解释这个情况,所以下一步就是把它们塞回去

1
2
3
4
5
6
7
8
sm <- statMark(mapBC9, chr = "L.2", stat.type = "marker")
dm <- markernames(mapBC9, "L.2")[sm$marker$neglog10P > 6]
mapBC10 <- drop.markers(mapBC9, dm)
pp <- pp.init(seg.ratio = "70:30")
mapBC11 <- pushCross(mapBC10, type = "seg.distortion", pars = pp)
mapBC12 <- mstmap(mapBC11, bychr = TRUE, trace = TRUE, p.value = 2)
round(chrlen(mapBC12) - chrlen(mapBC9), 5)
nmar(mapBC12) - nmar(mapBC10)

最后一步就是加入共分离的标记

1
mapBC <- pushCross(mapBC12, type = "co.located")

最后结果如下

结果统计

如何使用BSA方法进行遗传定位(水稻篇)

BSA虽然不是我最早接触的高通量数据分析项目(最早的是RNA-seq),但是却是我最早独立开展的数据分析项目, 我曾经专门写过一篇文章介绍如何使用SHOREMap做拟南芥的EMS诱变群体的BSA分析

在遗传定位上,相对于GWAS和binmap,BSA是一个比较省钱的策略,它只需要测两个亲本和后代中两个极端差异群体即可,但是它对实验设计,表型考察,样本挑选都有比较高的要求。如果你的表型差异并不是泾渭分明,那么还是不要用BSA比较合适。这方面知识,建议阅读徐云碧老师2016年发表在PBJ上的”Bulked sample analysis in genetics, genomics and crop improvement”, 这篇教程侧重于实际分析,而非理论讲解。

用于讲解的文章题为”Identification of a cold-tolerant locus in rice (Oryza sativa L.) using bulked segregant analysis with a next-generation sequencing strategy”, 发表在Rice上。 文章用的耐寒(Kongyu131)和不耐寒(Dongnong422)的两个亲本进行杂交,得到的F2后代利用单粒传法得到RIL(重组自交系)群体.之后用BWA+GATK识别SNP,计算ED和SNP-index作图。

这次实战的目标也就是得到文章关键的两张图:

ED

SNP-index

环境准备

新建一个项目,用于存放本次分析的数据和结果

1
2
mkdir -p rice-bsa 
cd rice-bsa

后续分析还需要用到如下软件:

  • wget: 一般Linux系统会自带,用于下载数据
  • seqkit: 多能的序列处理软件
  • fastp: 数据质控
  • bwa: 数据比对到参考基因组
  • samtools: 处理sam/bam文件
  • sambamba: 标记重复序列
  • bcftools: 处理vcf/bcf,能用于变异检测
  • R: 数据分析

我们需要分别下载参考基因组(IRGSP)数据和测序数据。

1
2
3
4
5
6
# rice-bsa目录下
mkdir -p ref && cd ref
wget https://rapdb.dna.affrc.go.jp/download/archive/irgsp1/IRGSP-1.0_genome.fasta.gz
gunzip IRGSP-1.0_genome.fasta.gz
# 建立索引
bwa index IRGSP-1.0_genome.fasta

之后 根据文章提供的编号,SRR6327815, SRR6327816, SRR6327817, SRR6327818 ,我们到ENA 查找对应的下载链接进行数据下载。

1
2
3
4
5
6
# rice-bsa目录下
mkdir -p data && cd data
wget ftp://ftp.sra.ebi.ac.uk/vol1/fastq/SRR632/005/SRR6327815/SRR6327815_{1,2}.fastq.gz
wget ftp://ftp.sra.ebi.ac.uk/vol1/fastq/SRR632/006/SRR6327816/SRR6327816_{1,2}.fastq.gz
wget ftp://ftp.sra.ebi.ac.uk/vol1/fastq/SRR632/007/SRR6327817/SRR6327817_{1,2}.fastq.gz
wget ftp://ftp.sra.ebi.ac.uk/vol1/fastq/SRR632/008/SRR6327818/SRR6327818_{1,2}.fastq.gz

因为在NCBI上下载的是SRA格式,需要做数据转换,而从ENA上可以直接下载压缩过的fastq文件,所以我更偏好于ENA。

上游预处理

对于测序数据而言,为了能够获得后续用于计算SNP-index或者ED的SNP信息,我们需要将二代测序得到的数据回贴到参考基因组上,然后利用变异检测软件找到每个样本和参考基因组的区别。

数据质控

原始数据中可能有一部分序列质量不够高,会影响后续分析,比如说测序质量不够,或者说存在接头。通常我们会都会对原始数据进行一波过滤,这里用的是fastp,优点就是快。

1
2
3
4
5
6
# rice-bsa目录下
mkdir -p 01-clean-data
fastp -i data/SRR6327815_1.fastq.gz -I data/SRR6327815_2.fastq.gz -o 01-clean-data/SRR6327815_1.fastq.gz -O 01-clean-data/SRR6327815_2.fastq.gz
fastp -i data/SRR6327816_1.fastq.gz -I data/SRR6327816_2.fastq.gz -o 01-clean-data/SRR6327816_1.fastq.gz -O 01-clean-data/SRR6327816_2.fastq.gz
fastp -i data/SRR6327817_1.fastq.gz -I data/SRR6327817_2.fastq.gz -o 01-clean-data/SRR6327817_1.fastq.gz -O 01-clean-data/SRR6327817_2.fastq.gz
fastp -i data/SRR6327818_1.fastq.gz -I data/SRR6327818_2.fastq.gz -o 01-clean-data/SRR6327818_1.fastq.gz -O 01-clean-data/SRR6327818_2.fastq.gz

序列比对

之后将各个样本的序列回帖到参考基因组

1
2
3
4
5
6
7
8
9
10
11
# rice-bsa目录下
mkdir -p 02-read-align
NUMBER_THREADS=80
REFERENCE=ref/IRGSP-1.0_genome.fasta
for i in `ls 01-clean-data/`; do
sample=${i%%_*}
(bwa mem -M -R "@RG\\tID:${sample}\\tSM:${sample}\\tPL:ILLUMINA" \
-t $NUMBER_THREADS $REFERENCE 01-clean-data/${sample}_1.fastq.gz 01-clean-data/${sample}_2.fastq.gz || echo -n 'error' ) \
| samtools sort -@ 20 -o 02-read-align/${sample}_sort.bam -
samtools index -@ 20 02-read-align/${sample}_sort.bam
done

这里用到了稍微比较高级的shell操作

  • 变量名替换sample=${i%%_*}
  • 逻辑判断: ||
  • 子shell: ()
  • for循环: for do done

接着我门需要去除重复序列,这里的重复序列指的是拥有相同位置信息,且序列也一模一样的read,通常是由PCR扩增引起。过滤重复序列的目的是为了提高变异检测的准确性,如果一条read上出现的“变异”其实是在第一轮PCR扩增时引入的错误,那么后续的扩增只会让这个错误一直保留着,随后测序的时候这条许多又被多次测到,那么在后续的分析中由于多次出现,就有可能会变异检测软件当作真实变异。

这里用sambamba,因为它的速度比较快,且结果和picard一模一样。

1
2
3
4
5
# rice-bsa目录下
for i in `ls 02-read-align/*_sort.bam`; do
sample=${i%%_*}
sambamba markdup -r -t 10 ${sample}_sort.bam ${sample}_mkdup.bam
done

变异检测

变异检测最常见的就是bcftools, freebayes和GATK. 这里用的是BCFtools,主要原因还是它的速度比较快。

这里为了让他的速度更快,我用了--region参数分染色体并行,由于水稻有12条染色体,相当于提速了12倍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# rice-bsa目录下
mkdir -p 03-variants
ls -1 02-read-align/*_mkdup.bam > bam_list.txt
seqkit seq -n ref/IRGSP-1.0_genome.fasta | \
while read region
do
bcftools mpileup -f ref/IRGSP-1.0_genome.fasta \
--redo-BAQ --min-BQ 30 \
--per-sample-mF \
--annotate FORMAT/AD,FORMAT/DP \
--regions ${region} \
-Ou --bam-list bam_list.txt | \
bcftools call -mv -Ob -o 03-variants/${region}.bcf &
done

之后将拆分运行的结果合并到单个文件中

1
2
3
# rice-bsa目录下
mkdir -p 04-variant-filter
bcftools concat --naive -o 04-variant-filter/merged.bcf 03-variants/*.bcf

变异过滤

得到VCF文件还需要进行一些过滤,来提高变异的准确性. 这个需要根据具体的项目来进行, 但是有一些固定的指标可以用来对结果进行过滤,例如

  • 测序深度
  • 非参考基因组的高质量read数
  • 是否和indel紧邻,通常和indel比较近的snp都不可靠
  • 平均的比对质量,

举个例子:

1
2
3
4
# rice-bsa目录下
cd 04-variant-filter

bcftools filter -g3 -G10 -e'%QUAL<10 || (RPB<0.1 && %QUAL<15) || (AC<2 && %QUAL<15) || MQ < 30 || MQSB <=0.1' merged.bcf > filter.vcf

之后,我只选择了snp用于后续分析

1
2
# 04-variant-filter目录下
bcftools view -i 'TYPE="snp" & N_ALT =1 & STRLEN(ALT) = 1' filter.vcf > snps.vcf

最终留下了将近80w的SNP。这就是后续R语言下游分析的基础。

上游处理后的数据可以从链接:https://pan.baidu.com/s/1n6CX33E6Qrpf85INMxGGAQ ( 密码:q9u3 )下载

下游分析

下游分析可能是比较成熟化的流程分析,对服务器的要求比较高一些,下游分析则是对背景知识要求高一些,例如VCF的格式,遗传学的三大定律等。

让我们先安装并加载所需的R包

1
2
3
4
5
install.pacakges("devtools")
install.packages("vcfR")
devtools:install_github("xuzhougeng/binmapr")
library("vcfR")
library("binmapr")

然后我们需要利用vcfR读取VCF文件

1
vcf <- read.vcfR("04-variant-filter/snps.vcf")

接着从VCF对象中提取两个关键信息,AD(Allele Depth)和GT(Genotype)

1
2
gt <- extract.gt(vcf)
ad <- extract.gt(vcf, "AD")

gt是一个基因型矩阵,基于之的前过滤操作,所以这里只会有”0/0”, “0/1”,”1/1”这三种情况,而ad则是等位基因的count数. 我们用head查看前10行来了解下情况,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> head(gt)
SRR6327815 SRR6327816 SRR6327817 SRR6327818
chr01_1151 "0/0" "1/1" "0/0" "0/1"
chr01_6918 "0/0" "1/1" "0/0" "0/1"
chr01_17263 "0/0" "1/1" "0/0" "0/1"
chr01_21546 "1/1" "1/1" "0/0" "0/1"
chr01_24732 "1/1" "0/0" "0/0" "0/0"
chr01_33667 "1/1" "1/1" "0/0" "0/1"

> head(ad)
SRR6327815 SRR6327816 SRR6327817 SRR6327818
chr01_1151 "14,0" "0,18" "10,0" "11,3"
chr01_6918 "31,0" "0,30" "32,0" "21,5"
chr01_17263 "46,0" "0,34" "20,0" "21,8"
chr01_21546 "0,25" "0,20" "17,0" "16,3"
chr01_24732 "0,29" "36,0" "21,0" "31,0"
chr01_33667 "0,26" "0,31" "28,0" "29,11"

其中SRR6327815, SRR6327816, SRR6327817, SRR6327818 分别对应着 KY131, DN422, T-pool 和 S-pool

仔细观察的话,你会发现一个chr01_21546是一个有趣的位置,因为双亲都是纯合情况下,SRR6327818居然是 “16,3”, 基因型是”0/1”, 这既有可能是亲本是杂合但没有测到,也有可能是后代测错了,一个简单粗暴的方法就是删掉它。此外我们还需要考虑是选择KY131是”1/1”且 “DN422” 是 “0/0”的位点。还是选择KY131是”0/0”, 且 “DN422” 是 “1/1”位点。最好的方法就是两种都测试一下。

首先测试KY131是”1/1”且 “DN422” 是 “0/0”的位点

1
2
3
4
mask <- which(gt[,"SRR6327815"] == "1/1" &  gt[,"SRR6327816"] == "0/0")

ad_flt <- ad[mask,c("SRR6327817", "SRR6327818")]
colnames(ad_flt) <- c("T_Pool", "S_Pool")

这一波过滤,我们从原来的80w位点中留下了20w个位点,因此测双亲很重要,能够极大的降低噪音。

然后,我们就可以根据AD计算SNP-index,即 ALT_COUNT / (REF_COUNT + ALT_COUNT), 在0-1之间。

1
freq <- calcFreqFromAd(ad_flt, min.depth = 10, max.depth = 100)

这里设了一个最大和最小深度用于计算频率,太大的深度可能是同源基因或者是重复序列,太低的深度在计算的时候不太准确。

1
freq2 <- freq[Matrix::rowSums(is.na(freq)) == 0, ]

接着我们就可以尝试去重现文章的结果了

1
2
3
4
5
6
7
8
9
10
par(mfrow = c(3,4))

for (i in paste0("chr", formatC(1:12, width = 2, flag=0)) ){
freq_flt <- freq2[grepl(i,row.names(freq2)), ]
pos <- as.numeric(substring(row.names(freq_flt), 7))
plot(pos, freq_flt[,2] - freq_flt[,1], ylim = c(-1,1),
pch = 20, cex = 0.2,
xlab = i,
ylab = expression(paste(Delta, " " ,"SNP index")))
}

snp-index-1

从图中,我惊讶的发现一些染色体上的部分区域居然都没有标记了,所以我们选择KY131是”0/0”, 而 “DN422” 是 “1/1”的位点进行分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mask <- which(gt[,"SRR6327815"] == "0/0" &  gt[,"SRR6327816"] == "1/1")

ad_flt <- ad[mask,c("SRR6327817", "SRR6327818")]
colnames(ad_flt) <- c("T_Pool", "S_Pool")


freq <- calcFreqFromAd(ad_flt, min.depth = 10, max.depth = 100)
freq2 <- freq[Matrix::rowSums(is.na(freq)) == 0, ]

par(mfrow = c(3,4))

for (i in paste0("chr", formatC(1:12, width = 2, flag=0)) ){
freq_flt <- freq2[grepl(i,row.names(freq2)), ]
pos <- as.numeric(substring(row.names(freq_flt), 7))
plot(pos, freq_flt[,1] - freq_flt[,2], ylim = c(-1,1),
pch = 20, cex = 0.2,
xlab = i,
ylab = expression(paste(Delta, " " ,"SNP index")))
}

snp index-2

从结果中,我们能够发现一个有趣的结果,不同的筛选标记标准会导致标记在染色体上分布发生变化,这可能意味着在某种的筛选标准下,会使得和性状有关的位点明显的被过滤掉。

根据文章里的结果,候选基因落在6号染色体的20-25M中,也就是选择KY131是”0/0”, 而 “DN422” 是 “1/1”的位点结果和原文比较类似。那么如果我们原本不知道这个结果应该怎么办?这其实也不是什么问题,像我这样把两幅图都做出来,然后和实验设计者交流下,也就差不多知道答案了。

除了SNP-index外,文章还有一个ED(Euclidean distance)方法用于定位,我根据文章的公式和自己的理解写了下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
mask <- which(gt[,"SRR6327815"] == "0/0" &  gt[,"SRR6327816"] == "1/1")

ad_flt <- ad[mask,c("SRR6327817", "SRR6327818")]

ED_list <- apply(ad_flt, 1, function(x){
count <- as.numeric(unlist(strsplit(x, ",",fixed = TRUE,useBytes = TRUE)))
depth1 <- count[1] + count[2]
depth2 <- count[3] + count[4]

ED <- sqrt((count[3] / depth2 - count[1] / depth1)^2 +
(count[4] / depth2- count[2] /depth1)^2)
return(ED^5)

})

par(mfrow = c(3,4))

for (i in paste0("chr", formatC(1:12, width = 2, flag=0)) ){
ED_flt <- ED_list[grepl(i,names(ED_list))]
pos <- as.numeric(substring(names(ED_flt), 7))
plot(pos, ED_flt,
pch = 20, cex = 0.2,
xlab = i,
ylab = "ED")
}

ED

我复现的结果和文章的区别在于,少一个拟合线,也就是以1Mb为区间,每次滑动10Kb计算均值。以KY131是”0/0”, 而 “DN422” 是 “1/1”的位点结果为例,我们来增加这条线

我写了一个函数用于根据窗口来计算均值,输入是之前snp-index的位置和对应的值,以及确定窗口的大小和步长,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
calcValueByWindow <- function(pos, value,
window_size = 1000000,
step_size = 100000){
# get the max position in the postion
max_pos <- max(pos)

# construct the window
window_start <- seq(0, max_pos + window_size,step_size)
window_end <- window_start + step_size
mean_value <- vector(mode = "numeric", length = length(window_start))

# select the value inside the window
for (j in seq_along(window_start)){

pos_in_window <- which(pos > window_start[j] &
pos < window_end[j])
value_in_window <- value[pos_in_window]

mean_value[j] <- mean(value_in_window)

}
# remove the Not A Number position
nan_pos <- is.nan(mean_value)
mean_value <- mean_value[! nan_pos]
window_pos <- ((window_start + window_end)/ 2)[!nan_pos]
df <- data.frame(pos = window_pos,
value = mean_value)
return(df)
}

得到结果就可以用用lines在之前结果上加上均值线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
par(mfrow = c(3,4))

for (i in paste0("chr", formatC(1:12, width = 2, flag=0)) ){

freq_flt <- freq2[grepl(i,row.names(freq2)), ]
pos <- as.numeric(substring(row.names(freq_flt), 7))
snp_index <- freq_flt[,1] - freq_flt[,2]

# bin
df <- calcValueByWindow(pos = pos, value = snp_index)

plot(x = pos, y =snp_index,
ylim = c(-1,1),
pch = 20, cex = 0.2,
xlab = i,
ylab = expression(paste(Delta, " " ,"SNP index")))
lines(x = df$pos, y = df$value, col = "red")
}

最后再对文章总结一下。文章并不是只用了BSA的方法进行定位,他们花了几年的时间用SSR分子标记确定了候选基因可能区间,用BSA的方法在原有基础上缩小了定位区间。当然即便如此,候选基因也有上百个,作者通过BLAST的方式,对这些基因进行了注释。尽管中间还加了一些GO富集分析的内容,说这些基因富集在某个词条里,有一个是DNA metabolic processes(GO:0006259),但我觉得如果作者用clusterProfiler做富集分析,它肯定无法得到任何富集结果。他做富集分析的目的是其实下面这个描述,也就是找到和抗冻相关的基因

LOC_Os06g39740 and LOC_Os06g39750,were annotated as the function of “response to cold (GO: 0009409)”, suggesting their key roles in regulating cold tolerance in rice. “

当然他还做了qRT-PCR进行了验证,最后推测LOC_Os06g39750应该是目标基因,这个基因里还有8个SNP位点。

使用SHOREmap做mapping-by-sequencing

简介

SHOREmap可以用来分析传统作图群体(自然系natural strains和分化系,diverged accession杂交,或outcrossing)或近等作图群体(isogenic mapping population, 诱变后代与未诱变亲本进行杂交,即会交,backcrossing)所产生的重测序数据。根据作图群体构建方式的不同,SHOREmap的outcross或backcross采用不同基于滑窗(sliding)方式对等位基因频率进行分析。

SHOREmap的backcross和outcross都需要从突变重组库中获得的一致的碱基识别信息

安装

前置安装

SHOREmap需要DISLIN科学库进行数据可视化

但是在安装DISLIN之前还需要保证存在/usr/lib/libXm.so*/usr/lib/libXm.so*,这两者的安全需要root权限,所以要么联系管理员,要么想办法绕开(这个办法,我还没有想到).

1
2
3
sudo apt-get update
sudo apt-get install libmotif4
sudo apt-get install libxt-dev

开始安装doslin库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cd /path/to/src
# 下载
wget ftp://ftp.gwdg.de/pub/grafik/dislin/linux/i586_64/dislin-11.0.linux.i586_64.tar.gz
# 解压缩
tar -zxvf dislin-11.0.linux.i586_64.tar.gz
cd dislin-11.0
# 加入系统路径
mkdir -p $HOME/biosoft/dislin
DISLIN=$HOME/biosoft/dislin
export DISLIN
# 安装
./INSTALL
# 复制dislin_d.h 到dislin的文件下
cp ./example/dislin_d.h $DISLIN
# 删除安装文件(可选)
rm -rf dislin-11.0

安装SHOREmap v3.x

我这次安装的是当前最新的3.4版本,其他版本估计换汤不换药。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cd $HOME/biosoft
wget http://bioinfo.mpipz.mpg.de/shoremap/SHOREmap_v3.4.tar.gz
# 替换SHOREmap下的dislin的一些文件
tar -zxvf SHOREmap_v3,4
rm dislin/*dislin_d.*
cp $DISLIN/*dislin_d.* dislin
# 编辑/etc/profile或.bashrc
vi .bashrc
export LD_LIBRARY_PATH=$HOME/src/SHOREmap_v3.4/dislin
# 退出保存.bashrc: Esc+:wq
source .bashrc
# 到之前安装的文件夹下
cd & cd src/SHOREmap_v3.4
(可选,如果没有g++)sudo apt-get install build-essential
make
# 将编译文件拷贝到习惯的文件夹中,然后添加执行路径
cp SHOREmap ../../biosoft/SHOREmap_v3.4
echo "export $HOME/bisoft/SHOREmap_v3.4" >> ~/.bashrc

最后,可以重新启动一下bash验证

shoremap

官方网站提供的两个常见问题的解答

Note 1: if the compilation complains like “/usr/bin/ld: cannot find -lXt” (or “/usr/bin/ld: cannot find -ldislin_d”), please open the makefile with the command

1
vi makefile

Press keys ‘esc’ and ‘i’ on the keyboard to edit makefile; move the cursor with arrow keys to the position before -lXt, and edit -L/path/to/libXt.so/; if ‘-ldislin_d’ is not found, edit -L/path/to/dislin_d/ before -ldislin_d). After that, press keys ‘esc’, type in :wq, and press enter to save editing and quit vi. (‘-L’ tells the linker where to find the library given by -l)

Note 2: if ‘/usr/lib/ld: warning: libXm.so.3, needed by ./dislin/libdislin_d.so, not found (try using -rpath or -rpath-link)’ occurs, and you have installed libmotif4, do the following:

1
cp /usr/lib/libXm.so.4 /usr/lib/libXm.so.3

We can make SHOREmap avaiable for general use by inserting the following command into /etc/profile

1
export PATH=$PATH:/path/to/SHOREmap_v3.x

and

1
source /etc/profile

总体流程

OUTCROSS

outcross的基本步骤 描述
SHOREmap extract 提取与SNP突变相关的重测序的一致的识别
SHOREmap create 根据背景/亲本系的重测序质量创建SNP标记列表
SHOREmap outcross 进行等位基因频率分析并定义mapping interval(也就是找到突变所在的大致区域)
SHOREmap annotate 对mapping interval中的突变基因效应进行注释

BACKCROSS

backcross的基本步骤 描述
SHOREmap extract 提取与SNP突变相关的重测序的一致的识别
SHOREmap backcross 进行等位基因分析
SHOREmap annotate 对mapping interval中的突变基因效应进行注

具体步骤

下载数据

只安装软件,却没有数据,我们也只能干瞪眼。

oucross分析所需数据

1
2
3
4
5
6
# OCF2 
wget -4 -q http://bioinfo.mpipz.mpg.de/shoremap/data/software/OC.fg.reads1.fq.gz &
wget -4 -q http://bioinfo.mpipz.mpg.de/shoremap/data/software/OC.fg.reads2.fq.gz &
# Ler
wget -4 -q http://bioinfo.mpipz.mpg.de/shoremap/data/software/OC.bg.reads1.fq.gz &
wget -4 -qh ttp://bioinfo.mpipz.mpg.de/shoremap/data/software/OC.bg.reads2.fq.gz &

backcross分析所需数据

1
2
3
4
5
6
# BCF2
wget -4 -q http://bioinfo.mpipz.mpg.de/shoremap/data/software/BC.fg.reads1.fq.gz &
wget -4 -q http://bioinfo.mpipz.mpg.de/shoremap/data/software/BC.fg.reads2.fq.gz &
# mir159a (Col)
wget -4 -q http://bioinfo.mpipz.mpg.de/shoremap/data/software/BC.bg.reads1.fq.gz
wget -4 -q http://bioinfo.mpipz.mpg.de/shoremap/data/software/BC.bg.reads2.fq.gz

其他数据

除了最基本的测序数据外,我们可能还需要参考基因组,已有的注释数据等

1
2
3
4
5
6
# 参考基因组
wget -4 -q http://bioinfo.mpipz.mpg.de/shoremap/data/software/TAIR10_chr_all.fas &
# 基因注释
wget -4 -q http://bioinfo.mpipz.mpg.de/shoremap/data/software/TAIR10_GFF3_genes.gff &
# SHORE操作的结果数据
wget -4 -q http://bioinfo.mpipz.mpg.de/shoremap/data/software/scoring_matrix_het.txt &

重测序

首先使用bwa,bowtie2等read比对工具将得到的数据比对到参考基因组上。
假设你当前处在MBS文件夹下,该文件下有如下文件

1
2
3
4
5
6
7
8
9
10
# 混池测序结果,双端
BC.fg.reads1.fq.gz
BC.fg.reads2.fq.gz
# 背景信息测序结果
BC.bg.reads1.fq.gz
BC.bg.reads2.fa.gz
# 拟南芥参考基因组
TAIR10_chr_all.fas
# 拟南芥注释信息
TAIR10_GFF3_genes.gff

以下操作都是基于上述文件进行。

第一步: 序列比对,产生SAM文件

1
2
3
4
5
6
mkdir index
# 创建比对所需索引
bowtie2-build TAIR10_chr_all.fas index/TAIR10
# 序列比对
bowtie2 -x index/TAIR10 -1 BC.fg.reads1.fq.gz -2 BC.fg.reads2.fq.gz -S FG.sam
bowtie2 -x index/TAIR10 -1 BC.bg.reads1.fq.gz -2 BC.bg.reads2.fq.gz -S BG.sam

第二步: SAMtools预测突变位点

为了加快运算速度,可以先转换格式,并排序

1
2
3
4
5
6
samtools view -b -o BG.bam BG.sam
samtools view -b -o FG.bam FG.sam
samtools sort -o BG.sorted.bam BG.bam
samtools sort -o FG.sorted.bam FG.bam
samtools index BG.sorted.bam
samtools index FG.sorted.bam

consensus-calling program 寻找可能的变异位点
由于官方说明中使用samtools版本为0.1.19,先要解释一下参数 mpileup -uD -f,

1
2
3
# -f:faidx indexed reference sequence file 前后版本一致
# -u:generate uncompress BCF output 前后版本一致
# -D:output per-sample DP in BCF (require -g/-u).与输出格式有关,目前改为-t

因此,对于1.4版本的samtools,相应的参数为

1
mpileup -u -t DP -f 

官方说明的bcftools也是0.1.19,参数为bcftools view -vcg 旧版本的view在当前的版本用于过滤,功能被call替代

1
2
3
# -v Output variant sites only (force -c)
# -c属于Call variants using Bayesian inference. This option automatically invokes option -e.When -v is in use, skip loci where the fraction of samples covered by reads is below FLOAT. [0],目前被m取代
# -g Call per-sample genotypes at variant sites (force -c),这个没有找到合适的替代参数

综上,推荐使用如下命令行

1
2
samtools mpileup -u -t DP -f ../../../index/TAIR10_chr_all.fa ../../align/bwa/default/BG.sorted.bam | bcftools call -vm -Ov > BG.vcf &
samtools mpileup -u -t DP -f ../../../index/TAIR10_chr_all.fa ../../align/bwa/default/FG.sorted.bam | bcftools call -vm -Ov > FG.vcf &

额外步骤:VCF格式转换

由于vcftools工具版本,所以最后的文件版本是4.2,而SHOREmap要求4.1。通过biostar找到高人写的降级工具(其实就是把一些字符替换一下,但是不了解vcf不同版本的差异话,是不知道怎么写)
把下面的代码存为vcf_dowgrade.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# If you are trying to view VCF 4.2 files in IGV - you may run into issues. This function might help you.
# This script will:
# 1. Rename the file as version 4.1
# 2. Replace parentheses in the INFO lines (IGV doesn't like these!)

function vcf_downgrade() {
outfile=${1/.bcf/}
outfile=${outfile/.gz/}
outfile=${outfile/.vcf/}
bcftools view --max-alleles 2 -O v $1 | \
sed "s/##fileformat=VCFv4.2/##fileformat=VCFv4.1/" | \
sed "s/(//" | \
sed "s/)//" | \
sed "s/,Version=\"3\">/>/" | \
bcftools view -O z > ${outfile}.dg.vcf.gz
tabix ${outfile}.dg.vcf.gz
}

其实对于单个文件而言,可以直接用以下命令

1
2
3
4
5
6
7
8
infile=BG.vcf
outfile=BG.vcf
bcftools view --max-alleles 2 -O v ${infile} | \
sed "s/##fileformat=VCFv4.2/##fileformat=VCFv4.1/" | \
sed "s/(//" | \
sed "s/)//" | \
sed "s/,Version=\"3\">/>/" | \
bcftools view -O z > ${outfile}.dg.vcf.gz

使用SHOREmap寻找突变所在区

第一步:需要把bcf文件通过SHOREmap convert转换成SHOREmap能认识的格式

1
SHOREmap convert --marker samtools.vcf --folder path/to/folder -runid int

会生成三个文件3_converted_consen.txt, 3_converted_variant.txt and 3_converted_reference.txt.

第二步:提取候选分子标记的consensus information(mapping pool)

1
SHOREmap extract --chrsizes chromsize.txt --folder ../SHOREmap_analysis --marker 11_converted_variant.txt --consen 11_converted_consen.txt -verbose

第三步:
然后使用SHOREmap backcross分析。SHOREmap backcross可用来分析回交作图群体所得到重组后代混池数据。相对于传统作图群体,只有诱变剂产生的突变会分离,也只有这些才会用于突变定位。

SHOREmap backcross会尝试过滤出所有参考基因组和测序池之间不同部分用于找到突变点特异部分。为了保证不是自然变异或者是测序错误,测序池选择的部分要多次出现在亲本或背景中。然后根据前景和/或背景的(识别碱基,base calls,质量/覆盖率/等位基因)信息,确定是否把保留的SNP位点作为分子标记。在正确的筛选后(拟南芥大概有上百个标记),SHOREmap backcross就能在分析marker的AF后识别大致的峰。进一步对变异注释后,就能找到目标性状的候选基因了。

SHOREmap backcross所需的输入文件如下:

  • 染色体大小文件,--chrsizes。分为两行,一行是染色体位置,一行是染色体大小。scaffold同理
  • 候选marker文件。也就是使用SHOREmap convert通过vcf生成的converted_variant.txt,每一列的含义如下。
    Column Description
    1 Project name
    2 Identity of chromosome
    3 Position of the SNP-marker
    4 Reference base
    5 Alternative base (or mutant base)
    6 Quality of the alternative base (ranging from 0 to 40)
    7 Number of reads supporting the predicted base
    8 Ratio of reads supporting the predicted base to total coverage
1
SHOREmap backcross --chrsizes chromsize.txt --marker ../convert/11_converted_variant.txt --consen extracted_consensus_0.txt --folder ../BC_analysis -plot-bc --marker-score 40 --marker-freq 0.0 --min-converage 10 --max-coverage 80 -bg ../convert/12_converted_variant.txt  --bg-cov 1 --bg-freq 0.0 --bg-score 1 -non-EMS --cluster 1 --marker-hit 1 -verbose

第四步:对结果进行注释

1
SHOREmap annotate --chrsizes chromsize.txt --folder ../BC_analysis/ann --snp ../convert/11_converted_variant.txt --chrom 2 --start 1 --end 4000000 --genome ../../TAIR10_chr_all.fas --gff ../../TAIR10_GFF3_genes.gff

如何用binmapr进行遗传定位

binmapr目前代码正在重构,目前无法从CRAN进行下载,本篇教程仅为存档用。

binmapr是我折腾的一个R包,它能够将NGS测序得到SNP数据基于binmap进行纠错,用于更好的遗传定位。

在阅读本文之前,请先花点时间看看Bin, Bin, Bin!Map, Map, Map Now!, 我只是将里面的步骤整理成R包方便调用而已。

首先你得安装并加载R包。因为这个R包目前主要是方便自己使用,所以托管在GitHub上,需要用devtools进行安装

1
devtools::install_github("xuzhougeng/binmapr")

之后就可以和其他R包一样正常使用

1
library(binmapr)

R包的使用非常简单,就是调用batchCallGeno 将原本的genotype矩阵按照15bp对snp进行纠正

1
2
3
geno <- batchCallGeno(GT_flt, CHROM = CHROM, 
outdir = ".",
pos.start = 7, fix.size = 10)

因此,你只要提供一个符合要求的输入即可。

以李广伟师兄的数据为例,我已经将其整理成示例数据,因此可以可以通过下面两行命令读取

1
2
data(geno)
data(pheno)

这两个都是数据框,其中geno存放的是基因型数据,而pheno存放的是表型数据

为了能够让batchCallGeno运行,我们需要将geno数据转成一个矩阵,其中行名是位置信息,列名是样本信息

1
2
GT <- as.matrix(geno[,-1:-4])
row.names(GT) <- paste0(geno$CHR, "_", geno$POS)

由于每个位点都在所有样本中都不一定存在,因此可以考虑过滤一些缺失比较多的位点.

1
2
miss_ratio <- rowSums(is.na(GT)) / ncol(GT)
GT_flt <- GT[miss_ratio < 0.20, ]

我这里过滤掉缺失大于20%的位点,原本是打算用5%,未曾想到这个标准过滤下去,90%的数据都快没了,吓得我赶紧用summary(miss_ratio)看了一波分布,改了下标准。

有了合适的数据类型后,就可以调用batchCallGeno函数了。运行结束后,除了得到一个列表外存放基因型外,还会在当前目录下输出csv和pdf文件。其中csv是表型数据,而pdf则是纠错前后的基因型分布。

1
2
3
CHROM <- unique(geno$CHR)
geno_out <- batchCallGeno(GT_flt, CHROM = CHROM,
pos.start = 7, fix.size = 10)

介绍下几个参数

  • CHROM: 用于构建binmap的染色体
  • pos.start, 该参数用于绘图时从行名中提取坐标,例如Chr1_1245需要设置为pos.start=6, 这里命名为chr01_12345所以要设置为pos.start=7
  • fix.size, 它和纠错有关,比如说你的基因型是00000100000, 中间的1很可能是错误的,因为它只出现了一次。fix.size需要根据具体数据来调整。

下一步,我们就可以用方差分析的方法来寻找和表型相关的区间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Load phenotype
anova_analysis <- function(x, phenotype){

p.lmout <- lm(phenotype ~ x, na.action = na.exclude)
p <- anova(p.lmout)[,5][1]
return(p)
}

geno_mt <- geno_out[[1]]
geno_mt_reorder <- geno_mt[,pheno$ID]
pvals <- apply(geno_mt_reorder,
1,
anova_analysis,
phenotype = pheno$PH)


接着画图

1
2
3
4
5
6
7
pos <- as.numeric(substring(row.names(geno_mt_reorder), 7))

plotQtl(pos = pos,
pvalue = pvals,
chr.name = "chr_01",
ymax = 11,
threshold = 3)

QTL mapping

我们可以从中看到一个非常显著的峰,里面的基因中就有一个碰巧是水稻里的绿色革命基因,sd1 LOC_Os01g66100 物理区间是 38382382-38385504,距QTL最显著位置只有258kb的距离,对于一个只有172个个体的F2群体而言,结果是不是已经很不错了。

如何在服务器上安装最新的R

R语言在服务器上安装是一个比较可麻烦可简单的事情,这里记录下R语言在两个比较常见的Linux发行版的安装方法,分别是CentOS和Ubuntu。

通用方法(无需Root)

只要你的服务器能够安装conda,那么你就可以用conda去安装你的R语言。conda已经不再局限于最早的Python的环境管理了,而是扩展到R, Java, C/C++等编程语言。

Package, dependency and environment management for any language—Python, R, Ruby, Lua, Scala, Java, JavaScript, C/ C++, FORTRAN, and more.

让我们使用conda search r-base在conda的频道中检索,

1
2
3
4
5
6
7
8
9
10
...
r-base 3.5.1 hfb2a302_1009 anaconda/cloud/conda-forge
r-base 3.5.1 hfb2a302_1010 anaconda/cloud/conda-forge
r-base 3.6.0 hce969dd_0 pkgs/r
r-base 3.6.1 h6e652e1_3 anaconda/cloud/conda-forge
r-base 3.6.1 h8900bf8_0 anaconda/cloud/conda-forge
r-base 3.6.1 h8900bf8_1 anaconda/cloud/conda-forge
r-base 3.6.1 h8900bf8_2 anaconda/cloud/conda-forge
r-base 3.6.1 hba50c9b_4 anaconda/cloud/conda-forge
r-base 3.6.1 hce969dd_0 pkgs/r

发现能找到好几个版本的R语言,我推荐通过新建环境的方式安装不同版本的R语言,这样就能在不同环境间切换。

1
conda create -n r-3.6.1 r-base=3.6.1

之后用conda activate r-3.6.1调用R的环境即可。

这个方法的优点是不需要root权限,安装方便,不过听过在使用的时候或许会出现一些bug,我还没有遇到。

除了conda外,我们可以通过手工解决R语言的依赖环境,通过源码安装最新的R语言,这个方法也不依赖于平台。不过你需要看下这篇无root权限解决编译时的依赖问题, 但是很麻烦。还是建议用conda比较合适。

假如需要用Jupyter notebook调用R,那么安装方式为

1
2
3
conda install -c r r-irkernel 
# or
conda install -c r r-essentials

因为 conda install -c r r=3.6.x/r-base 默认不会安装 irkernel,而且先安装的 r=3.6.x/r-base 可能与后安装的 r-irkernel/r-essentials 产生冲突。

CentOS

CentOS/RedHat是可以通过sudo yum install R的方式安装R语言,解决一切依赖问题,并且安装比较新的R版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
================================================================
Package Arch Version Repository Size
================================================================
Installing:
R x86_64 3.6.0-1.el7 epel 30 k
Installing for dependencies:
R-core x86_64 3.6.0-1.el7 epel 57 M
R-core-devel x86_64 3.6.0-1.el7 epel 109 k
R-devel x86_64 3.6.0-1.el7 epel 30 k
R-java x86_64 3.6.0-1.el7 epel 31 k
R-java-devel x86_64 3.6.0-1.el7 epel 30 K
....
Transaction Summary
=================================================================
Install 1 Package (+373 Dependent packages)
Upgrade ( 14 Dependent packages)

这个方法安装的是比较新的R,基本上所有新版本能装的R包,它也能装了。但是如果你需要安装最新的R,那么就需要从头编译

先运行sudo yum install R搞定大部分依赖问题,然后你可能还得手动解决几个依赖问题,我遇到的是X11和libcurl

1
2
3
4
# X11
yum install xorg-x11-server-devel libX11-devel libXt-devel
# libcurl
yum install libcurl-devel

之后下载源代码编译安装

1
2
3
4
5
6
7
8
wget https://mirrors.tuna.tsinghua.edu.cn/CRAN/src/base/R-3/R-3.6.1.tar.gz
tar -zxvf R-3.6.1.tar.gz
cd R-3.6.1
# --enable-R-shlib for Rstudio server
mkdir -p /opt/sysoft
./configure --enable-R-shlib --prefix=/opt/sysoft/R-3.6.1
make -j 8
make install

其中--enable-R-shlib是Rsutdio-Server安装需要,而--prefix是指定安装路径

Ubuntu

在Ubuntu上直接通过sudo apt-get install r-base的方式安装的不是最新版本的R,而是R-3.4版本。

R 3.4 packages for Ubuntu on i386 and amd64 are available for all stable Desktop releases of Ubuntu prior to Bionic Beaver (18.04) until their official end of life date.

不过我们可以通过增加安装源的方式,使得能够通过apt-get的方式安装最新的R。

第一步,确认你的Ubuntu版本,是Xenial Xerus(16.04; LTS), Trusty Tahr (14.04; LTS), Bionic Beaver (18.04;LTS), Cosmic Cuttlefish (18.10), Disco Dingo (19.04)的哪一种。

第二步,根据你的服务器Ubuntu版本,按照需求复制下面的其中一行代码(一定要注意,是一行,不是全部复制)

1
2
3
4
5
deb https://cloud.r-project.org/bin/linux/ubuntu disco-cran35/
deb https://cloud.r-project.org/bin/linux/ubuntu cosmic-cran35/
deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/
deb https://cloud.r-project.org/bin/linux/ubuntu xenial-cran35/
deb https://cloud.r-project.org/bin/linux/ubuntu trusty-cran35/

然后用vim编译/etc/apt/sources.list, 添加你复制的内容到最后一行,我的服务器是 xenial,所以增加的是

1
deb https://cloud.r-project.org/bin/linux/ubuntu xenial-cran35/

除了修改/etc/apt/sources.list,还需要增加APT

1
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9

之后,用下面的命令就可以安装最新的R

1
2
3
sudo apt-get update
sudo apt-get install r-base
sudo apt-get install r-base-dev

这个方法稍微麻烦些,据说通过这样子安装的R存在一些bug,不过我没有遇到。

假如你需要安装不同版本的R语言,那么就需要下载源代码进行编译安装。根据我的经验,你至少先得用下面这些命令安装R的依赖环境(可能还不够)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 设置环境变量
export CFLAGS=" -fPIC" CXXFLAGS=" -fPIC"
## build-essential
sudo apt-get install -y build-essential
## java
sudo apt install -y openjdk-9-jdk
## 各种包
sudo apt install -y autoconf libz-dev libbz2-dev liblzma-dev libssl-dev
# solve libcurl problem
#sudo apt install -y libcurl4-openssl-dev # not works for Ubuntu 16.04
sudo apt install -y libcurl4-gnutls-dev
### curses
sudo apt-get install -y libncurses5-dev
### solve X11 problem
sudo apt-get install -y xorg-dev
### zlib2
wget http://zlib.net/zlib-1.2.11.tar.gz
tar -zxvf zlib-1.2.11.tar.gz && cd zlib-1.2.11 && ./configure && make && sudo make install && cd .. && rm -rf zlib-1.2.11
### bzip2
wget https://fossies.org/linux/misc/bzip2-1.0.8.tar.gz
tar -zxvf bzip2-1.0.8.tar.gz && cd bzip2-1.0.8
# add -fPIC
sed -i 's/CFLAGS=/CFLAGS=-fPIC /' Makefile
make && sudo make install && cd .. && rm -rf bzip2-1.0.8

假如你使用的是conda用户,那么安装之前,你需要用先退出conda环境,不然后续libcurl可能会一直提示出错

下载R的源代码,进行编译安装

1
2
3
4
5
6
7
wget https://mirrors.tuna.tsinghua.edu.cn/CRAN/src/base/R-3/R-3.6.1.tar.gz
tar -zxvf R-3.6.1.tar.gz
cd R-3.6.1
# --enable-R-shlib for Rstudio server
./configure --enable-R-shlib --prefix=/opt/sysoft/R-3.6.1
make -j 8
make install

其他

此外,我还推荐大家安装OpenSSL/Curl/XML/HD5F,可以节约之后的一点时间

Fedora, CentOS, RHEL

1
2
3
4
yum install openssl-devel
yum install libcurl-devel
yum install libxml2-devel
yum install hdf5-devel

Debian, Ubuntu等

1
2
3
4
sudo apt install libssl-dev 
sudo apt install libcurl4-openssl-dev
sudo apt install libxml2-dev
sudo apt-get install libhdf5-dev

无root权限解决编译时的依赖问题

如果你拥有最高权限,如果你只管理一台服务器,那么系统自带的包管理工具就帮你解决了所有问题。但是真实世界没有那么美好,所以我花了那么久时间去学习如何从源码开始编译一个软件。

环境为CentOS Linux release 7.4.1708 (Core), Linux内核version 3.10.0-693.el7.x86_64, GCC版本为4.8.5 20150623 (Red Hat 4.8.5-16) (GCC),

Linux的编译体系

无管理员权限编译的常规三部曲是./configure --prefix=$HOME/usr && make && make install,其中最重要的一步就是configure,它所做的任务如下

  • 检查GCC版本以及是否安装了编译所需工具
  • 如果需要头文件,则默认去/usr/include查找
  • 如果涉及到动态编译库,则默认去/usr/lib/usr/lib64查找. 注:lib的函数库仅用于开机时用,提供给/bin和/sbin.

那为何需要配置?配置主要解决软件开发和软件实际安装时平台不同所导致的问题。

首先,一个C/C++工程不可能只用到标准库,很多已有的轮子就不需要重复制造。其次,由于很多软件都重复用到相同的依赖库,那么如果把这些依赖库独立成单独的模块,在调用的时候加载,也能节省空间。早期为了适配多个平台,开发人员需要手写条件语句来检查环境依赖,后来GNU专门开发了Autotools辅助构建源码编译所需要的关键文件。

Autotools

编译环境变量

./configure -h查看帮助文档的时候,会提示几个和编译相关非常重要的环境变量。

1
2
3
4
5
6
7
8
9
10
11
12
# 编译器
CC 指定C编译器(compiler command)路径
CXX 指定C++编译器
# 编译器选项
CFLAGS 用于C编译器的选项
CXXFLAGS 用于C++编译器的选项
LDFLAGS 链接相关选项,如果你有自定义的函数库(lib dir),即可以用 -L<lib dir>指定
# 预编译器
CXXCPP C++ 预处理器(preprocessor)
CPP C 预处理器(preprocessor)
# 预编译器选项
CPPFLAGS C/C++预处理器选项, 如果你自定义的头文件,可以用-I<include dir>

Makfile规则中的编译命令通常遵守如下规范:

1,首先从源代码生成目标文件( 预处理,编译,汇编 ),”-c”选项表示不执行链接步骤;

1
$(CC) $(CPPFLAGS) $(CFLAGS) example.c   -c   -o example.o

2,然后将目标文件链接为最终的结果( 链接 ),”-o”选项用于指定输出文件的名字。

1
$(CC) $(LDFLAGS) example.o   -o example

这些只是约定俗成的惯例,所以有些人会“随性而为”,你也拿他没有办法。尽管将源代码编译为二进制文件的四个步骤由不同的程序(cpp,gcc/g++,as,ld)完成,但是事实上 cpp, as, ld 都是由 gcc/g++ 进行间接调用的。换句话说,控制了 gcc/g++ 就等于控制了所有四个步骤。从 Makefile 规则中的编译命令可以看出,编译工具的行为全靠 CC/CXX CPPFLAGS CFLAGS/CXXFLAGS LDFLAGS 这几个变量在控制。所以控制这些变量最简单的做法是首先设置与这些 Makefile 变量同名的环境变量并将它们 export 为 环境变量(全局),然后运行 configure 脚本,大多数 configure 脚本会使用这同名的环境变量代替 Makefile 中的值

  • CC/CXX: 指定C/C++编译所在路径,即可以选择不同的版本的编译器进行编译。
  • CPPFLAGS: 这是用于预处理阶段的选项。用于添加不在标准路径/usr/include下的头文件。如CPPFLAGS="-I$HOME/usr/include -I$HOME/usr/include/ncurses"
  • CFLAGS/CXXFLAGS: 指定头文件(.h文件)的路径,如:CFLAGS=-I/usr/include -I/path/include。同样地,安装一个包时会在安装路径下建立一个include目录,当安装过程中出现问题时,试着把以前安装的包的include目录加入到该变量中来。

CPPFLAG和CFLAGS/CXXFLAGS这两个变量可以认为是等价关系,都用在预处理阶段,也就是都能用于指定头文件所在位置。

  • LDFLAGS:gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。每安装一个包都几乎一定的会在安装目录里建立一个lib目录。如果明明安装了某个包,而安装另一个包时,它愣是说找不到,可以抒那个包的lib路径加入的LDFALGS中试一下。

有时候LDFLAGS指定-L虽然能让链接器找到库进行链接,但是运行时链接器却找不到这个库,如果要让软件运行时库文件的路径也得到扩展,那么我们需要增加这两个库给”-Wl,R”:

1
LDFLAGS = -L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib

如在执行./configure以前设置环境变量 export LDFLAGS="-L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib",注意设置环境变量等号两边不可以有空格,而且要加上引号(shell的用法)。那么执行configure以后,Makefile将会设置这个选项,链接时会有这个参数,编译出来的可执行程序的库文件搜索路径就得到扩展了

除了通过以上几种环境变量为编译器提供头文件和静态和动态库文件的位置信息外,还有一种变量叫做 PKG_CONFIG_PATH , 它从xx.pc文件获取读取相应的环境环境。

注意:Linux下编译共享库时,必须加上-fPIC参数,即export CFLAGS=" -fPIC" CXXFLAGS=" -fPIC"否则在链接时会有错误提示.这是在编译zsh时候发现明明装了ncurse却还是不能用的共享库的坑。

fPIC的目的是什么?共享对象可能会被不同的进程加载到不同的位置上,如果共享对象中的指令使用了绝对地址、外部模块地址,那么在共享对象被加载时就必须根据相关模块的加载位置对这个地址做调整,也就是修改这些地址,让它在对应进程中能正确访问,而被修改到的段就不能实现多进程共享一份物理内存,它们在每个进程中都必须有一份物理内存的拷贝。fPIC指令就是为了让使用到同一个共享对象的多个进程能尽可能多的共享物理内存,它背后把那些涉及到绝对地址、外部模块地址访问的地方都抽离出来,保证代码段的内容可以多进程相同,实现共享。

动态库路径问题: 由前面可以知道许多大型软件为了减少体积不会完全编译所有功能,而是借助于动态连接技术在运行时加载封装好的动态连接库内的功能。这就涉及一个非常重要的问题,软件如何知道动态链接库所处的位置。动态库搜索路径分两种情况,一种是编译生成可执行文件时,另外一种是运行可执行文件时。

编译生成可执行文件时,动态库的搜索路径顺序如下:

  • 首先gcc会找-L选项;
  • 然后再找gcc的环境变量LIBRARY_PATH,可以在.profile设置这个环境变量,并且可以通过选项-v查看gcc最终编译时LIBRARY_PATH的值;
  • 再找内定目录: /lib:/usr/lib:/usr/local/lib,这些都是当初compile gcc时写在程序内的。

注意上面索顺序是不会递归在目录下搜索的。

生成可执行文件后,运行文件时,动态库的搜索路径顺序如下:

  • 首先编译目标代码时指定的动态库搜索路径,就是用选项 -Wl,rpath 指定程序在运行时动态库的搜索路径,比如gcc -Wl,-rpath,include -L. -ldltest hello.c,在执行文件时会搜索路径./include;
  • 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
  • 配置文件/etc/ld.so.conf中指定的动态库搜索路径,即在配置文件中添加动态库的绝对路径,然后运行指令ldconfig是配置文件生效;
  • 默认的动态库搜索路径/lib:/usr/lib

同样上面索顺序是不会递归在目录下搜索的。通常使用动态库简单做法是:把生成的so文件拷贝到/usr/lib中,这样不管是生成可以执行文件时,还是执行程序时,都能找到需要的so文件。但是普通用户没有/usr/lib的写入权限,所有要指定LD_LIBRARY_PATH.ls

参考资料:

GCC安装(非必要)

首先让我们利用系统原来老旧的GCC编译器编译出最新版本的gcc吧,毕竟安装软件的时候,GCC的版本一定要过最低要求。

第一步: 下载gcc源码

1
2
3
4
mkdir -p ~/src && cd ~/src
wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-7.2.0/gcc-7.2.0.tar.gz
tar -zxvf gcc-7.2.0.tar.gz && cd gcc-7.2.0
ls

check

第二步, 检查系统是否已经具备前置软件, 主要是GMP,MPFR, MPC。这些软件可以到ftp://gcc.gnu.org/pub/gcc/infrastructure/找到,然后下载后解压缩,并移动到gcc源码文件夹下。 可以在配置的时候用--with-gmp, --with-mpfr --with-mpc指定具体所在路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
cd src
# GNU Multiple precision Library
wget ftp://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.1.0.tar.bz2 \
&& tar -jxvf gmp-6.1.0.tar.bz2 && mv gmp-6.1.0 gcc-7.2.0/gmp
# isl library
wget ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2 \
&& tar -jxvf isl-0.18.tar.bz2 && mv isl-0.18 gcc-7.2.0/isl
# MPFR Library
wget ftp://gcc.gnu.org/pub/gcc/infrastructure/mpfr-3.1.4.tar.bz2 \
&& tar -jxvf mpfr-3.1.4.tar.bz2 && mv mpfr-3.1.4 gcc-7.2.0/mpfr
# MPC Library
wget ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.0.3.tar.gz \
&& tar -zxvf mpc-1.0.3.tar.gz && mv mpc-1.0.3 gcc-7.2.0/mpc

不过更加人性化的方法是在GCC源码根目录下运行./contrib/download_prerequisites,可以自动搞定。

第三步:使用./configure进行配置。官方强烈建议, 配置所在文件夹一定要和源码所在文件夹区分开,此外configure还可以配置很多参数,我的代码如下:

1
2
3
4
5
mkdir build && cd build
../configure\
--prefix=$HOME/usr \ # 指定安装路径
--disable-multilib \ # 取消32位库编译
--enable-threads=posix \ # 使用POSIX/Unix98作为线程支持库

基本上这一步不会出现太多的报错,都能够顺利生成Makefile.

第四步: 编译. 这步有一个小技巧就是利用多核处理器进行加速,例如make -j2 就是双核。

这一部分很慢很慢,因为默认设置下是3个阶段的引导(3-stage bootstrap), 以保证能够编译出完整的GCC系统并且还不会出错,你可以在配置的时候用--disable-bootstrap进行关闭。

第五步: 安装。如果你编译都成功了,那么安装也不会存在问题了, make install.

那么我们编译的GCC和系统自带的有什么区别吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 从头编译
$ $HOME/usr/bin/gcc -v
Using built-in specs.
COLLECT_GCC=/home/zgxu/usr/bin/gcc
COLLECT_LTO_WRAPPER=/home/zgxu/usr/libexec/gcc/x86_64-pc-linux-gnu/7.2.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ../configure --prefix=/home/zgxu/usr --disable-multilib --enable-threads=posix
Thread model: posix
gcc version 7.2.0 (GCC)
# 系统自带
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)

不谈安装路径和版本,基本上 差别 就是在配置这一步,而这些参数就需要仔细研究了。

一个 错误 : ‘Link tests are not allowed after GCC_NO_EXECUTABLES.’ 后来发现是第三步没有在独立的文件下构建Makefile.

参考资料:

CMake: 平台无关的编译软件

不同平台有着不同的Make工具用于编译,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。

CMake就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。显然,CMake 是一个比上述几种 make 更高级的编译配置工具。一些使用 CMake 作为项目架构系统的知名开源项目有 VTK、ITK、KDE、OpenCV、OSG 等.

1
2
3
wget https://cmake.org/files/v3.10/cmake-3.10.2.tar.gz
tar xf cmake-3.10.2.tar.gz
cd cmake-3.10.2

参考资料:

几个必须要装的函数库

在安装之前需要先声明几个环境变量,可以直接添加在配置文件中。这都是后面遇到共享库的问题得到的经验教训。

1
2
3
4
5
6
export CFLAGS=" -fPIC"
export CXXFLAGS=" -fPIC"
export CPPFLAGS="-I$HOME/usr/include -I$HOME/usr/include/ncurses -I$HOME/usr/include/X11"
export LDFLAGS="-L$HOME/usr/lib -L$HOME/usr/lib64"
export LD_LIBRARY_PATH=$HOME/usr/lib:$HOME/usr/lib64
export PKG_CONFIG_PATH=$HOME/usr/lib/pkgconfig:$HOME/usr/share/pkgconfig

ncurses提供了一系列的函数以便使用者调用它们去生成基于文本的用户界面,许多大名鼎鼎的软件都用到了ncurses,例如vim, screen,tmux,zsh等。并且samtools如果需要tview可视化BAM文件,也需要这个库做支持。

1
2
3
wget ftp://ftp.invisible-island.net/ncurses/ncurses.tar.gz && tar -zxvf ncurses.tar.gz
./configure --enable-shared --prefix=$HOME/usr
make && make install

Libevent是一个用C语言编写的、轻量级的开源高性能事件通知库, 后续安装tmux时候需要这个依赖库。

1
2
3
4
5
# libevent
cd src
wget https://github.com/libevent/libevent/releases/download/release-2.1.8-stable/libevent-2.1.8-stable.tar.gz
tar -zxvf libevent-2.1.8-stable.tar.gz && cd libevent-2.1.8
./configure prefix=$HOME/usr && make && make install

bzip2, xz, zlib: 文件压缩相关函数库,后续samtools编译时需要。

1
2
3
4
5
6
wget http://www.zlib.net/zlib-1.2.11.tar.gz
tar -zxvf zlib-1.2.11.tar.gz && cd zlib-1.2.11 && ./configure --prefix=$HOME/usr && make && make install
wget http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
tar -zxvf bzip2-1.0.6.tar.gz && cd bzip2-1.0.6 && ./configure --prefix=$HOME/usr && make && make install
wget https://tukaani.org/xz/xz-5.2.3.tar.gz
tar -zxvf xz-5.2.3.tar.gz && cd xz-5.2.3 && ./configure --prefix=$HOME/usr && make && make install

openssl, libssh2, libcurl: 计算机之间文件传输访问相关库。其中OpenSSL是一个安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。libssh2是一个C 函数库,用来实现SSH2协议。libcurl主要功能就是用不同的协议连接和沟通不同的服务器.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 安装有先后
# openssl
wget https://www.openssl.org/source/openssl-1.0.2m.tar.gz
tar -zxvf openssl-1.0.2m.tar.gz && cd openssl-1.0.2m
# 这里非常神奇的居然是config,添加shared生成动态库
./config prefix=$HOME/usr shared
make && make install
# 卸载使用 make clean
# libssh2
wget https://www.libssh2.org/download/libssh2-1.8.0.tar.gz
tar -zxvf libssh2-1.8.0.tar.gz && cd libssh2-1.8.0
./configure --with-libssl-prefix=$HOME/usr/ssl --prefix=$HOME/usr
# libcurl
wget https://curl.haxx.se/download/curl-7.56.1.tar.gz
tar -zxvf curl-7.56.1.tar.gz && cd curl-7.56.1
./configure --prefix=$HOME/usr --enable-http --enable-ftp --enable-file --enable-proxy --enable-telnet --enable-libcurl-option --enable-ipv6 --with-lib --with-ssl

readline: GNU提供用于这些命令补全、搜索历史命令、行编辑快捷键等等这些人性化的交互方式的函数库,缺少这个标准库,编译的R就缺少自动补全的功能。

1
2
3
wget http://ftp.gnu.org/gnu/readline/readline-7.0.tar.gz
tar -zxvf readline-7.0.tar.gz && cd readline-7.0
./configure --prefix=$HOME/usr && make && make install

PCRE: 提供和Perl5相同语法和语义正则表达式的函数库,后续安装R用到。

1
2
3
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.41.tar.gz
tar -zxvf pcre-8.41.tar.gz && cd pcre-8.41
./configure --enable-utf --enable-pcregrep-libz --enable-pcregrep-libbz2 --prefix=$HOME/usr

x11:X11也叫做X Window系统,X Window系统 (X11或X)是一种位图显示的视窗系统,是在 Unix 和 类Unix 操作系统,以及OpenVMS上建立图形用户界面的标准工具包和协议,并可用于几乎所有已有的现代操作系统。主要是R编译的时候要用,具体用途有待开发。

x11安装比较复杂,有很多的依赖库,因此需要先安装xtrans, xextproto, xcb(lib,xcb-proto, libpthread-subs), kbproto,xproto,inputproto。但是编译很容易,仅提供下载链接

1
2
3
4
5
6
7
8
9
10
11
https://www.x.org/releases/X11R7.7/src/lib/xtrans-1.2.7.tar.gz
https://www.x.org/releases/X11R7.7/src/proto/xextproto-7.2.1.tar.gz
https://www.x.org/releases/X11R7.7/src/proto/kbproto-1.0.6.tar.gz
https://www.x.org/releases/X11R7.7/src/proto/xproto-7.0.23.tar.gz
https://www.x.org/releases/X11R7.7/src/proto/inputproto-2.2.tar.gz
https://www.x.org/releases/X11R7.7/src/xcb/libpthread-stubs-0.3.tar.gz
https://www.x.org/releases/X11R7.7/src/xcb/xcb-proto-1.7.1.tar.gz
https://www.x.org/releases/X11R7.7/src/xcb/libxcb-1.8.1.tar.gz
https://www.x.org/releases/X11R7.7/src/lib/libSM-1.2.1.tar.gz
https://www.x.org/releases/X11R7.7/src/lib/libICE-1.0.8.tar.gz
https://www.x.org/releases/X11R7.7/src/lib/libXt-1.1.3.tar.gz

相当于人工检查依赖环境,仅仅是繁琐而已,并不困难

1
2
3
4
# 安装X11
wget -4 https://www.x.org/releases/X11R7.7/src/lib/libX11-1.5.0.tar.gz
tar -zxvf libX11-1.5.0.tar.gz && cd libX11-1.5.0
./configure --prefix=$HOME/usr && make && make install

编译案例

安装zsh

zsh或许可以认为是最好的shell,用过zsh的人都不会想着bash了。不过zsh自定义配置,可供选择的插件以及主题实在是太多,因此一定要搭配oh-my-zsh。zsh依赖ncurses.

1
2
3
4
5
wget -O zsh.tar.gz https://sourceforge.net/projects/zsh/files/latest/download
tar -zxvf zsh.tar.gz && cd zsh
export CPPFLAGS="-I$HOME/usr/include/" LDFLAGS="-L$HOME/usr/lib"
../configure --prefix=$HOME/usr --enable-shared
make && make install

由于没有root权限,无法使用chsh,只能通过在~/.bashrc添加exec $HOME/usr/bin/zsh -l保证登陆的时候自动切换成zsh。其次, zsh搭配oh-my-zsh才完整, 只不过这里只能手动安装了。

1
2
3
4
5
6
7
8
# 从github上克隆oh-my-zsh
git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh
# 用oh-my-zsh的zsh配置文件替代
cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zhsrc
# 安装一些字体, 不然一些主题会显示异常
cd src
git clone https://github.com/powerline/fonts.git --depth=1
cd fonts && ./install.sh

重启一下终端,后面根据需要调整配置文件里的参数。

编译tmux

tmux和screen类似,也是文本终端神器, 依赖于libevent和ncurses.

1
2
3
4
5
6
7
8
export CPPFLAGS="-I$HOME/usr/include -I$HOME/usr/include/ncurses"
export LDFLAGS="-L$HOME/usr/lib -L$HOME/usr/lib64"
mkdir -p src && cd src
git clone https://github.com/tmux/tmux.git
cd tmux
sh autogen.sh
./configure --prefix=$HOME/usr
make && make install

编译R语言

由于我自己编译完全版的GCC套餐,很多之前的gfortran不存在的问题也就不存在了(管理员安装了Java)。此外,R还需要gnu readline, pcre > 8.2, x11。当然这些函数包都在之前安装好了。

一些依赖库

1
2
# 安装tidyverse发现xm12需要libiconv的libiconv.so.2
https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz

正式安装

1
2
3
4
wget https://cran.r-project.org/src/base/R-3/R-3.4.2.tar.gz
tar -zxvf R-3.4.2.tar.gz && cd R-3.4.2/
./configure --prefix=$HOME/R
make && make install

R configure

到此,我可以说Linux平台下即便我没有root权限,也没有多少软件包是我所不能手工编译。

使用RepeatModeler和RepeatMasker注释基因组重复序列

重复序列注释有两种常用策略,基于同源序列相似性和基于重复序列结构特征。RepeatMasker是基于同源序列相似性注释序列的常用工具, RepeatModeler可用来从头对基因组的重复序列家族进行建模注释,它的核心组件是RECON和RepatScout。

这篇教程介绍如何使用RepeatModeler从头鉴定基因组的重复序列,之后用RepeatMasker根据自定义的重复序列库注释基因组的重复序列。

软件安装

原本的RepeatMaskerRepeatModeler的手动安装需要配置很多文件,但是利用bioconda就只用一行命令。

1
conda create -n repeat repeatmasker repeatmodeler

之后在RepeatMasker环境下配置运行环境。由于我的miniconda装在~/opt路径下,因此对应的RepeatMasker路径为~/opt/miniconda3/envs/repeat/share/RepeatMasker/

1
2
3
conda activate repeat
cd ~/opt/miniconda3/envs/repeat/share/RepeatMasker/
perl ./configure

这一步只需要配置好比对软件

配置比对工具

之后就会显示RepeatMasker已经配置完毕,其中Dfam_3.0是用于注释的数据库。

配置完成

软件运行

以拟南芥的参考基因组为例,基因组命名为”Athaliana.fa”

第一步:为RepeatModeler创建索引数据库

1
2
3
BuildDatabase -name ath -engine ncbi Athaliana.fa
# -engine ncbi: 表示使用rmblast
# -name aht: 表示数据库的名字为ath

第二步:运行RepeatModeler从头预测

1
2
3
4
RepeatModeler -database ath -engine ncbi -pa 20 &> ath.out &
# -database 要和上一步一致
# -engine 要和上一步一致
# -pa 表示线程数

这一步运行时间相对比较久,和线程数有关。运行中的的文件存放在RM_.xxx文件夹下

1
2
3
4
5
6
7
8
9
10
11
RM_100741.WedSep181006282019
├── consensi.fa
├── consensi.fa.classified
├── consensi.fa.masked
├── families-classified.stk
├── families.stk
├── round-1
├── round-2
├── round-3
├── round-4
└── round-5

运行结束后,就得到了ath-families.faath-families.stk。 前者是找到的重复序列,后者是Stockholm格式的种子联配文件(seed alignment file), 可以用util/dfamConsensusTool.pl上传到Dfam_consensus数据库中。

ath-families.fa的fasta的序列部分格式为>repeatname#class/subclass,用于表明每个重复序列的归类。

第三步:根据自定义的重复序列数据库注释基因组

1
RepeatMasker -lib ath-families.fa -e ncbi -dir . Athaliana.fa

RepeatMasker比较常用的参数如下

  • -e: 搜索引擎,默认都选择ncbi
  • -pa: 并行计算,多线程
  • -s, -q, -qq: 搜索速度,速度和敏感度成反比
  • -lib: 自定义重复数据库
  • -species: 指定物种,例如human, mouse, arabidopsis
  • -gff: 额外输出GFF文件

输出结果中, 以.masked结尾的是用N屏蔽后的序列,以tal结尾的则是统计各种重复序列的比例。

使用wgd进行全基因组复制分析

使用wgd进行全基因组复制分析

因为全基因组复制(Whole genome duplications, WGD)是生物进化的重要因素之一, 所以WGD分析也是基因组分析经常用到的一种分析方法。举个例子,我们之所以能在多条染色体之间发现一些古老基因连锁现象,是因为被子植物在过去2亿年时间里就出现了多次的全基因组复制和基因丢失事件(见下图,Tang et al., 2008)

基因组进化

古老WGD检测有两种方法,一种是共线性分析,另一种则是根据Ks分布图。其中Ks定义为平均每个同义位点上的同义置换数,与其对应的还有一个Ka,指的是平均每个非同义位点上的非同义置换数。

如果没有WGD或是大片段重复,那么基因组中的旁系同源基因的同义置换符合指数分布(exponential distribution), 反之,Ks分布图中就会出现一个由于WGD导致的正态分布峰(normal distributed peak). 而古老WGD的年龄则可通过分析这些峰中的同源置换数目来预测(Tiley et al., 2018)。

以发表在Science上的_Papaver somniferum_ L. 基因组文章中的图Fig 1C为例,文章分别分析了_P. somniferum_ 和其他几个物种的Ks分布。从Ks分布图可以看到_P. somniferum_经历了一次比较近的WGD事件(Guo et al., 2018)。

Ks plot

文章中关于WGD的分析流程参考附录8.1 Whole genome duplication in opium poppy genome, 我总结了关键的几点

  • 使用megablast进行自比对,寻找基因组中共线性的区块
  • 使用BLASTP基于RBH( reciprocal best hits )进行蛋白之间的相互比对
  • 使用KaKs_Calculator基于YN模型计算RBH基因对中的Ks(synonymous substitution rate)
  • 为了区分Ks中peak是WGD事件还是小规模重复,作者使用MCScanX进行共线性分析,发现93.9%的RBH基因都是基因组内共线性

目前WGD的分析流程也有人发了文章,我通过关键字”wgd pipeline”搜索找到了如下几个:

这一篇介绍的是wgd的用法。

软件安装

wgd目前无法用bioconda直接安装,所以安装起来稍显麻烦,这是因为它的依赖软件有点多。wgd依赖于以下软件

  • BLAST
  • MCL
  • MUSCLE/MAFFT/PRANK
  • PAML
  • PhyML/FastTree
  • i-ADHoRe

但是好消息是它依赖的软件大部分都可以用bioconda进行安装

1
2
conda create -n wgd python=3.5 blast mcl muscle mafft prank paml  fasttree cmake libpng mpi=1.0=mpich
conda activate wgd

这里选择了mpi=1.0=mpich, 原因是i-adhore依赖于mpich. 如果安装了openmpi就会出现error while loading shared libraries: libmpi_cxx.so.40: cannot open shared object file: No such file or directory

之后的安装就简单多了

1
2
3
4
5
git clone https://github.com/arzwa/wgd.git
cd wgd
pip install .
# 或者一行命令
pip install git+https://github.com/arzwa/wgd.git

对于i-ADHoRe,需要先在http://bioinformatics.psb.ugent.be/webtools/i-adhore/licensing/同意许可,才能下载i-ADHoRe-3.0

由于我的miniconda3安装在~/opt/下,所以安装路径为~/opt/miniconda3/envs/wgd/

1
2
3
4
5
6
tar -zxvf i-adhore-3.0.01.tar.gz
cd i-adhore-3.0.01
mkdir -p build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=~/opt/miniconda3/envs/wgd/
make -j 4
make insatall

软件介绍

WGD一共有9个子模块

  • kde: 对Ks分布进行KDE拟合
  • ksd : Ks分布构建
  • mcl:All-vs-ALl的BLASP比对 + MCL分类分析.
  • mix: Ks分布的混合建模.
  • pre: 对CDS文件进行预处理
  • syn: 调用I-ADHoRe 3.0利用GFF文件进行共线性分析
  • viz: 绘制柱状图和密度图
  • wf1: 全基因组旁系同源基因组(paranome)的Ks标准分析流程,调用mcl, ksd和syn
  • wf2: one-vs-one 同源基因(ortholog)的Ks标准分析流程,调用wcl和ksd

流程示意图如下:

工作流程

使用方法

以甘蔗的基因组 Saccharum spontaneum L 为例,基因组为8倍体,共32条染色体(2n = 4x8 = 32)

下载CDS和GFF注释文件 tutorial

1
2
3
4
mkdir -p wgd_tutorial && cd wgd_tutorial
wget http://www.life.illinois.edu/ming/downloads/Spontaneum_genome/Sspon.v20190103.cds.fasta.gz
wget http://www.life.illinois.edu/ming/downloads/Spontaneum_genome/Sspon.v20190103.gff3.gz
gunzip *.gz

先用conda activate wgd启动我们的分析环境,然后就开始分析了

第一步: 使用wgd mcl鉴定基因组内的同源基因

1
2
3
4
wgd mcl -n 20 --cds --mcl -s Sspon.v20190103.cds.fasta -o Sspon_cds.out
# -n: 线程
# --cds: 输入为cds
# --mcl: mcl聚类

这一步运行过程中,wgd会先检查cds序列是否有效,也就是是否以ATG(起始密码子)开头,且以TAA/TAG/TGA(终止密码子)结尾,然后将cds转成氨基酸序列,之后用Blastp进行相互比对,然后根据blastp结果用mcl聚类的方式寻找旁系同源基因。

输出结果在Sspon_cds.out,有两个结果输出

  • blast.tsv: BLASTP的outfmt6输出结果
  • blast.tsv.mcl: MCL聚类结果,每一行可以认为是一个基因家族(gene family)

第二步: 使用wgd ksd构建Ks分布

1
wgd ksd --n_threads 80 Sspon_cds.out/Sspon.v20190103.cds.fasta.blast.tsv.mcl Sspon.v20190103.cds.fasta

这一步也是先过滤cds中的无效数据,然后用mafft(默认)/muscle/prank对每个基因家族进行多重序列联配,用codeml计算dN/dS, 用alc/fasttree(默认)/phyml建树

输出结果在wgd_ksd目录下

  • ks.tsv: 每个基因家族中基因对的各项统计,其中包括Ka和Ks
  • ks.svg: Ks分布,见下图
Ks分布

第三步: 如果基因组质量不错,那么可以使用wgd syn进行共线性分析。它能帮我们找到基因组内的共线性区块,以及相应的锚定点

1
2
3
4
5
wgd syn --feature gene --gene_attribute ID \
-ks wgd_ksd/Sspon.v20190103.cds.fasta.ks.tsv \
Sspon.v20190103.gff3 Sspon_cds.out/Sspon.v20190103.cds.fasta.blast.tsv.mcl
#--feature: 从第三列选择特征
#--gene_attribute: 从第九列提取编号

输出图片以.svg结尾,如下所示,图中颜色红蓝代表Ks得分。

wgd syn

Ks的下游分析通常还包括对Ks分布的统计建模,这可以使用wgd kde进行核密度拟合(Kernel density estimate, KDE)或用wgd mix建立高斯混合模型(Gaussian mixture models)

1
2
3
4
# KDE
wgd kde wgd_ksd/Sspon.v20190103.cds.fasta.ks.tsv
# Gaussian
wgd mix wgd_ksd/Sspon.v20190103.cds.fasta.ks.tsv

wgd kde输出kde.svg, 而wdg mix则生成一个 wgd_mix文件夹。

混合模型常用于基于Ks分布研究WGD。基于一些基本的分子进化假设,我们预期Ks分布中由WGD导致的peak应该符合高斯分布,能够用log-normal分布近似。但是考虑到混合模型容易过拟合,特别是Ks分布,因此作者不建议使用混合模型作为多次WGD假设的正式统计测试。

参考文献

Tang, H., Bowers, J.E., Wang, X., Ming, R., Alam, M., and Paterson, A.H. (2008). Synteny and Collinearity in Plant Genomes. Science 320, 486–488.

Tiley, G.P., Barker, M.S., and Burleigh, J.G. (2018). Assessing the Performance of Ks Plots for Detecting Ancient Whole Genome Duplications. Genome Biol Evol 10, 2882–2898.

Guo, L., Winzer, T., Yang, X., Li, Y., Ning, Z., He, Z., Teodor, R., Lu, Y., Bowser, T.A., Graham, I.A., et al. (2018). The opium poppy genome and morphinan production. Science 362, 343–347.

Zwaenepoel, A., and Van de Peer, Y. (2019). wgd—simple command line tools for the analysis of ancient whole-genome duplications. Bioinformatics 35, 2153–2155.

Tue Sep 10 2019 05:45:43 GMT+0000 (Coordinated Universal Time)

1
2
3
4
5
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --set show_channel_urls yes
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/

使用refgenie管理你的参考基因组

在服务器管理初期,我管理参考基因组的方法非常简单,就是以物种名建立一个文件夹,然后把和该物种有关的FASTA文件,GFF文件都放到该文件中,之后在文件夹中建立不同软件的索引。

当然目前已经有一些软件可以帮助你进行管理,比如说refgenie。它是一个Python编写的参考基因组管理工具,软件的设计思路如下:

设计思路

你既可以在本地自己构建,也可以从它的服务器上下载已有的物种。

软件安装

refgenie的安装非常简单,只需要一行代码

1
2
# 建议先安装miniconda
pip install refgenie

之后,你需要初始化一个文件夹,之后下载的基因组都会在该目录下

1
2
mkdir -p ~/reference/
refgenie init -c ~/reference/genome_config.yaml

将这一行export REFGENIE=~/reference/genome_config.yaml根据所用的SHELL加入到对应的.bashrc.zshrc

软件使用

我们可以使用refgenie listr去查看目前refgenomes服务器中已经有的参考基因组,输出信息如下

1
2
3
4
5
6
7
Querying available assets from server: http://refgenomes.databio.org/assets
Remote genomes: hg19, hg19_cdna, hg38, hg38_cdna
Remote assets:
hg19: bismark_bt1_index; bismark_bt2_index; bowtie2_index; bwa_index; fasta; hisat2_index
hg19_cdna: bowtie2_index; hisat2_index; kallisto_index; salmon_index
hg38: bismark_bt1_index; bismark_bt2_index; bowtie2_index; bwa_index; fasta; hisat2_index
hg38_cdna: bowtie2_index; hisat2_index; kallisto_index; salmon_index

我们可以用refgenie pull来下载数据

1
refgenie pull --genome hg38 --asset bowtie2_index

当然更常见的情况是,你的物种并不在已有的列表中,以及这是一个国外服务器,你甚至都无法拉取列表,所以我们需要用refgenie build来构建参考基因组。

我们以拟南芥参考基因组为例,我们需要先从EnsemblPlants上下载参考基因组序列

1
wget 'ftp://ftp.ensemblgenomes.org/pub/plants/release-44/fasta/arabidopsis_thaliana/dna/Arabidopsis_thaliana.TAIR10.dna.toplevel.fa.gz'

先导入fasta文件

1
refgenie build --genome TAIR10 --asset fasta --fasta Arabidopsis_thaliana.TAIR10.dna.toplevel.fa.gz

建立bwa的索引

1
refgenie build --genome TAIR10 --asset bwa_index --fasta Arabidopsis_thaliana.TAIR10.dna.toplevel.fa.gz

此时检查~/reference/genome_config.yaml文件,里面记录了刚才新建文件的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
config_version: 0.2
genome_folder: /path/to/reference
genome_server: http://refgenomes.databio.org
genomes:
TAIR10:
assets:
fasta:
path: fasta/TAIR10.fa
asset_description: Sequences in the FASTA format
fai:
path: fasta/TAIR10.fa.fai
asset_description: Indexed fasta file, produced with samtools faidx
chrom_sizes:
path: fasta/TAIR10.chrom.sizes
asset_description: Chromosome sizes file
bwa_index:
path: bwa_index
asset_description: Genome index for Burrows-Wheeler Alignment Tool, produced with bwa index

和build相关的详细信息参考http://refgenie.databio.org/en/latest/build/

参考资料

官方文档: http://refgenie.databio.org/en/latest/refgenie_interfaces.svg