用R语言做GWAS分析

该流程有:GWAS的QC,PCA分析,Manhattan图,QQ图,候选位点的功能分析

参考学习资料:Genome-wide association studies in R

本教程通过使用三个东南亚人群绘制胆固醇水平的遗传决定因素图来说明全基因组关联(GWA)研究的力量。
全文翻译版:

先了解一下背景:
早在19世纪末查尔斯·达尔文(Charles Darwin)提出自然选择和性选择理论以来,基因在不同个体中由不同的等位基因(即variants)代表的潜在作用尚未阐明。他年轻的当代同事格雷戈尔·孟德尔(Gregor Mendel)在确定了性状遗传和基因分离的机制后,触及了皮毛。在接下来的几年里,以罗纳德·费舍尔(Ronald Fisher)为先驱的经典遗传学家逐渐解开了表型(即可观察性状)的遗传基础,他引入了遗传方差等关键概念。当时出现的基因型(即genetic make-up)概念要求开发多态遗传标记。早期,基因分型的基础是确定位点的等位基因组成(染色体区域的loose definition),后来是拷贝数变异(CNVs)、短串联重复序列(STRs)和单核苷酸多态性(SNPs)。人类和包括植物在内的许多其他生物都是二倍体(即每条染色体携带两个拷贝),这意味着含有等位基因的双等位标记,例如A和一个区分为AA(homozygous dominant,纯合显性)、AA(heterozygous,杂合子)和AA(homozygous recessive,纯合隐性)的个体
镰状细胞性贫血和血友病等疾病的遗传基础的早期发现在很大程度上要归功于它们相对简单的遗传结构-很少有高外显性的突变,因此更容易识别。可以理解的是,更复杂的长期存在的多基因疾病,如神经退行性阿尔茨海默病和帕金森,目前仍在调查中。要表征这些性状,即许多基因相互作用的产物,影响很小,需要大量的遗传资源以及灵活的统计方法,这在当前的组学数据和高性能计算时代是不成问题的。

全基因组关联研究 Genome-wide association studies

全基因组关联(GWA)研究扫描整个物种基因组,寻找多达数百万个SNP与给定的感兴趣性状之间的关联。值得注意的是,感兴趣的性状实际上可以是归因于群体的任何一种表型,无论是定性的(例如疾病状态)还是数量的(例如身高)。本质上,给定p个SNP和n个样本或个体,GWA分析将拟合p个独立的单变量线性模型,每个模型基于n个样本,使用每个SNP的基因型作为感兴趣性状的预测因子。每个p检验中的关联显著性(P值)由相应SNP的系数估计确定(从技术上讲,关联的显著性为P())。请注意,因为这些测试是独立的并且数量相当多,所以设置并行化的GWA分析有很大的计算优势(我们很快就会这样做)。相当合理的是,有必要使用诸如BonferroniBenjamini-Hochbergfalse discovery rate(FDR)等多种假设检验方法来调整所得到的P值。GWA研究现在在许多不同物种的遗传学中是司空见惯的。

关联映射与链接映射Association mapping vs. linkage mapping

人们常常分不清关联和连锁作图或数量性状位点(quantitative trait loci,QTL)作图之间的区别。虽然在概念上相似,但实际上它们的工作方式是相反的。两者的主要区别之一是,关联作图依赖于对无关个体的高密度SNP基因分型,而连锁作图则依赖于在受控育种实验中分离出的相当少的标记-不出所料,QTL作图很少在人类身上进行。重要的是,关联作图给你的是基因组中的点关联,而连锁作图给你的是QTL,染色体区域。
本教程涵盖了进行GWA分析时要考虑的基本方面,从基因型和表型数据的预处理到结果的解释。我们将使用316名中国人、印度人和马来人的混合群体,这些群体最近通过高通量SNP芯片测序、转录和脂质组学进行了表征(Saw et al., 2017)。更具体地说,我们将寻找 >250万个SNP标志物和胆固醇水平之间的联系。最后,我们将使用USCS基因组浏览器探索候选SNP的附近,以获得功能性位点(gain functional insights)。这里显示的方法在很大程度上是基于Reed等人在2015年概述的教程。R脚本和一些数据可以在我的存储库(repository)中找到,但是您仍然需要从这里下载组学(omics)数据。请按照repo中的说明操作。

开启R操作Let’s get started with R

读入数据

让我们首先从这三个民族中的每一个导入由PLINK转换的.bed.fam.bim Illumina文件。我们将使用snpStats包中的函数read.plink,并在本教程的其余部分中处理生成的对象。此函数读取.bed.fam.bim,并创建一个包含三个元素的列表-$genotypes, $fam和$map。第一个包含从所有样本中确定的所有SNPs,第二个包含关于家系和性别的信息,第三个包含SNPs的基因组坐标。到目前为止,我们总共有323个个体(110个中国人,105个印度人和108个马来人)和2527458个SNP。接下来,我们将把存储在$genotype中的Illumina SNP ID更改为更传统的rs ID,这将允许我们在USCS基因组浏览器中放大候选SNP周围的基因组区域。我准备了一个表来建立两者之间的对应关系,这样我们就可以很容易地交换ID。

library(snpStats)
load("conversionTable.RData")
 
pathM <- paste("Genomics/108Malay_2527458snps", c(".bed", ".bim", ".fam"), sep = "")
SNP_M <- read.plink(pathM[1], pathM[2], pathM[3])
 
pathI <- paste("Genomics/105Indian_2527458snps", c(".bed", ".bim", ".fam"), sep = "")
SNP_I <- read.plink(pathI[1], pathI[2], pathI[3])
 
pathC <- paste("Genomics/110Chinese_2527458snps", c(".bed", ".bim", ".fam"), sep = "")
SNP_C <- read.plink(pathC[1], pathC[2], pathC[3])
 
# Merge the three SNP datasets
SNP <- rbind(SNP_M$genotypes, SNP_I$genotypes, SNP_C$genotypes)
 
# Take one bim map (all 3 maps are based on the same ordered set of SNPs)
map <- SNP_M$map
colnames(map) <- c("chr", "SNP", "gen.dist", "position", "A1", "A2")
 
# Rename SNPs present in the conversion table into rs IDs
mappedSNPs <- intersect(map$SNP, names(conversionTable))
newIDs <- conversionTable[match(map$SNP[map$SNP %in% mappedSNPs], names(conversionTable))]
map$SNP[rownames(map) %in% mappedSNPs] <- newIDs

接下来,我们导入并合并三个脂质数据集(存储为.txt),并确定哪些样本同时存在于SNP和脂质数据集中。在下面的分析中,我们将使用在两个平台上分析的样本子集,总共319个。最后,我们创建一个genData列表,该列表存储合并的SNP数据($SNP)、三个$map之一(因为它们都相同)($map)和合并的脂质数据($lip)。最后,让我们为后续步骤保存RAM,并在将genData保存到.RData文件之后删除所有文件。

# Load lipid datasets & match SNP-Lipidomics samples
lipidsMalay <- read.delim("Lipidomic/117Malay_282lipids.txt", row.names = 1)
lipidsIndian <- read.delim("Lipidomic/120Indian_282lipids.txt", row.names = 1)
lipidsChinese <- read.delim("Lipidomic/122Chinese_282lipids.txt", row.names = 1)
all(Reduce(intersect, list(colnames(lipidsMalay),
colnames(lipidsIndian),
colnames(lipidsChinese))) == colnames(lipidsMalay)) # TRUE
lip <- rbind(lipidsMalay, lipidsIndian, lipidsChinese)
 
matchingSamples <- intersect(rownames(lip), rownames(SNP))
SNP <- SNP[matchingSamples,]
lip <- lip[matchingSamples,]
 
genData <- list(SNP = SNP, MAP = map, LIP = lip)
save(genData, file = "PhenoGenoMap.RData")
 
# Clear memory
rm(list = ls())

预处理

让我们重新加载genData并清理它。简而言之,数据的预处理包括:

  • discarding SNPs with call rate < 1 or MAF < 0.1
  • discarding samples with call rate < 100%, IBD kinship coefficient > 0.1 or inbreeding coefficient |F| > 0.1

Call rate是基因分型的SNP(或样本)的比例。例如,特定SNP(样本)的Call rate为0.95意味着缺少5%的值。因为我们有如此多的SNP,所以通过为SNP和样本设置1的Call rate阈值,我们可以在$SNP矩阵中绝对不遗漏任何值。如果您希望放宽阈值并容忍缺失值,请记住您需要运行一个完整的过程来输入这些值。如果你有兴趣的话可以查阅Reed等人,2015年描述了一种基于PCA的聚类方法,该方法利用千人基因组计划作为proxy。
次要等位基因频率(Minor-allele frequency,MAF)表示每个SNP最不常见的等位基因的比例。当然,很难检测到与罕见变异的关联,这就是为什么我们选择低MAF值的原因。我读过的大多数GWA研究通常报告MAF阈值为0.05。在这里,我选择更严格的0.1,因为同样,我们有大量的数据,而且由于所有这些都是为了说明的目的,我们想要进行快速的GWA分析。

library(snpStats)
library(doParallel)
library(SNPRelate)
library(GenABEL)
library(dplyr)
source("GWASfunction.R")
load("PhenoGenoMap.RData")
 
# Use SNP call rate of 100%, MAF of 0.1 (very stringent)
maf <- 0.1
callRate <- 1
SNPstats <- col.summary(genData$SNP)
 
maf_call <- with(SNPstats, MAF > maf & Call.rate == callRate)
genData$SNP <- genData$SNP[,maf_call]
genData$MAP <- genData$MAP[maf_call,]
SNPstats <- SNPstats[maf_call,]

接下来,我们需要考虑表现出过度杂合性的样本-从技术上讲,偏离哈笛-温伯格平衡(Hardy-Weinberg equilibrium,HWE),,其中p和q是两个等位基因A和a的频率之和为1,2pq是杂合子个体的频率,假设为双等位SNPs。因此,我们确定近亲交配系数|F|,,其中H和2pq分别是观察到的和预期的杂合度(即每个样本中杂合性SNP的比例)。您将注意到,在实践中,与使用我们将使用的比例不同,我们使用的计数仍然会给出相同的结果。总体而言,大|F|和小|F|可能分别表示样本质量较差或近亲繁殖。我们将排除的样品,如Reed等人,2015年所述。

# Sample call rate & heterozygosity
callMat <- !is.na(genData$SNP)
Sampstats <- row.summary(genData$SNP)
hetExp <- callMat %*% (2 * SNPstats$MAF * (1 - SNPstats$MAF)) # Hardy-Weinberg heterozygosity (expected)
hetObs <- with(Sampstats, Heterozygosity * (ncol(genData$SNP)) * Call.rate)
Sampstats$hetF <- 1-(hetObs/hetExp)
# Use sample call rate of 100%, het threshold of 0.1 (very stringent)
het <- 0.1 # Set cutoff for inbreeding coefficient;
het_call <- with(Sampstats, abs(hetF) < het & Call.rate == 1)
genData$SNP <- genData$SNP[het_call,]
genData$LIP <- genData$LIP[het_call,]

最后,我们将使用基于血统认同的亲属系数(IBD)来调查样本之间的关联性。请注意,SNPRelate包中的这些函数需要GDS文件。为此,我们首先需要将这三个群体中的.bed、.fam和.bim文件聚合到ConvertGDS中。函数snpgdsBED2GDS2创建此部分分析所需的GDS。为了确定样本对之间的亲缘关系系数,我们将使用不相关SNP的子集,以便有无偏见的估计。为此,我们将使用连锁不平衡(LD)作为标记间相关性的度量。LD的范围从0到1,它的值越高,两个SNP共分离的可能性就越大,因此就会相互关联。在这里,我们将利用LD < 0.2(p~12000)的SNPs子集来确定IBD的亲缘关系系数。我用snpgdsLDpruning函数计算LD花了大约2个小时,所以请耐心等待。接下来,继Reed等人,2015年,我们将排除所有亲属关系系数 > 0.1的样本。

# LD and kinship coeff
ld <- .2
kin <- .1
snpgdsBED2GDS(bed.fn = "convertGDS.bed", bim.fn = "convertGDS.bim",
fam.fn = "convertGDS.fam", out.gdsfn = "myGDS", cvt.chr = "char")
genofile <- snpgdsOpen("myGDS", readonly = F)
gds.ids <- read.gdsn(index.gdsn(genofile, "sample.id"))
gds.ids <- sub("-1", "", gds.ids)
add.gdsn(genofile, "sample.id", gds.ids, replace = T)
geno.sample.ids <- rownames(genData$SNP)
# First filter for LD
snpSUB <- snpgdsLDpruning(genofile, ld.threshold = ld,
sample.id = geno.sample.ids,
snp.id = colnames(genData$SNP))
snpset.ibd <- unlist(snpSUB, use.names = F)
# And now filter for MoM
ibd <- snpgdsIBDMoM(genofile, kinship = T,
sample.id = geno.sample.ids,
snp.id = snpset.ibd,
num.thread = 1)
ibdcoef <- snpgdsIBDSelection(ibd)
ibdcoef <- ibdcoef[ibdcoef$kinship >= kin,]
 
# Filter samples out
related.samples <- NULL
while (nrow(ibdcoef) > 0) {
# count the number of occurrences of each and take the top one
sample.counts <- arrange(count(c(ibdcoef$ID1, ibdcoef$ID2)), -freq)
rm.sample <- sample.counts[1, 'x']
cat("Removing sample", as.character(rm.sample), 'too closely related to', sample.counts[1, 'freq'],'other samples.\n')
 
# remove from ibdcoef and add to list
ibdcoef <- ibdcoef[ibdcoef$ID1 != rm.sample & ibdcoef$ID2 != rm.sample,]
related.samples <- c(as.character(rm.sample), related.samples)
}
genData$SNP <- genData$SNP[!(rownames(genData$SNP) %in% related.samples),]
genData$LIP <- genData$LIP[!(rownames(genData$LIP) %in% related.samples),]

经过预处理,我们剩下316个样本(110个中国人,100个印度人和106个马来人),其特征是795,668个SNP markers和282个lipid species。请注意,您的样本大小可能略有不同,因为LD修剪过程是随机的。

分析

主成分分析(PCA)

既然我们已经完成了预处理,那么使用主成分分析(Principal Component Analysis,PCA)来检查基因型数据中最大的变异源并寻找异常值或聚类模式可能是个好主意。因为我们使用的是S4对象,所以我们将使用SNPRelate包的PCA函数snpgdsPCA。让我们绘制前两个主成分(PCs)。

# PCA
pca <- snpgdsPCA(genofile, sample.id = geno.sample.ids, snp.id = snpset.ibd, num.thread = 1)
pctab <- data.frame(sample.id = pca$sample.id,
PC1 = pca$eigenvect[,1],
PC2 = pca$eigenvect[,2],
stringsAsFactors = F)
 
origin <- read.delim("countryOrigin.txt", sep = "\t")
origin <- origin[match(pca$sample.id, origin$sample.id),]
 
pcaCol <- rep(rgb(0,0,0,.3), length(pca$sample.id)) # Set black for chinese
pcaCol[origin$Country == "I"] <- rgb(1,0,0,.3) # red for indian
pcaCol[origin$Country == "M"] <- rgb(0,.7,0,.3) # green for malay
 
png("PCApopulation.png", width = 500, height = 500)
plot(pctab$PC1, pctab$PC2, xlab = "PC1", ylab = "PC2", col = pcaCol, pch = 16)
abline(h = 0, v = 0, lty = 2, col = "grey")
legend("top", legend = c("Chinese", "Indian", "Malay"), col = 1:3, pch = 16, bty = "n")
dev.off()
用R语言做GWAS分析_第1张图片
image.png

不出所料,795,668个SNP标记清楚地描绘了这三个群体。结果还表明,华人和马来人之间的距离比印度人更近(这一观察结果将更好地解决,例如,层次聚类hierarchical clustering)。此外,前两PCs没有发现明显的异常值。一切正常,可以进行GWA了。

Genome-Wide Association

最后,我要说的是“抵抗”(the pièce de résistance)。从282种脂质中,我选择了胆固醇,这是最熟悉的一种,作为我感兴趣的特性。您可以完全自由地选择不同的脂质种类并继续操作。我们将使用Reed等人在2015年提供的GWA函数,但做了一些较小的修改。我建议您打开脚·GWASfunction.R并浏览一下。这是一个优秀的、有良好文档记录的并行化实现。请注意,glm函数用于确定每个SNP与感兴趣的性状之间关联的重要性。如果您想知道,glmlm更通用,因为它进行高斯、泊松、二项式和多项式回归/分类,这取决于您感兴趣的性状是如何分布的(表型文件中的所有脂质都是高斯的)。此GWA函数不会创建变量,而是编写一个.txt汇总表,列出每个SNP的系数估计$\beta4,t和相应的P值,以及相应的基因组坐标。运行GWA功能使用了2012年中的MacBook Pro(8Gb, 2.9 GHz Intel Core i7)大约1.5个小时。

# Choose trait for association analysis, use colnames(genData$LIP) for listing
# NOTE: Ignore the first column of genData$LIP (gender)
target <- "Cholesterol"
 
phenodata <- data.frame("id" = rownames(genData$LIP),
"phenotype" = scale(genData$LIP[,target]), stringsAsFactors = F)
 
# Conduct GWAS (will take a while)
start <- Sys.time()
GWAA(genodata = genData$SNP, phenodata = phenodata, filename = paste(target, ".txt", sep = ""))
Sys.time() - start # benchmark

一旦完成,我们就可以用所谓的曼哈顿图(Manhattan plots)来可视化结果。我们只需要加载上一步中编写的.txt汇总表,添加一个带有的列,并根据所有SNP的基因组坐标绘制这些显著性估计。

# Manhattan plot
GWASout <- read.table(paste(target, ".txt", sep = ""), header = T, colClasses = c("character", rep("numeric",4)))
GWASout$type <- rep("typed", nrow(GWASout))
GWASout$Neg_logP <- -log10(GWASout$p.value)
GWASout <- merge(GWASout, genData$MAP[,c("SNP", "chr", "position")])
GWASout <- GWASout[order(GWASout$Neg_logP, decreasing = T),]
 
png(paste(target, ".png", sep = ""), height = 500,width = 1000)
GWAS_Manhattan(GWASout)
dev.off()

此外,完整的(resp.虚线)表示1,000,000的Bonferroni调整后的的水平(resp. 10,000)个测试。

image.png

我们看到总共有四个SNP通过了“relaxed” Bonferroni阈值(没有一个通过了“硬”阈值)。这些是SNPs rs7527051(Chr. 1)、rs12140539(与第一个Chr.1)、rs9509213(Chr.13)和rs2250402(Chr.15)。
在继续进行这四个命中之前,将结果P值的分布与随机预期的P值进行对比是有帮助的,以确保没有混淆的系统性偏差。这很容易通过分位数-分位数(Q-Q)图来解决。顾名思义,它根据分位数比较两个分布。在目前的情况下,我们希望将我们的t统计分布与偶然获得的分布进行比较。如果两个分布形状相同,Q-Q图将显示一条 x=y线。因此,来自可靠的GWA分析的Q-Q曲线图将显示一条 x=y线,其中只有很少的偏差值暗示关联。否则,如果这条线向上或向下移动,GWA分析可能会受到混杂因素的影响。我们将使用 GenABEL包中的函数 estlambda绘制Q-Q图。

# QQ plot using GenABEL estlambda function
png(paste(target, "_QQplot.png", sep = ""), width = 500, height = 500)
lambda <- estlambda(GWASout$t.value**2, plot = T, method = "median")
dev.off()

image.png

得到的Q-Q图清楚地描绘了一条趋势线(,红色),与x=y(黑色)重叠,并在右尾略有偏差,因此我们可以对我们的结果充满信心。

Functional insights into candidate markers

最后,我们将使用USCS Genome Browser(进入Genome Browser,在文本框中插入SNP ID,输入并缩小)搜索其附近的基因,试图找到这四个候选SNP的功能相关性。我发现rs9509213(Chr.13)正好落在CRYL1(crystalline λ1,内含子序列)上,使其成为后续研究的感兴趣候选位点。

用R语言做GWAS分析_第2张图片
image.png

SNP rs9509213在图的底部显示为黑色文本框,其坐标以垂直黄色线突出显示。CRYL1基因模型显示在顶部,在染色体模型的下方(最上面)。
Interestingly, there is a recent publication that found ‘ POE, HP, and CRYL1 have all been associated with Alzheimer’s Disease, the pathology of which involves lipid and cholesterol pathways.’.
最后,我想评论一下,需要对候选基因进行实验验证,以验证由此产生的SNP-性状关联。在这个特殊的案例中,人们可以测试过度表达或敲除CRYL1的同源基因对小鼠胆固醇水平的影响。

Wrap-up

总而言之,我们已经涵盖了GWA分析的一些最基本的方面:

  • Pre-processing of genotype data based on call rates, MAF, kinship and heterozygosity
  • Investigation of population structure with PCA of the genotype data
  • GWA and visualisation with Manhattan plots
  • Q-Q plots
  • Functional insights into the resulting candidate SNPs

我没有介绍GWA的许多其他方面,例如使用LD图进行精细映射。我鼓励您从表型数据中选择不同的脂质种类,运行分析并解释结果。同样值得注意的是,GWA的现状现在几乎完全基于混合效应线性模型(MLM),该模型将亲属关系和隐含关系视为随机因素,因此比GLM强大得多。我的下一篇帖子可能是关于MLM的。
我要感谢Saw et al., 2017为他们提供了我对组学数据的无限制访问。如果没有这些尽职尽责的科学家和他们透明的研究努力,整个教程根本不会存在。我还重申,本教程在很大程度上是基于Reed et al., 2015年的指南,这是与Sikorska et al., 2013年一样优秀的参考指南。

你可能感兴趣的:(用R语言做GWAS分析)