TFEA.ChIP: a tool kit for transcription factor binding site enrichment analysis capitalizing on ChIP-seq datasets

Laura Puente-Santamaria, Luis del Peso

2019-01-22

Introduction

The identification of the transcription factor (TF) responsible for the coregulation of an specific set of genes is a common problem in transcriptomics. In the most simple scenario, the comparison of the transcriptome of cells or organisms in two conditions leads to the identification of a set of differentially expressed (DE) genes and the underlying assumption is that one or a few TFs regulate the expression of those genes. Traditionally, the identification of the relevant TFs has relied on the use of position weight matrices (PWMs) to predict transcription factor binding sites (TFBSs) proximal to the DE genes (Wasserman and Sandelin, 2004). The comparison of predicted TFBS in DE versus control genes reveals factors that are significantly enriched in the DE gene set. These approaches have been useful to narrow down potential binding sites, but can suffer from high rates of false positives. In addition, this strategy is limited by design to sequence-specific transcription factors (TF) and thus unable to identify cofactors that bind indirectly to target genes. To overcome these limitations, TFEA.ChIP exploits the vast amount of publicly available ChIP-seq datasets to determine TFBS proximal to a given set of genes and computes enrichment analysis based on this experimentally-derived rich information. Specifically TFEA.ChIP, uses information derived from the hundreds of ChIP-seq experiments from the ENCODE Consortium 1 expanded to include additional datasets contributed to GEO database2 3 by individual laboratories representing the binding sites of factors not assayed by ENCODE. The package includes a set of tools to customize the ChIP data, perform enrichment analysis and visualize the results. The package implements two enrichment analysis methods:

TFEA.ChIP includes a TF-gene interaction database containing 1122 datasets from ChIP-Seq experiments testing 331 different human transcription factors. Although the package is mainly focused towards analyzing expression data generated from human cells, TFEA.ChIP includes the option to use datasets coming from experiments in mice, translating mouse gene names to their equivalent ID on the human genome.

Analysis Example

TFEA.ChIP is designed to take the output of a differential expression analysis and identify TFBS enriched in the list of DE genes. In the case of the analysis of association, the only required input is a set of DE genes and, optionally, a set of control genes whose expression is not altered by the experimental conditions under study. For the GSEA analysis a ranked list of genes is required. This is supplied as a dataframe containing a column with gene names and a numerical column with the ranking metric, which typically are log-fold change or p-values for the gene expression changes in the two conditions under evaluation. For illustration purposes we will derive the input required for both analysis from a table containing the following field columns:

The output of popular packages, such as DESeq2, for detection of differentially expressed genes from the analysis of count data from RNA-seq experiments produce tables with this information. The hypoxia_DESeq and hypoxia datasets are the output of a differential expression analysis performed on an RNAseq experiment analyzing the response to hypoxia of endothelial cells6 deposited at the NCBI’s GEO repository (GSE89831).

To extract the information from a DESeqResults object or a data frame the function preprocessInputData is available. Using the uption from.Mouse = TRUE will translate mouse gene IDs to their equivalent human gene ID.

library(TFEA.ChIP)
data("hypoxia_DESeq","hypoxia",package="TFEA.ChIP") # load example datasets
hypoxia_table<-preprocessInputData(hypoxia_DESeq)
## Warning: Some genes returned 1:many mapping to ENTREZ ID. Genes were assigned the first ENTREZ ID match found.
head(hypoxia_table)
##   Genes log2FoldChange        pvalue      pval.adj
## 1  1769       4.588064  3.942129e-62  3.285202e-59
## 2  6781       4.339768  7.840000e-11  2.714222e-09
## 3 54843       4.307058  1.435621e-75  1.522672e-72
## 4  3486       4.297902  6.595438e-18  5.535897e-16
## 5   205       4.235590 1.318311e-125 5.126912e-122
## 6  5740       3.931662  4.398104e-15  2.631420e-13
head(hypoxia)
##      Genes log2FoldChange      pvalue   pval.adj
## 1     TNMD     0.00000000 1.000000000 1.00000000
## 2     DPM1    -0.44497788 0.001243905 0.01809033
## 3    SCYL3    -0.27092276 0.139977254 0.65057456
## 4 C1orf112    -0.52382569 0.035752231 0.25407939
## 5      FGR    -0.44810165 0.217220835 0.83961811
## 6      CFH     0.05237749 0.740720152 1.00000000
hypoxia_table<-preprocessInputData(hypoxia)
## Warning: Some genes returned 1:many mapping to ENTREZ ID. Genes were assigned the first ENTREZ ID match found.
head(hypoxia_table)
##        Genes log2FoldChange       pvalue     pval.adj
## 10785   1365       5.319709 9.647797e-77 2.817857e-73
## 10138 284525       5.179949 2.411485e-64 5.414910e-61
## 9362  392255       4.401819 3.559414e-91 1.998137e-87
## 3958    4883       4.208881 1.414157e-39 1.082537e-36
## 10320   2199       4.060889 1.305316e-46 1.465522e-43
## 5256    1769       4.024703 8.418844e-65 2.025454e-61

After running preprocessInputData, your dataset will be ready to use with the rest of the package; gene names will be in Entrez Gene ID format and the resulting table is sorted by log2(Fold Change).

Analysis of the association of TFBS and differential expression.

  1. Identification of DE genes

As indicated before, for this analysis, we must provide a list of genes are considered differentially induced and a list of control genes whose expression is not altered in the analyzed experiment. For that we will use the function Select_genes:

  1. Translate the gene IDs to Entrez Gene IDs

In case the input dataset cannot be preprocessed or the user is interested in analyzing a particular set of genes that doesn’t come from the input dataset, translating the IDs to Entrez Gene ID format is required. To that end its available the function GeneID2entrez:

## [1] "112399" "4800"   "57679"  "4609"   "405"
  1. Association analysis

In this step, we will construct a contingency table for each of the factors stored in the internal database categorizing the DE (DE_yes) and control (DE_no) genes according to the presence or absence of binding sites:

TFbound_yes TFbound_no
DE_yes number y/y number y/n
DE_no number n/y number n/n

Then, we will apply Fisher’s exact test to each contingency table to test the null hypothesis that factor binding and differential expression are independent. In addition, to the raw p-values the function also return the FDR-adjusted values to correct for multiple testing.

##            Accession  Cell  Treatment    TF      p.value        OR
## 420       GSM2390643 HUVEC    Hypoxia  ARNT 4.448825e-62  8.601617
## 419       GSM2390642 HUVEC    Hypoxia EPAS1 1.166679e-36 10.755095
## 850 wgEncodeEH001758 HUVEC       none GATA2 4.129697e-24  3.258312
## 451        GSM700944 MCF-7 0.5%02 16h HIF1A 9.189662e-21  6.360235
## 606 wgEncodeEH000719 HUVEC       none   JUN 1.215251e-16  2.691102
## 864 wgEncodeEH001774 HUVEC       none   FOS 2.151690e-16  2.919160
##      log2.OR  adj.p.value log10.adj.pVal distance
## 420 3.104608 5.133943e-59       58.28955 58.37217
## 419 3.426948 6.731736e-34       33.17187 33.34842
## 850 1.704125 1.588557e-21       20.79900 20.86869
## 451 2.669080 2.651217e-18       17.57655 17.77806
## 606 1.428197 2.804800e-14       13.55210 13.62715
## 864 1.545553 3.547214e-14       13.45011 13.53862

In this example, all 1122 datasets in the internal database were used in the analysis. However, we can restrict the analysis to a specific subset of the database and/or a given set of transcription factors. To this end we can produce and index of the tables of interest with the function get_chip_index and pass this index as an additional argument to contingency_matrix. Finally, note that the list of control genes is optional. If not supplied, all human genes not present in the test list will be used as control. Thus, we could restrict the analysis to the datasets generated by the ENCODE project and use all non-DE genes as control:

##            Accession          Cell        Treatment    TF      p.value
## 381 wgEncodeEH001758         HUVEC             none GATA2 4.129697e-24
## 395 wgEncodeEH001774         HUVEC             none   FOS 2.151690e-16
## 137 wgEncodeEH000719         HUVEC             none   JUN 1.215251e-16
## 475 wgEncodeEH001871 MCF10A-Er-Src EtOH_0.01pct_4hr STAT3 2.816971e-13
## 678 wgEncodeEH003214       H1-hESC             none TEAD4 4.453741e-13
## 155 wgEncodeEH000746       HeLa-S3             none   JUN 1.648435e-12
##           OR  log2.OR  adj.p.value log10.adj.pVal  distance
## 381 3.258312 1.704125 2.828842e-21      20.548391 20.618934
## 395 2.919160 1.545553 3.684769e-14      13.433590 13.522207
## 137 2.691102 1.428197 3.684769e-14      13.433590 13.509296
## 475 2.481616 1.311280 3.859250e-11      10.413497 10.495731
## 678 2.297328 1.199957 5.084688e-11      10.293736 10.363440
## 155 2.241583 1.164518 1.613111e-10       9.792336  9.861336

To know more about the experiments included in TFEA.ChIP’s database or the conditions of a particular experiment, load the metadata table included using data(“MetaData”, package = “TFEA.ChIP”).

  1. Plot results

The table of results generated by getCMstats can be parsed to select candidate TF. The function plot_CM uses the package plotly to generate an interactive plot representing the p-value against the odd-ratio that is very helpful to explore the results.

Snapshot of a plot generated with plot_CM.

Snapshot of a plot generated with plot_CM.

In fact, the exploration of this graph shows a strong enrichment for several HIF datasets, as expected for the hypoxia dataset. This can be clearly shown by highlighting the datasets of interest:

Snapshot of a plot generated with plot_CM.

Snapshot of a plot generated with plot_CM.

Gene Set Enrichment Analysis.

  1. Generate a sorted list of ENTREZ IDs

The GSEA analysis implemented in the TFEA.ChIP package requires as input a sorted list of genes. By default, the function preprocessInputData will sort genes according to log fold change in descending order. However, they could be sorted by any numerical parameter including p-value. If you want to generate your custom gene list with other parameters, remember to make sure the gene IDs are in Entrez Gene ID format or translate them with GeneID2Entrez

  1. Select the ChIP-Seq datasets to analyze

By default, the analysis will include all the ChIP-Seq experiments available in the database. However, this analysis might take several minutes to run. To restrict the analysis to a subset of the database we can generate an index variable and pass it to the function GSEA_run. This will limit the analysis to the ChIP-Seq datasets of the user’s choosing. This index variable can be generated using the function get_chip_index and allows the user to select the whole database, the set of ChIP-Seq experiments produced by the ENCODE project (“encode”) or a specific subset of transcription factors (as a vector containing the TF names).

  1. Run the GSEA analysis

The function GSEA_run will perform a GSEA-based analysis on the input gene list. This function is based on the R-GSEA R script bundle written by the GSEA team at the Broad Institute of MIT and Harvard 7 8. The output of the analysis depends on the variable get.RES:

##    Accession       Cell Treatment    TF       ES   pval.ES Arg.ES
## 1   GSE91535       K562      none  ARNT -0.21920 0.1037647  12622
## 2 GSM1024800      MCF-7      none  ARNT  0.27159 0.0048000   2257
## 3 GSM1056831 macrophage  4h 1% O2 HIF1A  0.36013 0.0000000   3231
## 4 GSM1056832 macrophage  4h 1% O2 HIF1A  0.27264 0.0000000   3073
## 5 GSM1056835 macrophage  4h 1% O2 EPAS1  0.35522 0.0000000   2705
## 6 GSM1056836 macrophage  4h 1% O2 EPAS1  0.27971 0.0000000   3070
## [1] -0.0000607054  0.0203471900  0.0202864846  0.0202257792  0.0201650738
## [6]  0.0360215466
## [1] 0 1 0 0 0 1

The list of results can be restricted to a given set of transcription factors by setting the variable RES.filter.

  1. Plotting the results

TFEA.ChIP includes two functions that use the package plotly to generate interactive html plots of your GSEA results: plot_ES and plot_RES.

  1. Plot Enrichment Scores with plot_ES

We can choose to highlight ChIP-Seq from specific transcription factors plotting them in a particular color.

Snapshot of a plot generated with plot_ES.

Snapshot of a plot generated with plot_ES.

  1. Plot Runing Enrichment Scores with plot_RES. This function will plot all the RES stored in the GSEA_run output. It is only recommended to restrict output to specific TF and/or datasets by setting the parameters TF and/or Accession respectively:
Snapshot of a plot generatd with plot_RES.

Snapshot of a plot generatd with plot_RES.

Building a TF-gene binding database

If the user wants to generate their own database of ChIPseq datasets, the functions txt2gr and GR2tfbs_db automate most of the process. The required inputs are:

  1. Filter peaks from source and store them as a GRanges object

Specify the folder where the ChIP-Seq files are stored, create an array with the names of the ChIP-Seq files, and choose a format. Set a for loop to convert all your files to GenomicRanges objects using txt2GR. Please note that, by default, only peaks with an associated p-value of 0.05 (for narrow peaks files) or 1e-5 (for MACS files) will be kept. The user can modify the default values by setting the alpha argument to the desired threshold p-value.

folder<-"~/peak.files.folder"
File.list<-dir(folder)
format<-"macs"

gr.list <- lapply(
    seq_along(File.list),
    function( File.list, myMetaData, format ){
        
        tmp<-read.table( File.list[i], ..., stringsAsFactors = FALSE )
        
        file.metadata <- myMetaData[ myMetaData$Name == File.list[i], ]
        
        ChIP.dataset.gr<-txt2GR(tmp, format, file.metadata)
        
        return(ChIP.dataset.gr)
    },
    File.list = File.list,
    myMetadata = myMetadata,
    format = format
)
# As an example of the output
data("ARNT.peaks.bed","ARNT.metadata",package = "TFEA.ChIP") # Loading example datasets for this function
ARNT.gr<-txt2GR(ARNT.peaks.bed,"macs1.4",ARNT.metadata)
head(ARNT.gr,n=2)
## GRanges object with 2 ranges and 9 metadata columns:
##       seqnames          ranges strand |                score
##          <Rle>       <IRanges>  <Rle> |            <numeric>
##   [1]     chr1 1813434-1814386      * | 4.60908802300594e-06
##   [2]     chr1 3456399-3457536      * | 8.82450123169111e-27
##                       mcols.Name mcols.Accession mcols.Cell
##                         <factor>        <factor>   <factor>
##   [1] GSM2390643_ARNT_ChIPSeq.bw      GSM2390643      HUVEC
##   [2] GSM2390643_ARNT_ChIPSeq.bw      GSM2390643      HUVEC
##       mcols.Cell Type mcols.Treatment mcols.Antibody mcols.TF
##              <factor>        <factor>       <factor> <factor>
##   [1]                         Hypoxia          HIF1B     ARNT
##   [2]                         Hypoxia          HIF1B     ARNT
##        mcols.Score Type
##                <factor>
##   [1] corrected p-Value
##   [2] corrected p-Value
##   -------
##   seqinfo: 32 sequences from an unspecified genome; no seqlengths
  1. [Optional] Dnase Hypersensitive Sites

By default (see step 3), TFEA.ChIP filters the ChIPseq peaks in each dataset to select those overlapping or near to (by default max. distance 10 nucleotides) DNase HS regions located within 1Kb of genes. The database of gene-associated DHSs is generated from Encode’s Master DNaseI HS as follows:

  1. Load Encode’s Master DNaseI HS and convert it to a Genomic Ranges object.
dnaseClusters<-read.table(file="~/path.to.file.txt", 
                          header = TRUE,sep="\t",stringsAsFactors = FALSE)
dnaseClusters<-makeGRangesFromDataFrame(dnaseClusters,
                         ignore.strand=TRUE,
                         seqnames.field="chrom",
                         start.field="chromStart",
                         end.field="chromEnd")
  1. Select the Dnase hypersensitive sites that are 1Kb or closer to a gene and assign a gene ID to every Dnase HS that remains.
txdb<-TxDb.Hsapiens.UCSC.hg19.knownGene
Genes<-genes(txdb)

near.gene<-findOverlaps(dnaseClusters, Genes, maxgap = 1000 )

dnase.sites.list<-queryHits(near.gene)
near.gene<-subjectHits(near.gene)

gene_ids <- Genes[ near.gene ]$gene_id
DHS.database <- dnaseClusters[ dnase.sites.list ]
mcols(DHS.database)$gene_id <- gene_ids

These steps can be modified to generate a new GRanges object containing custom DNAse HS sites.

  1. Assign TFBS peaks from ChIP dataset to specific genes

The function GR2tfbs_db assigns the TFBS peaks in the ChIP datasets stored in gr.list to a gene. To this end, a ChIP peak overlaping a Dnase HS region receive the gene label associated to that region. By default the function also assigns the gene name when the ChIP peak does not overlap a Dnase region but maps at less than 10 nucleotides from it. This behaviour can be modified by setting the argument distanceMargin to the desired value (by default distanceMargin = 10 bases).

data("DnaseHS_db","gr.list", package="TFEA.ChIP") # Loading example datasets for this function
TF.gene.binding.db<-GR2tfbs_db( DnaseHS_db, gr.list ) 
str(TF.gene.binding.db)
## List of 1
##  $ wgEncodeEH002402:Formal class 'GeneSet' [package "GSEABase"] with 13 slots
##   .. ..@ geneIdType      :Formal class 'NullIdentifier' [package "GSEABase"] with 2 slots
##   .. .. .. ..@ type      : chr "Entrez Gene ID"
##   .. .. .. ..@ annotation: chr ""
##   .. ..@ geneIds         : chr [1:54] "6009" "6522" "6604" "100507501" ...
##   .. ..@ setName         : chr "wgEncodeEH002402"
##   .. ..@ setIdentifier   : chr "wgEncodeEH002402"
##   .. ..@ shortDescription: chr "Genes assigned to ChIP-seq"
##   .. ..@ longDescription : chr "Cell: Dnd41, Treatment: none, TF: CTCF"
##   .. ..@ organism        : chr "Homo sapiens"
##   .. ..@ pubMedIds       : chr(0) 
##   .. ..@ urls            : chr(0) 
##   .. ..@ contributor     : chr(0) 
##   .. ..@ version         :Formal class 'Versions' [package "Biobase"] with 1 slot
##   .. .. .. ..@ .Data:List of 1
##   .. .. .. .. ..$ : int [1:3] 0 0 1
##   .. ..@ creationDate    : chr(0) 
##   .. ..@ collectionType  :Formal class 'NullCollection' [package "GSEABase"] with 1 slot
##   .. .. .. ..@ type: chr "Null"

The function, accepts any Genomic Range object that includes a metacolumn with a gene ID (stored in the @elementMetadata@listdata [["gene_id"]] slot of the object) for each genomic segment. For example, asignation of peaks to genes can be done by providing a list of all the genes in the genome:

library(TxDb.Hsapiens.UCSC.hg19.knownGene)
data("gr.list", package="TFEA.ChIP") # Loading example datasets for this function
txdb<-TxDb.Hsapiens.UCSC.hg19.knownGene
Genes<-genes(txdb)
TF.gene.binding.db<-GR2tfbs_db( Genes, gr.list, distanceMargin = 0 )
str(TF.gene.binding.db)
## List of 1
##  $ wgEncodeEH002402:Formal class 'GeneSet' [package "GSEABase"] with 13 slots
##   .. ..@ geneIdType      :Formal class 'NullIdentifier' [package "GSEABase"] with 2 slots
##   .. .. .. ..@ type      : chr "Entrez Gene ID"
##   .. .. .. ..@ annotation: chr ""
##   .. ..@ geneIds         : chr [1:21] "9289" "84991" "3728" "25946" ...
##   .. ..@ setName         : chr "wgEncodeEH002402"
##   .. ..@ setIdentifier   : chr "wgEncodeEH002402"
##   .. ..@ shortDescription: chr "Genes assigned to ChIP-seq"
##   .. ..@ longDescription : chr "Cell: Dnd41, Treatment: none, TF: CTCF"
##   .. ..@ organism        : chr "Homo sapiens"
##   .. ..@ pubMedIds       : chr(0) 
##   .. ..@ urls            : chr(0) 
##   .. ..@ contributor     : chr(0) 
##   .. ..@ version         :Formal class 'Versions' [package "Biobase"] with 1 slot
##   .. .. .. ..@ .Data:List of 1
##   .. .. .. .. ..$ : int [1:3] 0 0 1
##   .. ..@ creationDate    : chr(0) 
##   .. ..@ collectionType  :Formal class 'NullCollection' [package "GSEABase"] with 1 slot
##   .. .. .. ..@ type: chr "Null"

In this case the information about Dnase hypersensitivity is disregarded and peaks are asigned to overlapping genes (or genes closer than distanceMargin residues).

  1. Generation of the TFBS database.

The function makeTFBSmatrix generates a binary matrix with a row for each human gene and a column for each independent ChIPseq dataset. The cell of the matrix contain a value of 1 if the gene is bound by the TF and 0 otherwise. This matrix and the metadata table can be used instead of the default TFBS database.

data("tfbs.database",package = "TFEA.ChIP") # Loading example datasets for this function
txdb<-TxDb.Hsapiens.UCSC.hg19.knownGene
gen.list<-genes(txdb)$gene_id # selecting all the genes in knownGene

myTFBSmatrix<-makeTFBSmatrix(gen.list,tfbs.database)
myTFBSmatrix[2530:2533,1:3] # The gene HMGN4 (Entrez ID 10473) has TFBS for this three ChIP-Seq datasets
##       wgEncodeEH000645 wgEncodeEH000679 wgEncodeEH000694
## 10472                0                0                0
## 10473                1                1                1
## 10474                0                0                0
## 10475                0                0                0
  1. Sustitute the default database by a custom generated table.

At the beginning of a session, use the function set_user_data to use your TFBS binary matrix and metadata table with the rest of the package.

set_user_data(binary_matrix = myTFBSmatrix, metadata = myMetaData)

  1. ENCODE Project Consortium (2012) Nature 489, 57-74)

  2. Edgar, R et al. (2002) Nucleic Acids Res. 30:207-10

  3. Barrett, T et al. (2013) Nucleic Acids Res. 41(Database issue):D991-5

  4. Subramanian, Tamayo, et al. (2005) PNAS 102, 15545-15550

  5. Mootha, Lindgren, et al. (2003) Nat Genet 34, 267-273

  6. Tiana, M et al. The SIN3A histone deacetylase complex is required for a complete transcriptional response to hypoxia. https://doi.org/10.1101/182691

  7. Subramanian, Tamayo, et al. (2005) PNAS 102, 15545-15550

  8. Mootha, Lindgren, et al. (2003) Nat Genet 34, 267-273