Population genetics tutorial
Where do you come from?
1 Getting started – R and RStudio download
Before starting to work on some real data, we need to download R statistical software and its integrated development environment RStudio, which help us in programming more easily in R.
1.1 Mac Users
1.1.1 To Install R
- Open an internet browser and go to www.r-project.org.
- Click the “download R” link in the middle of the page under “Getting Started.”
- Select a CRAN location (a mirror site) and click the corresponding link.
- Click on the “Download R for (Mac) OS X” link at the top of the page.
- Click on the file containing the latest version of R under “Files.”
- Save the .pkg file, double-click it to open, and follow the installation instructions.
Now that R is installed, you need to download and install RStudio.
1.1.2 To Install RStudio
- Go to www.rstudio.com and click on the “Download RStudio” button.
- Click on “Download RStudio Desktop.”
- Click on the version recommended for your system, or the latest Mac version, save the .dmg file on your computer, double-click it to open, and then drag and drop it to your applications folder.
1.2 Windows Users
1.2.1 To Install R:
- Open an internet browser and go to www.r-project.org.
- Click the “download R” link in the middle of the page under “Getting Started.”
- Select a CRAN location (a mirror site) and click the corresponding link.
- Click on the “Download R for Windows” link at the top of the page.
- Click on the “install R for the first time” link at the top of the page.
- Click “Download R for Windows” and save the executable file somewhere on your computer. Run the .exe file and follow the installation instructions.
Now that R is installed, you need to download and install RStudio.
1.2.2 To Install RStudio
- Go to www.rstudio.com and click on the “Download RStudio” button.
- Click on “Download RStudio Desktop.”
- Click on the version recommended for your system, or the latest Windows version, and save the executable file. Run the .exe file and follow the installation instructions.
2 Worldwide populations and their DNA – genetic data download
In this tutorial we will analyse the genetic data from the 1000 Genomes Project. The 1000 Genomes Project ran between 2008 and 2015, creating the largest public catalogue of human variation and genotype data (Figure 2.1 and the map at this link).
The 1000 Genomes Project took advantage of developments in sequencing technology, which sharply reduced the cost of sequencing. It was the first project to sequence the genomes of a large number of people, to provide a comprehensive resource on human genetic variation. Data from the 1000 Genomes Project was quickly made available to the worldwide scientific community through freely accessible public databases.
Data from 1000 Genomes Project can bee freely available from this webpage. Unfortunately, we cannot work here the entire set of variants due to the limited computational resources of our laptops. In fact, in the phase3 of the 1000 Genomes Project more that 2000 individuals have been sequenced, generating a dataset of size around 50Gb.
For this reason, I created for you a dataset comprising 786 individuals from eight worldwide populations and around 30,000 genetic variants (“subsampled.bed”, “subsampled.bim”, “subsampled.fam”). Moreover, you also need a file containing the population label for each individual (“kgp3.pheno.tsv”).
Create a directory called “popgen_tutorial” and move these two files into the directory.
3 How DNA is coded in a text file? – genetic data format
3.1 File formats
Let’s have a look at the file. While .fam and .bim are text file, the .bed is a binary file and cannot be opened with a text editor. These are Plink formats currently used in population genetics because they can effectively store a huge amount of genetic data. .fam file contains the sample information, .bim contains the genetic variant information (see the table below) and .bed codes the genotype calls. More information about the file formats in Plink can be found here.
|
|
3.2 Set the working directory
Before starting to work in R environment, we need to specify the working directory, which will be used by R to read and save files. In order to set our wirking directory we need to find the path leading to the directory called “popgen_tutorial”.
## For Mac users you path will be something like this:
#setwd("/Users/I/Desktop/Uni/forensic_genetics/popgen_tutorial")
## Fow Windows users the pathe will be something like this:
# setwd("c:/Documents/Desktop/Uni/forensic_genetics/popgen_tutorial")
# My path is:
setwd("/Users/bioserenina/Google Drive File Stream/Il mio Drive/lezioni_carlo/lezioni/esercitazione/popgen_tutorial")
Given that the genotype information are store in a binary file (.bed) which cannot be opened easily, we will make use of an R library specifically developed to work with genetic data: SNPRelate. You can find here a tutorial on its functions.
As a first step, you need to install the library, one of its dependency (the package “gdsfmt”) as well as other packages we will use in this tutorial using the command below.
# installing some packages
source("http://bioconductor.org/biocLite.R")
biocLite("gdsfmt")
biocLite("SNPRelate")
install.packages(c("ggplot2","RColorBrewer", "knitr", "kableExtra", "dplyr"))
Then, once you have installed a package, you need to “call” the library.
As reported in the SNPRelate tutorial, this library and the gdsfmt package can store the huge amount of genetic information into a different and more efficient file firmat: .gds.
To support efficient memory management for genome-wide numerical data, the gdsfmt package provides the genomic data structure (GDS) file format for array-oriented bioinformatic data, which is a container for storing annotation data and SNP genotypes. In this format each byte encodes up to four SNP genotypes thereby reducing file size and access time.
# create the "test.gds" file starting from our .bed, .bim, and .fam.
bed.fn <- "subsampled.bed"
fam.fn <- "subsampled.fam"
bim.fn <- "subsampled.bim"
snpgdsBED2GDS(bed.fn, fam.fn, bim.fn, "test.gds")
## Start snpgdsBED2GDS ...
## BED file: "subsampled.bed" in the SNP-major mode (Sample X SNP)
## FAM file: "subsampled.fam", DONE.
## BIM file: "subsampled.bim", DONE.
## Wed May 20 09:51:52 2020 store sample id, snp id, position, and chromosome.
## start writing: 786 samples, 29547 SNPs ...
## Wed May 20 09:51:52 2020 0%
## Wed May 20 09:51:52 2020 100%
## Wed May 20 09:51:52 2020 Done.
## Optimize the access efficiency ...
## Clean up the fragments of GDS file:
## open the file 'test.gds' (5.7M)
## # of fragments: 39
## save to 'test.gds.tmp'
## rename 'test.gds.tmp' (5.7M, reduced: 252B)
## # of fragments: 18
## The file name: /Volumes/GoogleDrive/Il mio Drive/lezioni_carlo/lezioni/esercitazione/popgen_tutorial/test.gds
## The total number of samples: 786
## The total number of SNPs: 29547
## SNP genotypes are stored in SNP-major mode (Sample X SNP).
Once we have created the “test.gds” file, we need to open it and extract the information of interest, which are:
- samples information (sample.id)
- genetic variant information (snp.id)
- genotypes (g)
4 Spring cleaning – how to clean up your data
In order to make the computation steps fasible for our laptops I have already cleaned the data. Moreover, I have also sampled one SNP out of eight SNPs, in order to reduce the size of our files.
In particular the cleaning steps you always need to do are:
- check for missingness: removal of SNPs with missing call rates exceeding the provided value (0.05) and samples with missing call rates higher than 0.05.
- MAF (minor allele frequency): removal of monomorphic or rare SNPs. SNPs with a very low allele frequency in your dataset are of course not informative for distinguishing different populations because they are shared between a few individuals or are private of a single individual.
- LD (linkage-disequilibrium): according to the specific analysis we want to do, we may decide to prune (in other words, filter) the genetic variants for linkage disequilibrium. In particular, analyses requiring the independence of genetic markers (such as the Naive Bayes classification or STRUCTURE) will also require the LD pruning.
- check for relatedness among samples: the frequency computation cannot be influenced by the presence of related samples.
- autosomes only: in some analyses, like ancestry inference, we may prefer to work using only autosomes given the fact that they are equally shared between males and females (in other words, they have the same effective population size).
4.1 Creation of the genotype matrix
Once we have finished the cleaning steps (already perfomed), we set up the genotypes matrix. Using the R commands dim(geno) we can see matrix size (rows and columns number), while in order to see part of the matrix we can use the command geno[1:5,1:5], which shows the first five rows and columns of the matrix.
4.2 Missingness check
We know that the genotypes have already been cleaned by me, but the first rule in bioinformatics is “never trust the guy giving you some data”.
## [1] "how many missing cells are there in our dataset? 0"
At this point, we need another information: the ancestry of the individuals in sample.id variable. You can find this information in the file “kgp3.pheno.tsv” and, with the commands below, you can add this information to the genotype matrix.
4.3 Adding population labels
# read the population labels file
pop_labels = read.table("kgp3.pheno.tsv", hea=T)
# add the population labels to geno
target = rownames(geno)
#head(pop_labels[match(target,pop_labels$SAMPLE),])
geno$POP = pop_labels[match(target,pop_labels$SAMPLE),"POP"]
geno$SAMPLE = pop_labels[match(target,pop_labels$SAMPLE),"SAMPLE"]
# check if the order of samples is correct: if the answer is TRUE can be happy
identical(rownames(geno), as.character(geno$SAMPLE))
## [1] TRUE
How many populations do we have? As you can see with the R command table(geno$POP), there are 8 worldwide populations and 5 individuals of unknown ancestry.
label | population description | size |
---|---|---|
BEB | Bengali from Bangladesh | 86 |
CHB | Han Chinese in Beijing, China | 102 |
FIN | Finnish in Finland | 98 |
GBR | British in England and Scotland | 91 |
IBS | Iberian Population in Spain | 106 |
TSI | Tuscans in Italy | 107 |
PEL | Peruvians from Lima, Peru | 84 |
YRI | Yoruba in Ibadan, Nigeria | 107 |
# Divide the dataset in known populations and unknown individuals
geno_unk = geno[which(geno$POP %in% c("UNK1", "UNK2", "UNK3", "UNK4", "UNK5")),]
geno_pop = geno[!(geno$POP %in% c("UNK1", "UNK2", "UNK3", "UNK4", "UNK5")),]
In the end, our final dataset will be something like this, where each row is a sample, the first column is the population the sample comes from and the other columns are the SNPs:
POP | rs116400033 | rs115209712 | rs112455420 | rs375803869 | rs535902969 | rs9283154 | rs4520358 | |
---|---|---|---|---|---|---|---|---|
HG00096 | GBR | 2 | 0 | 0 | 0 | 0 | 0 | 1 |
HG00097 | GBR | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
HG00099 | GBR | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
HG00100 | GBR | 0 | 0 | 1 | 0 | 1 | 1 | 1 |
HG00101 | GBR | 0 | 0 | 0 | 0 | 0 | 2 | 1 |
HG00102 | GBR | 0 | 2 | 1 | 0 | 0 | 1 | 1 |
5 Keep calm and do some math – allelic and genotypic frequencies
5.1 Allele frequencies
At this point, the genotype matrix is ready and we can start the hard work: allele frequencies computation.
The frequency of an allele is defined as the number of the copies of the allelel in the population divided by the total number of gene copies in the population. In a diploid individual (in which all individuals carry two copies of each chromosome) with N individual, there are 2N gene copies. So the frequencies of alleles A1 and A2 are:
\(f_{A1}\) = \(\frac{N_{A1}}{2N}\)
and
\(f_{A2}\) = \(\frac{N_{A2}}{2N}\)
Let’s have a look at our genotypes, by taking the first five rows and five columns.
## POP rs116400033 rs115209712 rs112455420 rs375803869
## HG00096 GBR 2 0 0 0
## HG00097 GBR 0 0 0 0
## HG00099 GBR 0 1 0 0
## HG00100 GBR 0 0 1 0
## HG00101 GBR 0 0 0 0
There are four possible values stored in geno_pop: 0, 1, 2 and 3. For bi-allelic SNP sites, “0” indicates two A2 alleles, “1” indicates one A1 allele and one A2 allele, “2” indicates two A1 alleles, and “3” is a missing genotype.
Let’s see the second SNP in the geno_pop matrix: rs115209712.
|
|
Given that the geno_pop dataset harbours the count of one allele for each genotype (the cell), in order to compute the allele frequency, we simply need to sum all values in each column (the SNP) and divide it by 2N.
We can compute the frequency of the allele A across all samples or for each population separately.
# A1 frequency of the SNP rs115209712 in the entire dataset
sum(geno_pop$"rs115209712")/(2*dim(geno_pop)[1])
## [1] 0.1101152
# A2 frequency of the SNP rs115209712 in the entire dataset
1 - sum(geno_pop$"rs115209712")/(2*dim(geno_pop)[1])
## [1] 0.8898848
# Another way to compute the A2 frequency
(sum(geno_pop$"rs115209712" == 0)*2 + sum(geno_pop$"rs115209712" == 1))/(2*dim(geno_pop)[1])
## [1] 0.8898848
# A1 frequency of the SNP rs115209712 in the entire dataset
af_A1 = data.frame(matrix(NA,nrow=1, ncol=length(c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI"))))
rownames(af_A1)= "rs115209712"
colnames(af_A1) = c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")
for (pop in c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")){
pop.df = geno_pop[geno_pop$POP == pop,]
n.alleles = 2*nrow(pop.df)
snp ="rs115209712"
af_A1[snp, pop] = sum(pop.df[, snp])/n.alleles
}
# A2 frequency of the SNP rs115209712 in the entire dataset
af_A2 = 1 - af_A1
The frequencies of the alleles A1 and A2 of SNP rs115209712 in each populations are:
GBR | FIN | IBS | PEL | BEB | YRI | CHB | TSI | |
---|---|---|---|---|---|---|---|---|
rs115209712 | 0.1208791 | 0.1060606 | 0.0849057 | 0.0529412 | 0.1176471 | 0.1542056 | 0.0882353 | 0.1462264 |
GBR | FIN | IBS | PEL | BEB | YRI | CHB | TSI | |
---|---|---|---|---|---|---|---|---|
rs115209712 | 0.8791209 | 0.8939394 | 0.9150943 | 0.9470588 | 0.8823529 | 0.8457944 | 0.9117647 | 0.8537736 |
If you compare the allele frequencies found in our dataset with the ones reported in Ensembl, you can see that they match.
5.2 Genotype frequencies
The netx step will be the computation of the genotype frequencies according to Hardy-Weinberg formulas.
The genotype frequencies under Hardy-Weinberg Equilibrium are:
Genotype | AA | Aa | aa |
---|---|---|---|
Frequency | \(f_{A}^{2}\) | \(2f_{A}f_{a}\) | \(f_{a}^{2}\) |
gf_rs115209712 = data.frame(matrix(NA,nrow=3, ncol=length(c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI"))))
rownames(gf_rs115209712)= c("GG", "GA", "AA")
colnames(gf_rs115209712) = c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")
for (pop in c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")){
gf_rs115209712["GG",pop] = af_A1[pop]*af_A1[pop]
gf_rs115209712["GA",pop] = 2*af_A1[pop]*af_A2[pop]
gf_rs115209712["AA",pop] = af_A2[pop]*af_A2[pop]
}
genotype | GBR | FIN | IBS | TSI | BEB | CHB | PEL | YRI |
---|---|---|---|---|---|---|---|---|
GG | 0.0146 | 0.0112 | 0.0072 | 0.0214 | 0.0138 | 0.0078 | 0.0028 | 0.0238 |
GA | 0.2125 | 0.1896 | 0.1554 | 0.2497 | 0.2076 | 0.1609 | 0.1003 | 0.2609 |
AA | 0.7729 | 0.7991 | 0.8374 | 0.7289 | 0.7785 | 0.8313 | 0.8969 | 0.7154 |
As you can see the snp rs115209712 is not variable across different worldwide populations: in fact the homozygous genotype AA has an allele frequency higher that 0.71 everywhere, while the other two genotypes show little or no variation. From this rapid “visual inspection”, we can conclude that this SNP won’t surely be informative for ancestry inference.
Let’s try with another SNP, this time known in the literature for being informative for ancestry inference. The SNP is rs2814778 and has been reported byt this paper as a genetic marker informative in distinguishing European individuals. Let’s see if it works.
# allele frequencies
af_A1 = data.frame(matrix(NA,nrow=1, ncol=length(c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI"))))
rownames(af_A1)= "rs2814778"
colnames(af_A1) = c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")
for (pop in c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")){
pop.df = geno_pop[geno_pop$POP == pop,]
n.alleles = 2*nrow(pop.df)
snp ="rs2814778"
af_A1[snp, pop] = sum(pop.df[, snp])/n.alleles
}
# A2 frequency of the SNP rs2814778 in the entire dataset
af_A2 = 1 - af_A1
# genotype frequencies
gf_rs2814778 = data.frame(matrix(NA,nrow=3, ncol=length(c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI"))))
rownames(gf_rs2814778)= c("CC", "CT", "TT")
colnames(gf_rs2814778) = c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")
for (pop in c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")){
gf_rs2814778["CC",pop] = af_A1[pop]*af_A1[pop]
gf_rs2814778["CT",pop] = 2*af_A1[pop]*af_A2[pop]
gf_rs2814778["TT",pop] = af_A2[pop]*af_A2[pop]
}
genotype | GBR | FIN | IBS | TSI | BEB | CHB | PEL | YRI |
---|---|---|---|---|---|---|---|---|
CC | 0 | 0 | 4e-04 | 1e-04 | 0 | 0 | 0.0017 | 0.9907 |
CT | 0 | 0 | 0.037 | 0.0187 | 0 | 0 | 0.079 | 0.0093 |
TT | 1 | 1 | 0.9626 | 0.9812 | 1 | 1 | 0.9193 | 0 |
AS you can see from the table above and its Ensembl webpage, this SNP is informative in distinguishing Yoruba (African from Nigeria) individuals, because only in the Yoruba population the allele C and the genotype CC have almost reached the fixation.
6 Where do you come from? – the Naive Bayes classification
As we have seen in the Beyond identification (ancestry inference) lecture, the steps we need to do in order to infer the ancestry of a sample are:
- select and appropriate AIMs (Ancestral Informative Markers) panel,
- compute the allele frequencies in the reference populations,
- compute the genotypic frequencies in the reference populations of each unknown individual’s genotype,
- compute a RMP (Random Match Probability) multiplying the genotypic frequencies,
- compute the Likelihood Ratio for the highest probability.
In this tutorial, we cannot compute the informativeness for each marker due to the limited computational resources of our laptop. However, we will compare the classification performances of 30 randomly selected markers with the Global AIMs Nano set, a 31-plex assay of ancestry-informative SNPs developed in order to differentiate the five continents (M.de la Puente et al., 2016, Forensic Science International: Genetics). The complete set of SNPs is reported in Fig 6.1.
Now we will try to infer the ancestry of some of the unknown individuals. Let’s start with “NA18520”.
6.1 Where does NA18520 come from?
6.1.1 What happens if we use 30 randomly choosen markers?
Here we will try to infer the ancestry of the individual “NA18520”.
unknown_sample = "NA18520"
random_markers = sample(snp.id,30)
snp_set = random_markers
# compute the allele frequency across all populations
af_A1 = data.frame(matrix(NA,nrow=length(snp_set), ncol=length(c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI"))))
rownames(af_A1)= snp_set
colnames(af_A1) = c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")
for (pop in c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")){
pop.df = geno_pop[geno_pop$POP == pop,]
n.alleles = 2*nrow(pop.df)
for (snp in snp_set){
af_A1[snp, pop] = (1 + sum(pop.df[, snp]))/(2 + n.alleles)
}
}
# compute the genotypic frequencies of the genotypes in the unknown sample
geno_prob = data.frame(matrix(NA,nrow=length(snp_set), ncol=length(c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI"))))
rownames(geno_prob)= snp_set
colnames(geno_prob) = c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")
for (pop in c("GBR", "FIN", "IBS", "PEL", "BEB", "YRI", "CHB", "TSI")){
#pop.df = geno_pop[geno_pop$POP == pop,]
#n.alleles = 2*nrow(pop.df)
for (snp in snp_set){
p = af_A1[snp,pop]
q = 1 - p
if (geno_unk[unknown_sample,snp] == 0){
geno_prob[snp,pop] = q*q
} else if (geno_unk[unknown_sample,snp] == 1){
geno_prob[snp,pop] = 2*p*q
} else if (geno_unk[unknown_sample,snp] == 2){
geno_prob[snp,pop] = p*p
}
}
}
RMP = sort(apply(geno_prob, 2, prod), decreasing = T)
LR = RMP[1]/RMP[2]
YRI | BEB | TSI | IBS | CHB | GBR | FIN | PEL | |
---|---|---|---|---|---|---|---|---|
RMP | 3.61e-09 | 9.95e-13 | 2.36e-13 | 1.64e-13 | 9.48e-14 | 7.67e-14 | 4.67e-15 | 8.48e-18 |
## [1] "The YRI ancestry is 3627.38281716031 times more probable than BEB ancestry."
6.1.2 What happens when we use the Global AIMs Nano set?
YRI | BEB | PEL | IBS | TSI | FIN | GBR | CHB | |
---|---|---|---|---|---|---|---|---|
RMP | 1.20e-03 | 8.88e-21 | 3.82e-22 | 3.60e-23 | 4.78e-28 | 1.27e-28 | 1.53e-29 | 1.39e-31 |
## [1] "The YRI ancestry is 134892248993230752 times more probable than BEB ancestry."
As correctly inferred by both the random SNPs set and the Global AIMs Nano set, NA18520 comes from the Yoruba population (Nigeria). It is pretty impressive that even a set of random markers can (with some uncertainty) infer his ancestry, but if we think about our history as human beings, we can find an explanation.
A question for you: how is it possible that 30 randomly selected SNPs can relatively accurately infer the ancestry of an individual coming from Africa?
6.2 Where does NA18532 come from?
Now, we will compare the classification performances of the random and the Nano set on the individual NA18532.
|
|
The sample NA18532 is a Han Chinese individual from Beijing and while the random sets cannot infer his ancestry with high confidence (see the reduced LR with the random SNPs), the Global AIMs Nano set can accurately find his origins.
6.3 Where does HG03585 come from?
Now, we will compare the classification performances of tha random and the Nano set on the individual HG03585.
|
|
The sample HG03585 comes from Bangladesh. The Central-South Eurasia has been crossed for millennia by different populations and its populations show intermediate genetic frequencies between East Asia, Europe and Middle East. In the slide 11 of the pdf “Beyond identification (ancestry inference)” you can see that with K 4, 5 and 6 the Central/South Asia has the same colors of Europe (light blue) and East Asia (pink). However, the authors of the Global AIMs Nano set made an effort to distinguish this geographical area.
6.4 Where does NA20502 come from?
Now, we will compare the classification performances of tha random and the Nano set on the individual NA20502.
|
|
The sample NA20502 comes from Italy and particularly from Tuscany. None of the marker sets is able to correctly infer his ancestry. A random set of markers could never solve the difficult problem of distinguishing different countries from the same continents (in this case, Europe) and neither the Global AIMs Nano set can do it, because this set was developed with the aim of distinguishing the different continents. In fact, this set has correctly understood that this sample comes from Europe, since all the European populations have around the same probabilities of being the population of origin of NA20502. However, none of them prevails.
A question for you: where does the individual HG01620 come from?
7 All populations at a glance – principal component analysis
Can we use the entire set of markers to understand where an individual comes from? The simple answer is yes, however, it is unthinkable to use in the current forensic pratice whole genome sequences or, like in our case, around 30,000 genetic markers for ancestry inference.
## Principal Component Analysis (PCA) on genotypes:
## Excluding 0 SNP on non-autosomes
## Excluding 0 SNP (monomorphic: TRUE, MAF: NaN, missing rate: NaN)
## Working space: 786 samples, 29,547 SNPs
## using 2 (CPU) cores
## PCA: the sum of all selected genotypes (0,1,2) = 11466412
## CPU capabilities: Double-Precision SSE2
## Wed May 20 09:52:52 2020 (internal increment: 452)
##
[..................................................] 0%, ETC: ---
[==================================================] 100%, completed in 3s
## Wed May 20 09:52:55 2020 Begin (eigenvalues and eigenvectors)
## Wed May 20 09:52:55 2020 Done.
## [1] 5.94 3.85 1.61 0.75 0.43 0.23
tab <- data.frame(sample.id = pca$sample.id,
EV1 = pca$eigenvect[,1], # the first eigenvector
EV2 = pca$eigenvect[,2], # the second eigenvector
stringsAsFactors = FALSE)
# add pop labels
target = tab$sample.id
tab$POP = pop_labels[match(target,pop_labels$SAMPLE),"POP"]
tab$SAMPLE = pop_labels[match(target,pop_labels$SAMPLE),"SAMPLE"]
# add the inferred ancestry
tab$POP = as.character(tab$POP)
tab$POP[which(tab$sample.id=="NA18520")] = 'inf_YRI'
tab$POP[which(tab$sample.id=="NA18532")] = 'inf_CHB'
tab$POP[which(tab$sample.id=="HG03585")] = 'inf_BEB'
tab$POP[which(tab$sample.id=="NA20502")] = 'inf_TSI'
# create color palette
colors = c(brewer.pal(8, "Set1"), rep("black",5))
names(colors) = c("BEB","CHB","FIN","GBR","IBS","PEL","TSI","YRI","inf_TSI","inf_BEB","inf_YRI","inf_CHB","UNK2")
# give different shapes to the unknown samples
tab$pch=21
tab$pch[which(tab$sample.id=="NA18520")] = 22
tab$pch[which(tab$sample.id=="NA18532")] = 23
tab$pch[which(tab$sample.id=="HG03585")] = 24
tab$pch[which(tab$sample.id=="NA20502")] = 25
tab$pch[which(tab$sample.id=="HG01620")] = ""
tab$POP = as.factor(tab$POP)
# divide in tab_pop and tab_unk
tab_pop = tab[!(tab$POP %in% c("inf_TSI","inf_BEB","inf_YRI","inf_CHB","UNK2")),]
tab_pop = droplevels(tab_pop)
tab_unk = tab[which(tab$POP %in% c("inf_TSI","inf_BEB","inf_YRI","inf_CHB","UNK2")),]
tab_unk = droplevels(tab_unk)
ggplot(tab_pop, aes(x=EV1, y=EV2, color=POP)) + geom_point() + theme_linedraw() + xlab(round(pc.percent[1], digits=2)) + ylab(round(pc.percent[2], digits=2)) + scale_color_manual(values = colors) +
annotate("point", x = tab_unk$EV1[which(tab_unk$sample.id=="HG03585")], y = tab_unk$EV2[which(tab_unk$sample.id=="HG03585")], fill = "darkgrey", color="black", shape=22, size=3) +
annotate("text", label ="inf_BEB", x = tab_unk$EV1[which(tab_unk$sample.id=="HG03585")] + 0.005, y = tab_unk$EV2[which(tab_unk$sample.id=="HG03585")], color="black", size=3) +
annotate("point", x = tab_unk$EV1[which(tab_unk$sample.id=="NA18520")], y = tab_unk$EV2[which(tab_unk$sample.id=="NA18520")], fill = "darkgrey", color="black", shape=23, size=3) +
annotate("text", label= "inf_YRI", x = tab_unk$EV1[which(tab_unk$sample.id=="NA18520")] - 0.005, y = tab_unk$EV2[which(tab_unk$sample.id=="NA18520")], color="black", size=3) +
annotate("point", x = tab_unk$EV1[which(tab_unk$sample.id=="NA18532")], y = tab_unk$EV2[which(tab_unk$sample.id=="NA18532")], fill = "darkgrey", color="black", shape=24, size=3) +
annotate("text", label= "inf_CHB", x = tab_unk$EV1[which(tab_unk$sample.id=="NA18532")]+ 0.005, y = tab_unk$EV2[which(tab_unk$sample.id=="NA18532")], color="black", size=3) +
annotate("point", x = tab_unk$EV1[which(tab_unk$sample.id=="NA20502")], y = tab_unk$EV2[which(tab_unk$sample.id=="NA20502")], fill = "darkgrey", color="black", shape=25, size=3) +
annotate("text", label= "inf_TSI", x = tab_unk$EV1[which(tab_unk$sample.id=="NA20502")]+ 0.005, y = tab_unk$EV2[which(tab_unk$sample.id=="NA20502")], color="black", size=3) +
theme(legend.position="bottom") + guides(colour = guide_legend(override.aes = list(size=6))) + ggtitle("Principal component analysis")