Esse documento serve como estudo próprio e também como uma rápida demonstração da Análise de Componentes Principais a partir de dados genéticos. Fui altamente inspirado pelo Gábor Mészáros do Genomics Boot Camp no youtube. Passamos brevemente pela obtenção dos dados, pelo controle de qualidade e finalizamos com o PCA.
Um lugar onde podemos obter datasets disponíveis publicamente é o datadryad.org
Esse site possui diversos datasets, em seus diferentes formatos, prontos
para serem utilizados.
Para esse tutorial, escolhi utilizar os dados do artigo Aedes
aegypti in North America (Microsatellite and SNP array)
Basta clicarmos no link de download e obtemos o dataset em
formato plink.
Que tal darmos uma olhadinha nesses dados, só para irmos nos acostumando?
O arquivo .bim é aquele que contém informações sobre os SNPs presentes no dado. Cada linha corresponde a um SNP diferente.
bim <- read.table("./data/doi_10.5061_dryad.5x69p8d5j__v2/DatasetS2_NorthAmericaCombinedSnps_reorder.bim")
head(bim)
## V1 V2 V3 V4 V5 V6
## 1 3003 AX-93233745 0 44357 T G
## 2 3003 AX-93233746 0 44795 A G
## 3 3003 AX-93233749 0 56930 G A
## 4 3003 AX-93233752 0 66000 G T
## 5 3003 AX-93233758 0 118514 A T
## 6 3003 AX-93233759 0 131149 T C
nrow(bim)
## [1] 20003
O arquivo .fam é aquele que contém informações sobre os indivíduos. Cada linha contém um indivíduo diferente.
fam <- read.table("./data/doi_10.5061_dryad.5x69p8d5j__v2/DatasetS2_NorthAmericaCombinedSnps_reorder.fam")
head(fam)
## V1 V2 V3 V4 V5 V6
## 1 SM 3 0 0 0 -9
## 2 SM 8 0 0 0 -9
## 3 SM 11 0 0 0 -9
## 4 SM 12 0 0 0 -9
## 5 SM 13 0 0 0 -9
## 6 SM 14 0 0 0 -9
nrow(fam)
## [1] 381
O arquivo .bed é um arquivo binário contendo a informação genética desses indivíduos.
Essa olhada geral nos dados nos revelou que estamos lidando com um dataset com 20003 SNPs e 381 indivíduos. Além disso, os nomes dos cromossomos estão desformatados (não vai impedir em nada nossa análise), e não temos informações de parentais nem de sexo dos indivíduos.
Após baixarmos os dados, vamos passá-los pelo controle de
qualidade.
A etapa de controle de qualidade envolve passarmos os dados por uma
série de filtros comumente utilizados para se assegurar uma alta
taxa de genotipagem, ou seja, um dado de alta qualidade. Isso é
feito no próprio plink, e os filtros são:
system("./programs/plink.exe --bfile ./data/doi_10.5061_dryad.5x69p8d5j__v2/DatasetS2_NorthAmericaCombinedSnps_reorder --geno 0.1 --mind 0.1 --maf 0.05 --allow-extra-chr --make-bed --out ./data/aedes_QC")
## [1] 0
Aqui vemos que após os filtros de qualidade, de 20003 SNPs e 381 indivíduos, com taxa de genotipagem de 0.958638, ficamos com 19806 SNPs e 40 indivíduos removidos. Além disso, a taxa de genotipagem subiu para 0.987982.
Após passado pelo controle de qualidade, o dado precisa ser tratado de algumas outras formas antes de colocarmos no gráfico do PCA. Para construir esse gráfico, nós vamos precisar calcular as distâncias genéticas entre cada indivíduo. Isso é feito no plink, com o comando:
# Calculating genetic distances
system("./programs/plink.exe --bfile ./data/aedes_QC --distance-matrix --allow-extra-chr --allow-no-sex --nonfounders --out ./data/dist_ma_aedes")
## [1] 0
Nessa etapa, com a nossa matriz de distância entre os indivíduos em
mãos, vamos trabalhar em cima desses dados.
Por enquanto, vamos carregar a matriz de distância:
dist_ma <- read.table("./data/dist_ma_aedes.mdist", header = F)
Aqui estamos criando dois data frames, que serão utilizados posteriormente
# Family ID Column of .fam file
family <- tibble(family = read.table("./data/dist_ma_aedes.mdist.id")[,1])
# Individual ID column of .fam file
ind <- tibble(IID = read.table("./data/dist_ma_aedes.mdist")[,2])
Aplicamos a função cmdscale() na nossa matriz de distância genética. Essa função representa o multidimensional scaling clássico de uma matriz de dados.
mds <- cmdscale(dist_ma, eig = T, 5)
Finalizamos essa seção computando os autovetores e os autovalores. Estamos interessados apenas nos dois primeiros componentes principais:
eigenvec <- cbind(family, ind, mds$points)
eigen_percent <- round(((mds$eig)/sum(mds$eig)) * 100, 2)
Queremos fazer com que o gráfico do PCA seja útil para nós vermos se
as populações irão agrupar de acordo com as regiões em que elas ocorrem.
Para isso, eu tive que coletar, no meta-dado, a tabela que conecta as
abreviações com as regiões.
Essa parte foi um pouco trabalhosa. Consegui essa tabela neste
link.
Com a tabela em mãos, tive que arranjar alguma forma de tirar ela do
formato .docx. Nesse caso, eu copiei e colei num bloco de notas, e
depois salvei em .tsv.
Vamos trabalhar em cima do dado agora:
aedes_pops <- read_tsv("./data/aedes_pops.tsv")
## Rows: 70 Columns: 7
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): Pop. number, Population, Abbrev., Region name
## dbl (3): Year, N1, N2
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# 77 abbrv
aedes_tidy <- aedes_pops |>
select(Abbrev., `Region name`) |>
rename("abbrv" = Abbrev.,
"region" = `Region name`) |>
separate(abbrv, into = c('a', 'b'), sep = " and ") |>
mutate(rows = row_number()) |>
pivot_longer(c(a, b), names_to = "key", values_to = "values") |>
drop_na() |>
select(values, region)
## Warning: Expected 2 pieces. Missing pieces filled with `NA` in 63 rows [1, 2, 3,
## 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...].
eigenvec <- cbind(family, ind, mds$points) |>
left_join(aedes_tidy, by = c("family" = "values"))
eigenvec <- eigenvec |>
mutate(region = case_when(
family == "MAD13" ~ "Northern CA",
family == "MAD15" ~ "Northern CA",
family == "Alamagordo" ~ "Central",
family == "SanDiego" ~ "Southern CA",
family == "Tucs" ~ "Southwest",
family == "LC" ~ "Central",
family == "Lubbock" ~ "Central",
family == "Bexar" ~ "Central",
family == "NO11" ~ "Southeast",
family == "Mia" ~ "Southeast",
TRUE ~ as.character(region)))
eigen_percent <- round(((mds$eig)/sum(mds$eig)) * 100, 2)
Finalmente, com os autovalores e autovetores calculados, vamos plotar nossos indivíduos no gráfico usando ggplot
eigenvec |>
ggplot() +
geom_point(aes(`1`, `2`, color = region), size = 1.5) +
geom_vline(xintercept = 0, linetype = "dashed") +
geom_hline(yintercept = 0, linetype = "dashed") +
labs(
x = paste0("Componente Principal 1 (", eigen_percent[1], "%)"),
y = paste0("Componente Principal 2 (", eigen_percent[2], "%)")) +
theme_bw()