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 (exones codificantes y no-codificantes, regiones 5'UTR y 3'UTR, intrones, etc). Haremos todo esto dentro del ambiente de R/Bioconductor.

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, sus regiones 5' y 3'UTR, exones e intrones. 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="ENST00000224764", mart=mart)

head(transInfo)
  ensembl_transcript_id chromosome_name strand exon_chrom_start exon_chrom_end
1       ENST00000224764              10      1         88598622       88598736
2       ENST00000224764              10      1         88649819       88649981
3       ENST00000224764              10      1         88651884       88651986
4       ENST00000224764              10      1         88659551       88659647
5       ENST00000224764              10      1         88659784       88659883
6       ENST00000224764              10      1         88671997       88672141

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 10 mediante los parámetros filters="chromosome_name" y values="10".

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


Manipulación de tablas de coordenadas con IRanges

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

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

head(coords)
  ensembl_transcript_id ensembl_gene_id chromosome_name strand exon_chrom_start
1       ENST00000016171 ENSG00000014919           chr10      -        101491717
2       ENST00000016171 ENSG00000014919           chr10      -        101489310
3       ENST00000016171 ENSG00000014919           chr10      -        101487198
4       ENST00000016171 ENSG00000014919           chr10      -        101486725
5       ENST00000016171 ENSG00000014919           chr10      -        101483713
6       ENST00000016171 ENSG00000014919           chr10      -        101480744
  exon_chrom_end 3_utr_start 3_utr_end 5_utr_start 5_utr_end   gene_biotype
1      101491857          NA        NA   101491807 101491857 protein_coding
2      101489491          NA        NA          NA        NA protein_coding
3      101487320          NA        NA          NA        NA protein_coding
4      101486911          NA        NA          NA        NA protein_coding
5      101483880          NA        NA          NA        NA protein_coding
6      101480825          NA        NA          NA        NA protein_coding
  cds_chrom_start cds_chrom_end transcript_biotype
1       101491717     101491806     protein_coding
2       101489310     101489491     protein_coding
3       101487198     101487320     protein_coding
4       101486725     101486911     protein_coding
5       101483713     101483880     protein_coding
6       101480744     101480825     protein_coding

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 46395 ranges and 2 elementMetadata values
        seqnames                 ranges strand   |        biotype              id
           <Rle>              <IRanges>  <Rle>   |    <character>     <character>
    [1]    chr10 [101491717, 101491857]      -   | protein_coding ENST00000016171
    [2]    chr10 [101489310, 101489491]      -   | protein_coding ENST00000016171
    [3]    chr10 [101487198, 101487320]      -   | protein_coding ENST00000016171
    ...      ...                    ...    ... ...            ...             ...
[46393]    chr10 [ 74774855,  74776047]      +   |        lincRNA ENST00000502140
[46394]    chr10 [131910186, 131910380]      +   |        lincRNA ENST00000502243
[46395]    chr10 [131918330, 131919006]      +   |        lincRNA ENST00000502243

seqlengths
 chr10
    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)
              Mt_tRNA_pseudogene                          lincRNA                            miRNA 
                              20                              168                               64 
                        misc_RNA              misc_RNA_pseudogene                       non_coding 
                              60                                1                               10 
         nonsense_mediated_decay             processed_pseudogene             processed_transcript 
                            1132                              487                             9593 
                  protein_coding                       pseudogene                             rRNA 
                           33460                              220                               27 
                 rRNA_pseudogene                  retained_intron                  retrotransposed 
                              14                              494                               13 
                scRNA_pseudogene                            snRNA                 snRNA_pseudogene 
                              34                               70                               23 
                          snoRNA                snoRNA_pseudogene                  tRNA_pseudogene 
                              40                               31                                3 
transcribed_processed_pseudogene               unitary_pseudogene           unprocessed_pseudogene 
                               2                               25                              404 

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.



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 de esta carpeta.

library(ShortRead)

aln <- readAligned("aln.bwt.gz", type="Bowtie")

aln
class: AlignedRead
length: 394773 reads; width: 15..31 cycles
chromosome: chr10 chr10 ... chr10 chr10 
position: 82487667 76102535 ... 49380417 5014830 
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 394773 ranges and 3 elementMetadata values
         seqnames                 ranges strand   |                      id   similar    mismatch
            <Rle>              <IRanges>  <Rle>   |            <BStringSet> <integer> <character>
     [1]    chr10 [ 82487667,  82487684]      -   |       HWI-E09:5:1:0:153        52       8:T>G
     [2]    chr10 [ 76102535,  76102550]      +   |      HWI-EA9:5:1:0:1091       158            
     [3]    chr10 [107789720, 107789737]      +   |      HWI-EA9:5:1:0:1866         0       8:A>G
     ...      ...                    ...    ... ...                     ...       ...         ...
[394771]    chr10 [ 11113524,  11113540]      -   | HWI-EASXXX:00:1781:1467         0      15:T>C
[394772]    chr10 [ 49380417,  49380431]      -   | HWI-EASXXX:00:1781:1107         2       9:C>G
[394773]    chr10 [  5014830,   5014855]      +   |   HWI-EASXX:100:1781:61         1            

seqlengths
 chr10
    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 46395 rows and 3 columns
             biotype              id    counts
               
1     protein_coding ENST00000016171         1
2     protein_coding ENST00000016171        12
3     protein_coding ENST00000016171         0
...              ...             ...       ...
46393        lincRNA ENST00000502140         1
46394        lincRNA ENST00000502243         0
46395        lincRNA ENST00000502243         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
1  Mt_tRNA_pseudogene 654
2             lincRNA 453
3               miRNA 163
4            misc_RNA 517
5 misc_RNA_pseudogene   0
6          non_coding   5
transCounts <- aggregate(values(exonGRs)$counts, by=list("id"=values(exonGRs)$id), sum)

head(transCounts)
               id   x
1 ENST00000016171  38
2 ENST00000020673  10
3 ENST00000173785  12
4 ENST00000181796 123
5 ENST00000185885   0
6 ENST00000185907   1

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="Número de secuencias alineadas por biotipo")