Introduction

This vignette shows you how GenomicInteractions can be used to investigate significant interactions in HiC data that has been analysed using HOMER software [1]. GenomicInteractions can take a HOMER interaction file as input.

HiC data comes from chromosome conformation capture followed by high-throughput sequencing. Unlike 3C, 4C or 5C, which target specific regions, it can provide genome-wide information about the spatial proximity of regions of the genome. The raw data takes the form of paired-end reads connecting restriction fragments. The resolution of a HiC experiment is limited by the number of paired-end sequencing reads produced and by the sizes of restriction fragments. To increase the power to distinguish real interactions from random noise, HiC data is commonly analysed in bins from 20kb - 1Mb. There are a variety of tools available for binning the data, controlling for noise (e.g. self-ligations of restriction fragments), and finding significant interactions.

The data we are using comes from this paper [2] and can be downloaded from GEO. It is HiC data from wild type mouse double positive thymocytes. The experiment was carried out using the HindIII restriction enzyme. The paired-end reads were aligned to the mm9 mouse genome assembly and HOMER software was used to filter reads and detect significant interactions at a resolution of 100kb. For the purposes of this vignette, we will consider only data from chromosomes 14 and 15.

Making a GenomicInteractions object

Load the data by specifying the file location and experiment type. An experiment name and description are also required. A reference genome is required for constructing the GenomicInteractions object.

library(GenomicInteractions)
library(BSgenome.Mmusculus.UCSC.mm9)
hic_file <- system.file("extdata", "Seitan2013_WT_100kb_interactions.txt", 
                        package="GenomicInteractions")

hic_data <- GenomicInteractions(hic_file, 
                    type="homer", 
                    experiment_name = "HiC 100kb", 
                    description = "HiC 100kb resolution",
                    gname="BSgenome.Mmusculus.UCSC.mm9")

The GenomicInteractions object consists of two linked GenomicRanges objects containing the anchors of each interaction, as well as the number of reads supporting each interaction. In addition, p-values and FDRs from the input file are stored. Note that importing data of type ‘homer’ will always give a warning Name partially matched in data frame. This is because the name of the column containing the calculated FDR will contain the total number of interactions, which will be different for each experiment, so we cannot match the column name exactly.

We can check that the anchors are of the expected size (100kb).

summary(width(anchorOne(hic_data)))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   89500  100000  100000  100000  100000  100000
summary(width(anchorTwo(hic_data)))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  100000  100000  100000  100000  100000  100000
hic_data
## GenomicInteractions
##  Name: HiC 100kb
##  Description: HiC 100kb resolution
##  Genome: BSgenome.Mmusculus.UCSC.mm9
##  Number of individual interactions: 23276
##  Number of interactions: 447000
##  Annotated: no
##      Annotated with: N/A
##  Interactions:
##      chr15:97600000..97699999    -----   chr15:97500000..97599999
##      chr15:74800000..74899999    -----   chr15:74700000..74799999
##      chr14:55000000..55099999    -----   chr14:54800000..54899999
##      chr15:80400000..80499999    -----   chr15:80300000..80399999
##      chr14:55100000..55199999    -----   chr14:54800000..54899999
##      chr15:66600000..66699999    -----   chr15:66500000..66599999
##      chr15:59100000..59199999    -----   chr15:58800000..58899999
##      chr15:58900000..58999999    -----   chr15:58800000..58899999
##      chr15:80500000..80599999    -----   chr15:80100000..80199999
##      chr15:59100000..59199999    -----   chr15:58700000..58799999
##      ....

Some anchors are shorter than 100kb due to the bin being at the end of a chromosome. There are 23276 interactions in total, with a total of 447000 reads supporting them. To calculate the average number of reads per interaction, first use count() to get the count of reads supporting each individual interaction.

head(count(hic_data))
## [1] 344 373 258 397 213 441
mean(count(hic_data))
## [1] 19.204

However, since we have FDRs and p-values, it is probably more informative to use these to find interactions of interest.

plot(density(pValue(hic_data)))

plot(density(FDR(hic_data)))

Summary statistics

The package provides some functions to plot summary statistics of your data that may be of interest, such as the percentage of interactions that are between regions on the same chromosome (cis-interactions) or on different chromosomes (trans-interactions), or the number of reads supporting each interaction. These plots can be used to assess the level of noise in your dataset - the presence of many interactions with high FDRs or low read counts suggests that the data may be noisy and contain a lot of false positive interactions. You can subset the GenomicInteractions object by FDR or by number of reads.

sum(FDR(hic_data) < 0.1)
## [1] 8171
hic_data_subset <- hic_data[FDR(hic_data) < 0.1]
plotCisTrans(hic_data)

plotCisTrans(hic_data_subset)

plotCounts(hic_data, cut=30)

plotCounts(hic_data_subset, cut=30)

Subsetting by FDR will tend to remove interactions that are supported by fewer reads. Trans interactions tend to have lower read support than cis interactions, so the percentage of trans interactions decreases.

Annotation

One of the most powerful features of GenomicInteractions is that it allows you to annotate interactions by whether the anchors overlap genomic features of interest, such as promoters or enhancers. However this process can be slow for large datasets, so here we annotate with just promoters.

Genome annotation data can be obtained from, for example, UCSC databases using the GenomicFeatures package. We will use promoters of Refseq genes extended to a width of 5kb. Downloading all the data can be a slow process, so the data for promoters for chromosomes 14 and 15 is provided with this package.

## Not run
library(GenomicFeatures)
mm9.refseq.db <- makeTranscriptDbFromUCSC(genome="mm9", table="refGene")
refseq.genes = genes(mm9.refseq.db)
refseq.transcripts = transcriptsBy(mm9.refseq.db, by="gene")
refseq.transcripts = refseq.transcripts[ names(refseq.transcripts) %in% unlist(refseq.genes$gene_id) ] 
mm9_refseq_promoters <- promoters(refseq.transcripts, 2500,2500)
mm9_refseq_promoters <- unlist(mm9_refseq_promoters[seqnames(mm9_refseq_promoters) %in% c("chr14", "chr15")])

annotateInteractions takes a list of features in GRanges or GRangesList format and annotates the interaction anchors based on overlap with these features. The list of annotation features should have descriptive names, as these names are stored in the annotated GenomicInteractions object and used to assign anchor (node) classes.

data("mm9_refseq_promoters")

annotation.features <- list(promoter = mm9_refseq_promoters)

annotateInteractions(hic_data_subset, annotation.features)
## Annotating with promoter ...
hic_data_subset
## GenomicInteractions
##  Name: HiC 100kb
##  Description: HiC 100kb resolution
##  Genome: BSgenome.Mmusculus.UCSC.mm9
##  Number of individual interactions: 8171
##  Number of interactions: 284294
##  Annotated: yes
##      Annotated with: promoter, distal
##  Interactions:
##      chr15:97600000..97699999    -----   chr15:97500000..97599999
##      chr15:74800000..74899999    -----   chr15:74700000..74799999
##      chr14:55000000..55099999    -----   chr14:54800000..54899999
##      chr15:80400000..80499999    -----   chr15:80300000..80399999
##      chr14:55100000..55199999    -----   chr14:54800000..54899999
##      chr15:66600000..66699999    -----   chr15:66500000..66599999
##      chr15:59100000..59199999    -----   chr15:58800000..58899999
##      chr15:58900000..58999999    -----   chr15:58800000..58899999
##      chr15:80500000..80599999    -----   chr15:80100000..80199999
##      chr15:59100000..59199999    -----   chr15:58700000..58799999
##      ....

In addition, the features themselves should have names or IDs. For GRangesLists these names can be the names() of the items in the list. GRanges objects should have an “id” metadata column (note lowercase). These names or IDs for each feature are stored in the metadata for the anchors of the GenomicInteractions object. As each anchor may overlap multiple promoters, the “promoter.id” column is stored as a list. Promoter IDs can be accessed from the metadata of the anchors.

anchorOne(hic_data_subset)
## GRanges object with 8171 ranges and 2 metadata columns:
##          seqnames                 ranges strand   |  node.class
##             <Rle>              <IRanges>  <Rle>   | <character>
##      [1]    chr15   [97600000, 97699999]      *   |    promoter
##      [2]    chr15   [74800000, 74899999]      *   |    promoter
##      [3]    chr14   [55000000, 55099999]      *   |    promoter
##      [4]    chr15   [80400000, 80499999]      *   |    promoter
##      [5]    chr14   [55100000, 55199999]      *   |    promoter
##      ...      ...                    ...    ... ...         ...
##   [8167]    chr14 [ 64400000,  64499999]      *   |    promoter
##   [8168]    chr15 [ 93200000,  93299999]      *   |    promoter
##   [8169]    chr15 [103400000, 103490927]      *   |      distal
##   [8170]    chr15 [ 62600000,  62699999]      *   |      distal
##   [8171]    chr14 [ 80200000,  80299999]      *   |    promoter
##          promoter.id
##               <list>
##      [1]    ########
##      [2]    ########
##      [3]    ########
##      [4]    ########
##      [5]    ########
##      ...         ...
##   [8167]    ########
##   [8168]    ########
##   [8169]    ########
##   [8170]    ########
##   [8171]    ########
##   -------
##   seqinfo: 35 sequences from an unspecified genome
head(anchorOne(hic_data_subset)$promoter.id)
## [[1]]
## [1] "223864" "56233"  "67739" 
## 
## [[2]]
## [1] "110454" "17067"  "57248" 
## 
## [[3]]
## [1] "140743" "17387"  "20540"  "65107"  "68836" 
## 
## [[4]]
## [1] "102466773" "17444"     "213956"   
## 
## [[5]]
## [1] "16475"  "219072" "27374" 
## 
## [[6]]
## [1] "20491"

Node classes

Node classes (or anchor classes) are assigned to each anchor based on overlap with annotation features and the order of those features within the list passed to the annotation function. If the list is list(promoter=..., transcript=...) then an anchor which overlaps both a promoter and a transcript will be given the node class “promoter”. The features earlier in the list take priority. Any anchors which are not annotated with any of the given features will be assigned the class “distal”. In this case we have only annotated with one type of feature, so all anchors are either “promoter” or “distal”.

As the anchors are large, most of them overlap at least one promoter.

table(anchorOne(hic_data_subset)$node.class)
## 
##   distal promoter 
##     1944     6227
table(anchorTwo(hic_data_subset)$node.class)
## 
##   distal promoter 
##     2042     6129

Interaction types

Interaction types are determined by the classes of the interacting nodes. As we only have two node classes, we have three possible interaction classes, summarised in the plot below. Most of the interactions are between promoters. We can subset the data to look at interaction types that are of particular interest.

plotInteractionAnnotations(hic_data_subset)

Distal regions interacting with a promoter may contain distal regulatory elements such as enhancers or insulators. To get all promoter–distal interactions:

length(hic_data_subset[isInteractionType(hic_data_subset, "promoter", "distal")])
## [1] 1794

As this is a common type of interaction of interest, there is a function specifically for identifying these interactions (see the reference manual or help(isInteractionType) for additional built in interaction types). isInteractionType can be used with any pair of node classes. There are also functions for identifying cis or trans interactions.

length(hic_data_subset[is.pd(hic_data_subset)])
## [1] 1794
sum(is.trans(hic_data_subset))
## [1] 6

To find the strongest promoter–distal interaction:

hic_data_pd <- hic_data_subset[is.pd(hic_data_subset)]
max(count(hic_data_pd))
## [1] 385
most_counts <- hic_data_pd[which.max(count(hic_data_pd))]
most_counts
## GenomicInteractions
##  Name: HiC 100kb
##  Description: HiC 100kb resolution
##  Genome: BSgenome.Mmusculus.UCSC.mm9
##  Number of individual interactions: 1
##  Number of interactions: 385
##  Annotated: yes
##      Annotated with: promoter, distal
##  Interactions:
##      chr15:59400000..59499999    -----   chr15:59300000..59399999

Or the most significant promoter–distal interaction:

min(pValue(hic_data_pd))
## [1] 9.9935e-102
min_pval <- hic_data_pd[which.min(pValue(hic_data_pd))]
min_pval
## GenomicInteractions
##  Name: HiC 100kb
##  Description: HiC 100kb resolution
##  Genome: BSgenome.Mmusculus.UCSC.mm9
##  Number of individual interactions: 1
##  Number of interactions: 250
##  Annotated: yes
##      Annotated with: promoter, distal
##  Interactions:
##      chr15:59100000..59199999    -----   chr15:58800000..58899999

The distance between these interacting regions, or any interacting regions, can be found using calculateDistances. For trans interactions the distance will be NA.

calculateDistances(most_counts, method="midpoint")
## [1] 99999
calculateDistances(min_pval,method="midpoint")
## [1] 299999
summary(calculateDistances(hic_data_subset,method="midpoint"))
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max.      NA's 
##    100000   1100000   6200000  15300000  22700000 113000000         6

Virtual 4C plots

There are functions available to plot a “virtual 4C” plot of all interactions involving a region of interest, e.g. a specific promoter, or around a set of positions, e.g. all CTCF binding sites. These can be used to visualise interactions between a promoter of interest and its regulatory elements, or to summarise the interactions of a particular node class. The argument flip = TRUE can be used to flip coverage according to the strand of the region of interest. For example, the interactions around a set of promoters are plotted below. When flip = TRUE a slight bias towards downstream interactions is visible. This may be because of promoters interacting with the transcription termination site of the same gene or with intronic enhancers.

viewPoint(mm9_refseq_promoters[1], hic_data_subset, leftflank = 2000000, rightflank = 2000000)

set.seed(42)
promoter_subset <- sample(mm9_refseq_promoters, 20)
viewPointAverage(promoter_subset, hic_data_subset, 
                             leftflank = 1000000, rightflank = 1000000)

viewPointAverage(promoter_subset, hic_data_subset, 
                             leftflank = 1000000, rightflank = 1000000, flip = TRUE)

Export to BED12 format

The interactions can be exported to BED12 format for viewing in a genome browser. Anchors are visualised as thick blocks connected by thinner interactions.

## Not run
export.bed12(hic_data_subset, fn="hic_data_FDR0.1.bed")

References

  1. Heinz S, Benner C, Spann N, Bertolino E et al. Simple Combinations of Lineage-Determining Transcription Factors Prime cis-Regulatory Elements Required for Macrophage and B Cell Identities. Mol Cell 2010 May 28;38(4):576-589.

  2. Seitan, V. C. et al. Cohesin-based chromatin interactions enable regulated gene expression within pre-existing architectural compartments. Genome Res. 23, 2066-77 (2013).