Anotación genómica de los resultados del alineamiento

Introducción

El propósito de estos ejercicios es obtener la anotación del genoma con el que estamos trabajando, y utilizar esta anotación para catalogar nuestro mapeo de la secuenciación masiva. El resultado de esta práctica será obtener una figura mostrando la frecuencia con la que hemos secuenciado fragmentos de distintos tipos de elementos genómicos (regiones codificantes rRNA, snoRNA, etc). Haremos todo esto dentro del ambiente de R/Bioconductor.

Exactamente el mismo principio puede aplicarse a proyectos sin un genoma completo, siempre y cuando se cuente con algún tipo de archivo describiendo la anotación de distintas regiones de las secuencias de referencia (contigs, ESTs, cDNAs, etc).

Durante esta práctica vamos a utilizar:

R y BioConductor Instalados
Paquetes de BioC: IRanges, biomaRt, ShortRead, GenomicRanges Instalados


Breve introducción a IRanges

En esta práctica manipularemos muchos datos que se nos presentan en forma de rangos (coordenadas de genes, posiciones de secuencias mapeadas, etc). Una de las maneras más flexibles de hacer esto en R es aprovechando el paquete IRanges.

library(Biobase)
library(IRanges)

Podemos generar un objecto de clase IRanges si sabemos los inicios y finales de cada rango:

x = IRanges(start=c(11,35,40), end=c(20,50,63))

x
IRanges of length 3
    start end width
[1]    11  20    10
[2]    35  50    16
[3]    40  63    24

Para extraer la información básica de estos rangos usamos:

start(x) # Todos los inicios
end(x)   # Todos los finales
width(x) # Ancho de cada rango
range(x) # Rango total de todos

Pero existen muchas más funciones de gran utilidad:

coverage(x) # La suma de la cobertura en cada posición
'integer' Rle of length 63 with 6 runs
  Lengths: 10 10 14  5 11 13
  Values :  0  1  0  1  2  1
reduce(x)   # Une los rangos que estan encimados
IRanges of length 2
    start end width
[1]    11  20    10
[2]    35  63    29

y para darles una idea de lo que pretendemos:

exons = reduce(x)

reads = IRanges(start=c(1,21,30,50,80), width=20)

reads
IRanges of length 5
    start end width
[1]     1  20    20
[2]    21  40    20
[3]    30  49    20
[4]    50  69    20
[5]    80  99    20
countOverlaps(exons, reads)
[1] 1 3

Para mayores detalles, les recomiendo el vignette de IRanges (openVignette(package="IRanges")).


Obteniendo anotación con biomaRt

Lo primero que necesitamos es información sobre los elementos que nos interesa analizar de un genoma. Por ahora trabajaremos únicamente con genes conocidos del genoma humano. Para cada uno de estas regiones anotadas del genoma, necesitamos saber sus coordenadas y la cadena en la que se encuentran. También nos conviene saber el tipo biológico de cada transcrito, si representa una proteina, o un RNA no codificante. Vamos a extraer toda esta información de Ensembl, utilizando biomaRt, que es la inferase en R a BioMart.

Por cuestiones de tiempo, veremos un ejemplo muy básico de como usar biomaRt y procederemos a bajar una serie de datos obtenidos previamente.

library(biomaRt)

mart = useMart("ensembl", dataset="hsapiens_gene_ensembl")

# Los atributos que queremos obtener
attribs = c("ensembl_transcript_id", "chromosome_name", "strand", "exon_chrom_start", "exon_chrom_end")

# Obtener la información de la bases de datos
transInfo = getBM(attributes=attribs, filters="ensembl_transcript_id", values="ENST00000337138", mart=mart)

transInfo
   ensembl_transcript_id chromosome_name strand exon_chrom_start exon_chrom_end
1        ENST00000337138              14      1         62162258       62162557
2        ENST00000337138              14      1         62187100       62187290
3        ENST00000337138              14      1         62188227       62188372
4        ENST00000337138              14      1         62188457       62188541
5        ENST00000337138              14      1         62193424       62193536
...

Si quieren ver la lista completa de los atributos que podemos extraer, usen la función listAttributes(mart). Con listFilters(mart) obtenemos una tabla con todos los filtros válidos. Si quieren aprender más, vienen varios ejercicios en el vignette que se instala junto con biomaRt (openVignette(package="biomaRt")).

Para continuar con la práctica, preparé una tabla usando biomaRt, pidiendo una lista más grande de atributos, pero solamente para el cromosoma 14 mediante los parámetros filters="chromosome_name" y values="14".

Bajen entonces la tabla con la anotación del cromosoma 14.


Manipulación de tablas de coordenadas con IRanges

Leamos la tabla que bajaron en R y veamos que contiene:

coords = read.table("human_chr14_annotation.tab.gz", header=TRUE, sep="\t", check.names=FALSE)

head(coords)
  ensembl_gene_id ensembl_transcript_id chromosome_name strand exon_chrom_start exon_chrom_end transcript_biotype
1 ENSG00000249549       ENST00000493038           chr14      -         19940995       19941024            lincRNA
2 ENSG00000249549       ENST00000493038           chr14      -         19858667       19861377            lincRNA
3 ENSG00000251363       ENST00000515218           chr14      +         41423916       41424157            lincRNA
4 ENSG00000251363       ENST00000515218           chr14      +         41431994       41432047            lincRNA
5 ENSG00000251363       ENST00000515218           chr14      +         41443545       41443595            lincRNA
6 ENSG00000251363       ENST00000515218           chr14      +         41444546       41445068            lincRNA

Hay una clase de objeto muy util para guardar información acerca de coordenadas genómicas, llamado GRanges. Para usarlo, necesitamos asegurarnos de tener cargado el paquete GenomicRanges.

library(GenomicRanges)

exonGRs = GRanges(
   seqnames = Rle(factor(coords$chromosome_name)),
   ranges   = IRanges(start=coords$exon_chrom_start, end=coords$exon_chrom_end),
   strand   = Rle(factor(coords$strand)),
   biotype  = as.character(coords$transcript_biotype),
   id       = as.character(coords$ensembl_transcript_id)
)

exonGRs
GRanges with 24051 ranges and 2 elementMetadata values
        seqnames                 ranges strand   |          biotype              id
           <Rle>              <IRanges>  <Rle>   |      <character>     <character>
    [1]    chr14   [19940995, 19941024]      -   |          lincRNA ENST00000493038
    [2]    chr14   [19858667, 19861377]      -   |          lincRNA ENST00000493038
    [3]    chr14   [41423916, 41424157]      +   |          lincRNA ENST00000515218
    ...      ...                    ...    ... ...              ...             ...
[24049]    chr14 [ 21145178,  21145259]      -   |  tRNA_pseudogene ENST00000461087
[24050]    chr14 [ 24027031,  24027100]      -   |  tRNA_pseudogene ENST00000474722
[24051]    chr14 [ 74055529,  74055601]      -   |  tRNA_pseudogene ENST00000489070

seqlengths
 chr14
    NA

Podemos obtener acceso a los componentes de este nuevo objecto:

seqnames(exonGRs)
ranges(exonGRs)
strand(exonGRs)
values(exonGRs)

Lo último corresponde a una tabla donde podemos tener todas las columnas que querramos con información extra.

Podemos manipular los datos sencillamente:

table(values(exonGRs)$biotype)
                         IG_C_gene                    IG_C_pseudogene                          IG_D_gene
                                57                                  8                                 30
                         IG_J_gene                    IG_J_pseudogene                          IG_V_gene
                                71                                  3                                153
                   IG_V_pseudogene                 Mt_tRNA_pseudogene                          TR_V_gene
                               110                                 10                                 36
                   TR_V_pseudogene                          antisense                            lincRNA
                                 4                                108                                397
                             miRNA                           misc_RNA                         non_coding
                                92                                 46                                122
           nonsense_mediated_decay             polymorphic_pseudogene               processed_pseudogene
                              1132                                  2                                153
              processed_transcript                     protein_coding                         pseudogene
                              1600                              18815                                 17
                              rRNA                    rRNA_pseudogene                    retained_intron
                                10                                  3                                782
                   retrotransposed                   scRNA_pseudogene                              snRNA
                                 4                                 27                                 65
                  snRNA_pseudogene                             snoRNA                  snoRNA_pseudogene
                                 4                                 97                                  4
                   tRNA_pseudogene   transcribed_processed_pseudogene transcribed_unprocessed_pseudogene
                                 4                                  5                                 60
                unitary_pseudogene             unprocessed_pseudogene
                                 6                                 14

Una advertencia. Aunque no lo vamos a tomar en cuenta aquí, muchos de los rangos de nuestra tabla podrían estar contenidos unos dentro de otros. Como lo que queremos es contar ocurrencias de nuestras secuencias cortas dentro de estos rangos, podríamos terminar contando algunas de las secuencias más de una vez.


Ejercicios:



Anotación de secuencias mapeadas

Si cerraron su sesión de R después de la práctica anterior, vamos a tener que leer una vez más los resultados del alineamiento con Bowtie. Si no los tienen, pueden bajarlas aquí: clean_reads.bwt.zip.

library(ShortRead)

aln = readAligned("clean_reads.bwt", type="Bowtie")

aln
class: AlignedRead
length: 393848 reads; width: 51 cycles
chromosome: chr14 chr14 ... chr14 chr14
position: 74190113 53512945 ... 105477651 23536730
strand: + + ... - +
alignQuality: NumericQuality
alignData varLabels: similar mismatch

Lo primero que tenemos que hacer es convertir este objeto en uno que podamos comparar con nuestras coordenadas de exons:

alnGRs = as(aln, "GRanges")

alnGRs
GRanges with 393848 ranges and 3 elementMetadata values
         seqnames                 ranges strand   |                                id   similar    mismatch
            <Rle>              <IRanges>  <Rle>   |                      <BStringSet> <integer> <character>
     [1]    chr14 [ 74190113,  74190163]      +   |    GAII06_0003:3:1:1040:19544#0/1         0
     [2]    chr14 [ 53512945,  53512995]      +   |     GAII06_0003:3:1:1148:2517#0/1         0
     [3]    chr14 [ 54458302,  54458352]      +   |    GAII06_0003:3:1:1180:16552#0/1         0
     ...      ...                    ...    ... ...                               ...       ...         ...
[393846]    chr14 [ 38149602,  38149652]      -   |  GAII06_0003:3:120:19796:3235#0/1         0
[393847]    chr14 [105477651, 105477701]      -   | GAII06_0003:3:120:19801:16968#0/1         0
[393848]    chr14 [ 23536730,  23536780]      +   |  GAII06_0003:3:120:19805:4487#0/1         0

seqlengths
 chr14
    NA

Con la función countOverlaps podemos encontrar cuantas secuencias alineadas traslapan con cada uno de nuestros exones. De una vez guardamos el resultado directamente como anotación de los exones:

values(exonGRs)$counts = countOverlaps(exonGRs, alnGRs)

values(exonGRs)
DataFrame with 24051 rows and 3 columns
               biotype              id    counts
           <character>     <character> <integer>
1              lincRNA ENST00000493038         0
2              lincRNA ENST00000493038         4
3              lincRNA ENST00000515218         1
...                ...             ...       ...
24049  tRNA_pseudogene ENST00000461087         0
24050  tRNA_pseudogene ENST00000474722         0
24051  tRNA_pseudogene ENST00000489070         0

Finalmente, podemos agregar estos valores de la manera que más nos convenga. En este caso podría ser por tipo de transcrito, o por transcrito individual.

typeCounts = aggregate(values(exonGRs)$counts, by=list("biotype"=values(exonGRs)$biotype), sum)

head(typeCounts)
                              biotype      x
...
11                          antisense    347
12                            lincRNA    502
13                              miRNA     87
14                           misc_RNA    167
15                         non_coding      5
16            nonsense_mediated_decay  12548
17             polymorphic_pseudogene      0
18               processed_pseudogene   2627
19               processed_transcript  12472
20                     protein_coding 329940
21                         pseudogene    469
22                               rRNA     32
23                    rRNA_pseudogene      1
24                    retained_intron  16184
25                    retrotransposed    345
26                   scRNA_pseudogene      5
27                              snRNA     11
28                   snRNA_pseudogene      1
29                             snoRNA     27
...
transCounts = aggregate(values(exonGRs)$counts, by=list("id"=values(exonGRs)$id), sum)

head(transCounts)
               id   x
1 ENST00000005198 267
2 ENST00000013070 382
3 ENST00000013125 235
4 ENST00000031146 602
5 ENST00000045347  29
6 ENST00000163416 327

Las cuentas por transcrito las podríamos usar directamente en un proceso de análisis de expresión diferencial, por ejemplo. La distribución de secuencias cortas en los distinto biotipos podríamos querer visualizarlo en una figura:

minCount = 1000
typeCountsHigh = typeCounts[typeCounts$x > minCount,]
typeCountsHigh = typeCountsHigh[order(typeCountsHigh$x),]
typeCountsHigh = rbind(data.frame("biotype"="other",
                           "x"=sum(typeCounts$x[typeCounts$x <= minCount])),
                           typeCountsHigh)

pie(typeCountsHigh$x, labels=typeCountsHigh$biotype,
    main="Number of aligned reads per biotype")