---
title: "The **seqsetvis** package"
author: "Joseph R Boyd"
output:
  BiocStyle::html_document:
    toc_float: true
vignette: >
    %\VignetteIndexEntry{Overview and Use Cases}
    %\VignetteEngine{knitr::rmarkdown}
    %\VignetteEncoding{UTF-8}
---

<style type = "text/css"> em { font-weight: bold; } </style>

```{r setup, include = FALSE}
knitr::opts_chunk$set(
    collapse = TRUE,
    dpi=60
)
```

# Synopsis
seq - set - vis

`r Githubpkg("jrboyd/seqsetvis")` enables the visualization and analysis of 
multiple genomic datasets. Although seqsetvis was designed for the comparison 
of mulitple ChIP-seq datasets, this package is domain-agnostic and allows the 
processing of multiple genomic coordinate files (bed-like files) and signal 
files (bigwig files).

*seqsetvis* is ideal for the following tasks:

1. Feature assessment and comparison.
2. Differential enrichment or expression results assessment.
3. Replicate comparison and reproducibility analysis.
4. Chromatin state inspection.
5. Clustering of histone modification and/or transcription factor enrichment 
at features such as promoters or enhancers.

*seqsetvis* accepts and outputs the Bioconductor standard GRanges object while 
leveraging the power and speed of the *data.table* package (an enhancement of 
data.frame) with the flexibility of *ggplot2* to provide users with 
visualizations that are simultaneously publication ready while still being 
highly customizable. By default, *data.table* is used entirely internally so 
users unfamiliar with its use can safely ignore it. That said, fans of 
*data.table* can use seqsetvis in a *data.table* exclusive mode if they wish.


# Features

* Mostly it "just works" with minimal assumed knowledge of R or other 
Bioconductor packages.
* All plots produced are `r CRANpkg("ggplot2")` objects. 
    + easily customized according to user preferences by appending *ggplot2* 
functions.
    + powerful plot arrangement with with `r CRANpkg("cowplot")`, 
`r CRANpkg("gridExtra")`, etc.
* Although various plots are implemented, the underlying data are simple in 
structure and easily accessible so **you** can do what **you** want with them.
    + membership table for feature overlaps is just a logical matrix.
    + signal data is stored in a tidy formatted `r CRANpkg("data.table")`, ideal 
    for `r CRANpkg("ggplot2")` visualization.

Although initially designed to focus on peak call sets, *seqsetvis* accepts any
data in *GRanges* format and is therefore flexible enough to 
work dowstream of many genomics tools and packages.  Indeed, set comparison 
visualizations are genomics agnostic and also accept generic data types defining 
sets.  

# Functions

* `ssv*` - most primary user facing functions are prefixed by ssv
    + `ssvOverlapIntervalSets` - derives sets from a list of GRanges
    + `ssvMakeMembTable` - coerces various ways of defining sets to a membership 
table
    + `ssvFactorizeMembTable` - converts a multi column membership table into
    a single factor.
    + `ssvFetch*` - retrieves properly formatted data from .bigwig and .bam 
    files
    + `ssvFeature*` - visualizations of set counts and overlaps
    + `ssvSignal*` - visualizations of signal mapped to genomic coordinates in 
    sets context
* utility functions
    + `easyLoad_*` - loads files into a list of GRanges for input into `ssv*` 
functions
    + miscellaneous functions such as wrappers for 
    data.table functions.



# Installation and Loading

## From Bioconductor

```{r bioc install, eval=FALSE}
## try http:// if https:// URLs are not supported
if (!requireNamespace("BiocManager", quietly=TRUE))
    install.packages("BiocManager")
BiocManager::install("seqsetvis")
```

## Load the library
```{r load seqsetvis, message=FALSE}
library(seqsetvis)
```

## Load optional useful libraries
```{r load optional libs, message = FALSE}
library(GenomicRanges)
library(data.table)
library(cowplot)
theme_set(cowplot::theme_cowplot())
```

# Defining sets
*seqsetvis* has several ways of accepting set information.  Since *seqsetvis* 
was originally intended to focus on comparing sets of peak calls, a list of 
*GRanges* or a *GRangesList* is directly supported but there are more
generic alternatives.

## Overlapping a list of *GRanges*
Many functions in *seqsetvis* require some kind of sets definition
as input.  Sets can be defined in one of several ways.  
`ssvOverlapIntervalSets()` creates a single *GRanges* from a list of *GRanges*
with a logical membership table as metadata columns defining sets.
```{r overlap basic}
olaps = ssvOverlapIntervalSets(CTCF_in_10a_narrowPeak_grs)
head(olaps)
```

`ssvOverlapIntervalSets()` also accepts a *GRangesList* if that's more natural.
```{r overlap GRangesList}
olaps_fromGRangesList = ssvOverlapIntervalSets(
    GenomicRanges::GRangesList(CTCF_in_10a_narrowPeak_grs))
```

The membership data contained in the metadata columns of `olaps` is  
the data structure *seqsetvis* set overlap functions use internally.  
The S4 method `ssvMakeMembTable()` handles the necessary conversions and 
can be extended 
to accept a new class of data as long as it converts to an already accepted 
data type and calls ssvMakeMembTable again.

For instance, `ssvMakeMembTable(olaps)` extracts the membership table from
the mcols of a single *GRanges* object.
```{r ssvMakeMembTable basic}
head(ssvMakeMembTable(olaps))
```

## Overlapping a list of more generic data

`ssvMakeMembTable()` can handle sets defined by a list of vectors.  
These happen to be numeric but nearly anything that `as.character()` works on
should be acceptable.
```{r ssvMakeMembTable numeric}
my_set_list = list(1:3, 2:3, 3:6)
ssvMakeMembTable(my_set_list)
```
Supplying a named list is recommended
```{r ssvMakeMembTable named numeric}
names(my_set_list) = c("first", "second", "third")
ssvMakeMembTable(my_set_list)
```

A list of character vectors works just as well.
```{r ssvMakeMembTable character}
my_set_list_char = lapply(my_set_list, function(x)letters[x])
ssvMakeMembTable(my_set_list_char)
```
Notice the rownames changed as the items changed.




# Visualization of set overlaps, ssvFeature*

The ssvFeature* family of functions all accept a valid set definition and output
a ggplot.  

## Barplot
Barplots are useful for for conveying the size of each set/group.  
```{r barplot, fig.width=4, fig.height=3}
ssvFeatureBars(olaps)
```

## Pie chart
Pie charts are an alternative to barplots but are generally more difficult to 
interpret.
```{r pie, fig.width=5, fig.height=3}
ssvFeaturePie(olaps)
```

## Venn diagram
Venn diagrams show the number of items falling into each overlap category.
At release, *seqsetvis* is limited to displaying 3 sets via venn diagram. This
will be increased in a future update.
```{r venn, fig.width=4, fig.height=3}
ssvFeatureVenn(olaps)
```

## Euler diagram
Often confused with Venn diagrams, Euler diagrams attempt to convey the size
of each set and the degree to which the overlap with circles or ellipses of
varying sizes.  For data of any appreciable complexity, Euler diagrams are 
approximations by neccessity.

There is no hard limit to the number of sets *seqsetvis* can show in an Euler 
diagram but computation time increases rapidly along with the inaccuracy of 
approximation.
```{r euler, fig.width=4, fig.height=3}
ssvFeatureEuler(olaps)
```

## Binary Heatmap 

An alternative to Venn/Euler diagrams, this "heatmap" contains one column per 
set and one row per item with item membership per set mapped to color.
In this example black indicates TRUE for membership hence the top 2/3 is black
for all three sets.
```{r binary heatmap, fig.width=3, fig.height=4}
ssvFeatureBinaryHeatmap(olaps)
```

# Visualization of genomic signal, ssvSignal*

The ssvSignal* family of functions all take as input continuous signal data 
mapped to the genome.  This type of data is generally stored in bedGraph, 
wiggle, or bigWig format.  At this time, *seqsetvis*  directly supports the
bigwig format and creating pileups from indexed .bam files.  
This is accomplished via the `ssvFetchBigwig()` and 
`ssvFetchBam()` functions.

If you don't have .bigWig (or .bigwig or .bw) files you should be able to 
easily generate some using `r Biocpkg("rtracklayer")` `export.bw()` 
alongside `import.bedGraph()`
or `import.wig()` as appropriate.

## Loading bigwig data
```{r ssvFetchBigwig, eval = FALSE}
bigwig_files = c(
    system.file("extdata", "MCF10A_CTCF_FE_random100.bw", 
                package = "seqsetvis"),
    system.file("extdata", "MCF10AT1_CTCF_FE_random100.bw", 
                package = "seqsetvis"),
    system.file("extdata", "MCF10CA1_CTCF_FE_random100.bw", 
                package = "seqsetvis")
)
names(bigwig_files) = sub("_FE_random100.bw", "", basename(bigwig_files))
# names(bigwig_files) = letters[1:3]
olap_gr = CTCF_in_10a_overlaps_gr
target_size = quantile(width(olap_gr), .75)
window_size = 50
target_size = round(target_size / window_size) * window_size
olap_gr = resize(olap_gr, target_size, fix = "center")
bw_gr = ssvFetchBigwig(bigwig_files, olap_gr, win_size = window_size)

bw_gr
```

```{r}
olap_gr = CTCF_in_10a_overlaps_gr
bw_gr = CTCF_in_10a_profiles_gr
```

Overlap information must be transformed into a factor tied to id to be useful.
```{r factorize}
olap_groups = ssvFactorizeMembTable(mcols(olap_gr))
```

## Line plots

Line plots are perhaps the most natural and common type of visualization for
this type of continuous genomic data.  Many genome browsers and other BioC 
packages support
this type of visualization.  These other approaches are very good at show
lot's of different data of multiple types at single regions.  In contrast, 
*seqsetvis* is focused on bringing in data from multiple regions simultaneously.

### Individual line plots

Basic line plot of a subset of regions
```{r lineplot basic, fig.width=6, fig.height=2.5}
# facet labels will display better if split into multiple lines
bw_gr$facet_label = sub("_", "\n", bw_gr$sample)
ssvSignalLineplot(bw_data = subset(bw_gr, id %in% 1:12), facet_ = "facet_label")
```

Facetting by region id is less overwhelming
```{r lineplot region facet, fig.width=5, fig.height=3}
ssvSignalLineplot(bw_data = subset(bw_gr, id %in% 1:4), facet_ = "id")
```

### Aggregated line plots

Aggregate regions by mean 
```{r lineplot aggregated, fig.width=5, fig.height=2}
ssvSignalLineplotAgg(bw_data = bw_gr)
```

Apply spline of 10 to make a prettier plot
```{r lineplot aggregated smoothed, fig.width=5, fig.height=2}
ssvSignalLineplotAgg(bw_data = bw_gr, spline_n = 10)
```

By adding some info to `bw_gr`, tweaking the group_ parameter, and adding a 
facet call we can apply the aggregation function by peak call set and plot them 
separately.

```{r lineplot}
# append set info, modify aggregation group_ and add facet
olap_2groups = ssvFactorizeMembTable(ssvMakeMembTable(olap_gr)[, 1:2])
grouped_gr = GRanges(merge(bw_gr, olap_2groups))
grouped_gr = subset(grouped_gr, sample %in% c("MCF10A_CTCF", "MCF10AT1_CTCF"))
ssvSignalLineplotAgg(bw_data = grouped_gr, spline_n = 10,
                     group_ = c("sample", "group")) + 
    facet_wrap("group", ncol = 2) +
    labs(title = "Aggregated by peak call set", y = "FE", x = "bp from center")
```

## Scatterplot

Scatterplots are a common way of viewing the relationship between two variables.
*seqsetvis* allows users to easily apply a summary function (mean by default) to 
each region as measured in two samples and plot them on the x and y axis.

A basic scatterplot
```{r scatterplot basic, fig.width=3, fig.height=3}
ssvSignalScatterplot(bw_gr, x_name = "MCF10A_CTCF", y_name = "MCF10AT1_CTCF")
```

Using the result of `olap_groups = ssvFactorizeMembTable(olap_gr)` to add color
based on our peak calls.
```{r scatterplot all sets, fig.width=8, fig.height=3}
ssvSignalScatterplot(bw_gr, x_name = "MCF10A_CTCF", y_name = "MCF10AT1_CTCF", 
                     color_table = olap_groups)
```

More than a few colors gets confusing quickly.  Let's limit the color groups to 
the samples being plotted.
```{r scatterplot 2 sets, fig.width=6, fig.height=3}
# by subsetting the matrix returned by ssvMakeMembTable() we have a lot of 
# control over the coloring.
olap_2groups = ssvFactorizeMembTable(ssvMakeMembTable(olap_gr)[, 1:2])
ssvSignalScatterplot(bw_gr, x_name = "MCF10A_CTCF", y_name = "MCF10AT1_CTCF", 
                     color_table = olap_2groups)
```

Coloring by the third sample's peak call might be interesting.
```{r outside group, fig.width=5, fig.height=3}
olap_OutGroups = ssvFactorizeMembTable(
    ssvMakeMembTable(olap_gr)[, 3, drop = FALSE])
ssvSignalScatterplot(bw_gr, 
                     x_name = "MCF10A_CTCF", 
                     y_name = "MCF10AT1_CTCF", 
                     color_table = olap_OutGroups)
```


We can also get clever with *ggplot2*'s facetting.
```{r scatterplot facet, fig.width=6, fig.height=4}
#tweaking group description will clean up plot labels a lot
olap_groups$group = gsub("_CTCF", "", olap_groups$group)
olap_groups$group = gsub(" & ", "\n", olap_groups$group)
ssvSignalScatterplot(bw_gr, x_name = "MCF10A_CTCF", y_name = "MCF10AT1_CTCF", 
                     color_table = olap_groups) + 
    facet_wrap("group") + guides(color = "none") +
    theme_linedraw() 
```

## Banded quantiles

These quantile bands are a more sophisticated way to aggregate the same data 
shown in the line plots.
```{r banded quantiles}
ssvSignalBandedQuantiles(bw_gr, by_ = "sample", hsv_grayscale = TRUE, 
                         hsv_symmetric = TRUE, 
                         hsv_reverse = TRUE)
```

## Heatmap

Heatmaps combined with clustering are a powerful way to find similarly marked
regions of the genome.  Calling ssvSignalHeatmap will perform k-means clustering 
automatically.  k-means clusters sorted by similarity as determined by 
hierarchical clustering.
```{r heatmap basic, message=FALSE, fig.width=5}
ssvSignalHeatmap(bw_gr, nclust = 3, facet_ = "facet_label") 
```

Performing clustering manually allows for use of cluster info.
```{r heatmap perform pre-clustering}
bw_clust = ssvSignalClustering(bw_gr, nclust = 3)
bw_clust
```

Cluster selection:
```{r heatmap cluster selection}
subset(bw_clust, cluster_id == 3)
```

Plotting and pre-clustering (identical to just calling heatmap with same 
parameters)
```{r heatmap use pre-cluster, message=FALSE, fig.width=5}
ssvSignalHeatmap(bw_clust, facet_ = "facet_label")
```


# Use case : CTCF in breast cancer

This vignette runs using a small subset of datasets that are publicly available 
via [GEO](https://www.ncbi.nlm.nih.gov/geo/).  These data are fully described 
in [GSE98551](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE98551) and 
the associated paper: 
[Intranuclear and higher-order chromatin organization of the major histone gene cluster in breast cancer](https://www.ncbi.nlm.nih.gov/pubmed/28504305)

In brief, the three cell lines MCF10A, MCF10A-at1, and MCF10A-CA1a are used as 
a model of breast cancer progression from normal-like MCF10A to  
metastatic MCF10A-CA1a.  The ChIP-seq target CTCF is both a transcriptional
regulator and an insulator frequently associated with the boundaries of genomic 
structural domains.
Therefore, regions where CTCF changes are interesting for follow-up to see if 
they are also associated wtih regulatory or structural changes and perhaps 
important to breast cancer progression.

To download the full dataset see: [narrowPeak from GEO](#np_load) and 
[bigwig from GEO](#bw_load)

We'll use `r CRANpkg("cowplot")` to arrange our plots in this section.

## Setup

Set paths to bigwig files and narrowPeak files.  There is some extra code here
to download the complete datasets using *BiocFileCache* **IF** the package
*BiocFileCache* is installed and `cache_path` already exists.  If enabled, this
downloads ~3 GB of data.

```{r setup np_files bw_files}
pkgdata_path = system.file("extdata", 
                           package = "seqsetvis")
cache_path = paste0(pkgdata_path, "/.cache")
# the next line is enough to initialize the cache
# BiocFileCache(cache = cache_path)
use_full_data = dir.exists(cache_path) & require(BiocFileCache)
if(use_full_data){
    library(BiocFileCache)
    ssv_bfc = BiocFileCache(cache = cache_path)
    bw_files = vapply(seq_along(CTCF_in_10a_bigWig_urls), function(i){
        rname = paste(names(CTCF_in_10a_bigWig_urls)[i], 
                      "bigwig", 
                      sep = ",")
        fpath = CTCF_in_10a_bigWig_urls[i]
        #bfcrpath calls bfcadd() if necessary and returns file path
        bfcrpath(ssv_bfc, rname = rname, fpath = fpath)
    }, "char")
    names(bw_files) = names(CTCF_in_10a_bigWig_urls)
    
    np_files = vapply(seq_along(CTCF_in_10a_narrowPeak_urls), function(i){
        rname = paste(names(CTCF_in_10a_narrowPeak_urls)[i], 
                      "narrowPeak", 
                      sep = ",")
        fpath = CTCF_in_10a_narrowPeak_urls[i]
        #bfcrpath calls bfcadd() if necessary and returns file path
        bfcrpath(ssv_bfc, rname = rname, fpath = fpath)
    }, "a")
    names(np_files) = names(CTCF_in_10a_narrowPeak_urls)
}else{
    bw_files = vapply(c("MCF10A_CTCF", "MCF10AT1_CTCF", "MCF10CA1_CTCF"), 
    function(x){
        system.file("extdata", paste0(x, "_FE_random100.bw"), 
                    package = "seqsetvis")
    }, "char")
    # set filepaths
    np_files = c(
        system.file("extdata", "MCF10A_CTCF_random100.narrowPeak", 
                    package = "seqsetvis"),
        system.file("extdata", "MCF10AT1_CTCF_random100.narrowPeak", 
                    package = "seqsetvis"),
        system.file("extdata", "MCF10CA1_CTCF_random100.narrowPeak", 
                    package = "seqsetvis")
    )
    
    names(np_files) = sub("_random100.narrowPeak", "", 
                          x = basename(np_files))
}
```

## Loading narrowPeak files as *GRanges*

```{r load package narrowPeak}
# load peak calls
np_grs = easyLoad_narrowPeak(np_files)
```

Perform overlapping

```{r overlap peaks}
olaps = ssvOverlapIntervalSets(np_grs)
```

## Peak set desciption

Here we use a barplot (A) to convey the total number of peaks in each
cell line.  The Venn diagram (B) shows precisely how many peaks are in each
overlap set.  The Euler diagram (C) lacks the numerical precision (who could
say how many peaks are only present in MCF10AT1?) but the basic trends are 
more obvious (MCF10A has the most peaks and the majority of peaks are common to
all 3 cell lines).

```{r ctcf fig1,hold=TRUE, fig.align='center', fig.height=4, fig.width = 8}
p_bars = ssvFeatureBars(olaps, show_counts = FALSE) +  
    theme(legend.position = "left")
p_bars = p_bars + theme(axis.text.x = element_blank(), 
                axis.ticks.x = element_blank(),
                legend.justification = "center") +
    labs(fill = "cell line")
p_venn = ssvFeatureVenn(olaps, counts_txt_size = 4) + 
    guides(fill = "none", color = "none")
p_euler = ssvFeatureEuler(olaps) + 
    guides(fill = "none", color = "none")

cowplot::ggdraw() +
    cowplot::draw_plot(p_bars, x = 0, y = 0, width = .4, height = .7) +
    cowplot::draw_plot(p_venn, x = .4, y = .1, width = .3, height = .7) +
    cowplot::draw_plot(p_euler, x = 0.7, y = .1, width = 0.3, height = .7) +
    cowplot::draw_plot_label(c("CTCF binding in breast cancer cell lines", 
                               "A", "B", "C"),
                             x = c(.04, .17, 0.4, .7),
                             y = c(.92, .75, .75, .75), size = 10, hjust = 0)
```

Plot colors can be modified via the ggplot objects without recalling plot 
functions. (These colors aren't better per se.)

```{r color change, eval=FALSE,hold = TRUE, fig.align='center', fig.height=4, fig.width = 8}
col_vals = c("MCF10A_CTCF" = 'red', 
             "MCF10AT1_CTCF" = "blue", 
             "MCF10CA1_CTCF" = "green")
sf = scale_fill_manual(values = col_vals)
sc = scale_color_manual(values = col_vals)
cowplot::ggdraw() +
    cowplot::draw_plot(p_bars + sf, 
                       x = 0, y = 0, 
                       width = .4, height = .7) +
    cowplot::draw_plot(p_venn + sf + sc, 
                       x = .4, y = .1, 
                       width = .3, height = .7) +
    cowplot::draw_plot(p_euler + sf + sc, 
                       x = 0.7, y = .1, 
                       width = 0.3, height = .7) +
    cowplot::draw_plot_label(c("CTCF binding in breast cancer cell lines", 
                               "A", "B", "C"),
                             x = c(.04, .17, 0.4, .7),
                             y = c(.92, .75, .75, .75), size = 10, hjust = 0)
```

## Annotation with *ChIPpeakAnno*

By using an annotation package like `r Biocpkg("ChIPpeakAnno")` we can 
assign some biology to each peak.

```{r load ChIPpeakAnno, message=FALSE}
library(ChIPpeakAnno)
data(TSS.human.GRCh38)
macs.anno <- annotatePeakInBatch(olaps, AnnotationData=TSS.human.GRCh38)
```

This gets us to easily selectable "gene lists" that everyone wants.

Apply a limit of 10 kb to reduce non-specific associations.

```{r distance filter}
macs.anno = subset(macs.anno, distancetoFeature < 1000)
```

Gene ids associated with peaks present in MCF10A and MCF10AT1 but not 
MCF10CA1.

```{r subset 1}
subset(macs.anno, MCF10AT1_CTCF & MCF10A_CTCF & !MCF10CA1_CTCF)$feature
```

Gene ids associated with the peaks present in MCF10A and neither MCF10AT1 or 
MCF10CA1.

```{r subset 2}
subset(macs.anno, MCF10A_CTCF & !MCF10AT1_CTCF & !MCF10CA1_CTCF)$feature
```

With a meaningfully chosen set of substantial size we could then move on 
to gene ontology or a gene set enrichment type of analysis.

... if we're confident that this peak call is picking up real biological 
differences.  Let's take a closer look at these peak calls in the next section.

## Peak call validation

Digging into a new set of peak calls can be time-consuming and arbitrary.  It's
easy to spend a lot of time with a genome browser checking genes of interest or
peaks at random and not come away with a clear idea how you well your peak call
is performing.

*seqsetvis* is ideal for systematically assessing just what qualifies as a peak
in each sample of an experiment.

## Loading bigwig data

Before loading bigwigs, we need to decide on a uniform region size to retrieve.
In this case we'll use the 75th quantile of merged peak widths and round up to 
the nearest multiple of window size.  This is a bit arbitrary but a good fit in 
practice.  We'll fully capture 75% of peak regions 
and not be influenced by outliers.

This is the same procedure `ssvFetchBigwig()` uses to automatically
set a fixed width.

```{r set fixed width, fig.height=3, fig.width=3}
window_size = 50
width_q75 = quantile(width(olaps), .75)
width_q75 = ceiling(width_q75 / window_size) * window_size
hist_res = hist(width(olaps))
lines(rep(width_q75, 2), c(0, max(hist_res$counts)), col = "red", lwd = 5)
text(width_q75, max(hist_res$counts), "fixedSize", adj = c(-.1, 1), col = "red")
## apply width
olaps_fixedSize = centerFixedSizeGRanges(olaps, width_q75)
```

### Fetch profiles

Reading bigWig formatted files will not work at all on Windows OS. 
If you are able to read bigwigs on your system, 
there are few options.

Loading from the subsetted bigwigs inlcuded with *seqsetvis*.
This code chunk results in identical data to that stored in the
`CTCF_in_10a_profiles_gr` data object.

```{r fetch package bw}
if(use_full_data){
    bw_gr = ssvFetchBigwig(file_paths = bw_files, 
                                    qgr = olaps_fixedSize,
                                    win_size = 50)
}
```

## Inspecting peaks

A) This scatterplot makes it clear that the peaks that looked unique to MCF10A 
have comparable fold-enrichment in MCF10AT1.  This suggests that there may not
be a meaningful biological difference between peaks that are present in MCF10A 
but absent in MCF10AT1.  

B) Likewise for the comparison of MCF10A and MCF10CA1.  In this case, there is a 
systematic reduction in fold-enrichment in CA1 compared to 10A. While it's 
possible there's some biology at play, it's far more likely that the 
ChIP procedure for CA1 simply wasn't as successful.
```{r ctcf scatterplots, fig.width=10, fig.height=4, message=FALSE}
# shortening colnames will make group names less cumbersome in plot legend
colnames(mcols(olaps_fixedSize)) = sub("_CTCF", "", 
                                       colnames(mcols(olaps_fixedSize)))

all_groups = levels(ssvFactorizeMembTable(
    ssvMakeMembTable(olaps_fixedSize))$group)
all_colors = RColorBrewer::brewer.pal(length(all_groups), "Set1")
all_colors[5:7] = safeBrew(3, "Dark2")
names(all_colors) = all_groups
olap_groups_12 = ssvFactorizeMembTable(
    ssvMakeMembTable(olaps_fixedSize)[, 1:2])
p_12 = ssvSignalScatterplot(bw_gr, 
                            x_name = "MCF10A_CTCF", 
                            y_name = "MCF10AT1_CTCF", 
                            color_table = olap_groups_12) + 
    scale_color_manual(values = all_colors)

olap_groups_13 = ssvFactorizeMembTable(
    ssvMakeMembTable(olaps_fixedSize)[, c(1,3)])
p_13  = ssvSignalScatterplot(bw_gr, 
                             x_name = "MCF10A_CTCF", 
                             y_name = "MCF10CA1_CTCF", 
                     color_table = olap_groups_13) + 
    scale_color_manual(values = all_colors)


if(use_full_data){
    tp_12 = p_12 + scale_size_continuous(range = .1) + 
        scale_alpha_continuous(range = .1) + 
        geom_density2d(aes(color = group), h = 40, bins = 3)
    tp_13 = p_13 + scale_size_continuous(range = .1) + 
        scale_alpha_continuous(range = .1) + 
        geom_density2d(aes(color = group), h = 40, bins = 3)
    cowplot::plot_grid(tp_12 + labs(title = ""), 
                       tp_13 + labs(title = ""), 
                       label_y = .85, labels = "AUTO") 
}else{
    cowplot::plot_grid(p_12 + labs(title = ""), 
                       p_13 + labs(title = ""), 
                       label_y = .85, labels = "AUTO")
}

```

These observations are reinforced by the heatmap.

```{r ctcf heatmap, message=FALSE, fig.width=5}
bw_gr$facet_label = sub("_", "\n", bw_gr$sample)
clust_gr = ssvSignalClustering(bw_gr, nclust = 3, facet_ = "facet_label")
ssvSignalHeatmap(clust_gr, facet_ = "facet_label") + labs(fill = "FE", 
                                  y = "region", 
                                  x = "bp from center")
```

The heatmap can be improved by using `centerAtMax()` to allow each region to
be shifted such that the maximum signal is at the center.  A couple parameter
notes:
+ `view_size` controls how far each region can be shifted.  
Shifting will cause overhangs which are trimmed by default so the resulting 
regions are smaller.  You may need to extened the origial query GRanges 
set and repeat the fetch if the resulting regions are too small.
+ The `by_` parameter is particularly tricky.  Its default value of "id"
is probably what we want.  This causes each region to be individually shifted 
according to the maximum across samples.  One alternative, 
`by_ = c("id", "sample")` would have shifted each region in each sample.  
Another alternative, `by_ = ""` or `by_ = NULL` would shift every region in 
every sample identically.
```{r ctcf recentered heatmap, message = FALSE, fig.width=10, fig.height=6}
center_gr = centerAtMax(clust_gr, view_size = 150, 
                        by_ = "id", check_by_dupes = FALSE)


p_center_hmap = ssvSignalHeatmap(center_gr, facet_ = "facet_label") + 
    labs(fill = "FE",
         y = "region",
         x = "bp from center")

## since center_gr still retains clustering information, clustering is not
## repeated by default, the following reclusters the data.
clust_center_gr = ssvSignalClustering(center_gr, nclust = 3)
p_center_hmap_reclust = ssvSignalHeatmap(clust_center_gr, 
                                         facet_ = "facet_label") + 
    labs(fill = "FE",
         y = "region",
         x = "bp from center")
cowplot::plot_grid(p_center_hmap + labs(title = "original clustering"), 
                   p_center_hmap_reclust + labs(title = "reclustered"))
```

Cluster 1 and 2 seem to have the most robust enrichment.  Let's extract them
and annotate.

```{r cluster annotation}
clust_df = as.data.frame(mcols(clust_gr))
clust_df = unique(clust_df[,c("id", "cluster_id")])

olap_clust_annot = olap_gr
mcols(olap_clust_annot) = data.frame(id = seq_along(olap_clust_annot))
olap_clust_annot = GRanges(merge(olap_clust_annot, clust_df))

olap_clust_annot = subset(olap_clust_annot, cluster_id %in% 1:2)

olap_clust_annot <- annotatePeakInBatch(olap_clust_annot, 
                                        AnnotationData=TSS.human.GRCh38)
olap_clust_annot$feature
```

# Use case: ChromHMM inspection

In this demonstration we'll use the same CTCF dataset in breast cancer but 
bring in a chromHMM state file from an other dataset.  This is meant to 
highlight the flexibility of *seqsetvis*.

## Setup

If the previous CTCF in breast cancer **Use Case** made use of *BiocFileCache*,
we'll continue caching external data sources. If not, we'll skip to a 
pre-cooked version of the same data.

The pre-cooked version is quite a narrow slice and there's a significant 
amount of data manipulation 

```{r}
target_data = "MCF10A_CTCF"
chmm_win = 200 #window size is an important chromHMM parameter.
# 200 is the default window size and matches the state segementation

if(use_full_data){
    # set all file paths
    chmm_bw_file = bfcrpath(ssv_bfc, rnames = paste(target_data, "bigwig", 
                                                    sep = ","))
    chmm_np_file = bfcrpath(ssv_bfc, rnames = paste(target_data, "narrowPeak", 
                                                    sep = ","))
    chmm_seg_file = bfcrpath(ssv_bfc, rnames = "MCF7,segmentation",
                             fpath = chromHMM_demo_segmentation_url)
    
    query_chain = bfcquery(ssv_bfc, "hg19ToHg38,chain")
    if(nrow(query_chain) == 0){
        chain_hg19ToHg38_gz = bfcrpath(ssv_bfc, rnames = "hg19ToHg38,gz",
                                       fpath = chromHMM_demo_chain_url)
        ch_raw = readLines(gzfile(chain_hg19ToHg38_gz))
        ch_file = bfcnew(ssv_bfc, rname = "hg19ToHg38,chain")
        writeLines(ch_raw, con = ch_file)
        
    }
    chmm_chain_file = bfcrpath(ssv_bfc, rnames = "hg19ToHg38,chain")
    ch = rtracklayer::import.chain(chmm_chain_file)    
    # load segmentation data
    chmm_gr = rtracklayer::import.bed(chmm_seg_file)
    #cleanup state names.
    chmm_gr$name = gsub("\\+", " and ", chmm_gr$name)
    chmm_gr$name = gsub("_", " ", chmm_gr$name)
    #setup color to state mapping
    colDF = unique(mcols(chmm_gr)[c("name", "itemRgb")])
    state_colors = colDF$itemRgb
    names(state_colors) = colDF$name
    #liftover states from hg19 to hg38
    ch = rtracklayer::import.chain(chmm_chain_file)
    chmm_gr_hg38 = rtracklayer::liftOver(chmm_gr, ch)
    chmm_gr_hg38 = unlist(chmm_gr_hg38)
    chmm_grs_list = as.list(GenomicRanges::split(chmm_gr_hg38, 
                                                 chmm_gr_hg38$name))
    #transform narrowPeak ranges to summit positions
    chmm_np_grs = easyLoad_narrowPeak(chmm_np_file, file_names = target_data)
    chmm_summit_grs = lapply(chmm_np_grs, function(x){
        start(x) = start(x) + x$relSummit
        end(x) = start(x)
        x
    })
    qlist = append(chmm_summit_grs[1], chmm_grs_list)
    chmm_olaps = ssvOverlapIntervalSets(qlist, use_first = TRUE)
    #discard the columns for peak call and no_hit, not informative here.
    mcols(chmm_olaps)[[1]] = NULL
    chmm_olaps$no_hit = NULL
    #total width of genome assigned each state
    state_total_widths = sapply(chmm_grs_list, function(my_gr){
        sum(as.numeric(width(my_gr)))
    })
    #Expand state regions into 200 bp windows.
    state_wingrs = lapply(chmm_grs_list, function(my_gr){
        st = my_gr$name[1]
        wgr = unlist(slidingWindows(my_gr, chmm_win, chmm_win))
        wgr$state = st
        wgr
    })
    state_wingrs = unlist(GRangesList(state_wingrs))
    # fetch bw data for each state
    # it probably isn't useful to grab every single window for each state
    # so we can cap the number of each state carried forward
    max_per_state = 5000
    # flank size zooms out a bit from each chromHMM window
    flank_size = 400
    
    state_split = split(state_wingrs, state_wingrs$state)
    state_split = lapply(state_split, function(my_gr){
        samp_gr = sample(my_gr, min(length(my_gr), max_per_state))
        samp_gr = sort(samp_gr)
        names(samp_gr) = seq_along(samp_gr)
        samp_gr
    })
    state_gr = unlist(GRangesList(state_split))
    state_gr = resize(state_gr, width = chmm_win + 2 * flank_size, 
                      fix = "center")
    
    bw_states_gr = ssvFetchBigwig(bw_file = chmm_bw_file, 
                                       qgr = state_gr, 
                                       win_size = 50)
    bw_states_gr$grp = sub("\\..+", "", bw_states_gr$id)
    bw_states_gr$grp_id = sub(".+\\.", "", bw_states_gr$id)
}else{
    max_per_state = 20
    flank_size = 400
    state_colors = chromHMM_demo_state_colors
    bw_states_gr = chromHMM_demo_bw_states_gr
    chmm_olaps = chromHMM_demo_overlaps_gr
    state_total_widths = chromHMM_demo_state_total_widths
}

```

## Peak overlaps with states, counts
Create barplot of raw count of peaks overlapping states.

```{r state raw, message = FALSE, fig.width=3, fig.height=3}
olaps_df = as.data.frame(mcols(chmm_olaps))
colnames(olaps_df) = gsub("\\.", " ", colnames(olaps_df))

p_state_raw_count = ssvFeatureBars(olaps_df, show_counts = FALSE) +
    labs(fill = "state", x = "") +
    scale_fill_manual(values = state_colors) +
    theme_cowplot() + guides(fill = "none") +
    theme(axis.text.x = element_text(angle = 90, size = 8,
                                     hjust = 1, vjust = .5))
p_state_raw_count
```

## Peak overlaps with states, enrichment
Create barplot of enrichment of peaks overlapping states.

```{r state enrichment, fig.width=3, fig.height=3}
state_width_fraction = state_total_widths / sum(state_total_widths)

state_peak_hits =  colSums(olaps_df)
state_peak_fraction = state_peak_hits / sum(state_peak_hits)

enrichment = state_peak_fraction / 
    state_width_fraction[names(state_peak_fraction)]
enrich_df = data.frame(state = names(enrichment), enrichment = enrichment)
p_state_enrichment =  ggplot(enrich_df) +
    geom_bar(aes(x = state, fill = state, y = enrichment), stat = "identity") +
    labs(x = "") + 
    theme_cowplot() + guides(fill = "none") +
    scale_fill_manual(values = state_colors) +
    theme(axis.text.x = element_text(angle = 90, size = 8,
                                     hjust = 1, vjust = .5))
p_state_enrichment
```

## Aggregated line plots by state
Plot aggregated line plot by state

```{r, message=FALSE, fig.width=6}
p_agg_tracks = ssvSignalLineplotAgg(bw_states_gr, 
                                    sample_ = "grp", 
                                    color_ = "grp")
gb = ggplot2::ggplot_build(p_agg_tracks)
yrng = range(gb$data[[1]]$y)
p_agg_tracks = p_agg_tracks +
    scale_color_manual(values = state_colors) +
    annotate("line", x = rep(-chmm_win/2, 2), y = yrng) +
    annotate("line", x = rep(chmm_win/2, 2), y = yrng) +
    labs(y = "FE", x = "bp", color = "state", 
         title = paste("Average FE by state,", target_data), 
         subtitle = paste("states sampled to", 
                          max_per_state, 
                          chmm_win, 
                          "bp windows each\n", 
                          flank_size, 
                          "bp flanking each side")) +
    theme(plot.title = element_text(hjust = 0))
p_agg_tracks
```

## States heatmap
A heatmap using the same data from the aggregated line plot.  Facetting is
done on state and regions are sorted in decreasing order of signal at center.

```{r state heatmap, fig.width=8}
pdt = as.data.table(mcols(bw_states_gr))
pdt$grp_id = as.integer(pdt$grp_id)
# reassign grp_id to sort within each state set
dt_list = lapply(unique(pdt$grp), function(state){
    dt = pdt[grp == state]
    dtmax = dt[, .(ymax = y[which(x == x[order(abs(x))][1])]), by = grp_id]
    dtmax = dtmax[order(ymax, decreasing = TRUE)]
    dtmax[, grp_o := seq_len(.N)]
    dtmax$ymax = NULL
    dt = merge(dtmax, dt)
    dt[, grp_id := grp_o]
    dt$grp_o = NULL
    dt
})
# reassemble
pdt = rbindlist(dt_list)
# heatmap facetted by state and sorted in decreasing order
p_state_hmap = ggplot(pdt) + 
    geom_raster(aes(x = x, y = grp_id, fill = y)) + 
    scale_y_reverse() +
    facet_wrap("grp", nrow = 2) +
    theme(axis.text.y = element_blank(), 
          axis.ticks.y = element_blank(),
          axis.line.y = element_blank(), 
          axis.text.x = element_text(angle = 90, hjust = 1, vjust = .5),
          strip.text = element_text(size= 8)) +
    scale_fill_gradientn(colors = c("white", "orange", "red")) +
    labs(y = "", fill = "FE", 
         x = "bp from local summit", 
         title = paste(max_per_state, "random regions per state"))
p_state_hmap
```

## Figure assembly
Tie it all together into one figure with cowplot.

```{r, fig.height=12, fig.width=8}
bar_height = .25
line_height = .3
ggdraw() +
    draw_plot(p_state_raw_count + guides(fill = "none"), 
              x = 0, 
              y = 1 - bar_height, 
              width = .5, 
              height = bar_height) +
    draw_plot(p_state_enrichment + guides(fill = "none"), 
              x = .5, 
              y = 1 - bar_height, 
              width = .5, 
              height = bar_height) +
    draw_plot(p_agg_tracks, 
              x = 0, 
              y = 1 - bar_height - line_height, 
              width = 1, 
              height = line_height) +
    draw_plot(p_state_hmap, 
              x = 0, 
              y = 0, 
              width = 1, 
              height = 1 - bar_height - line_height) +
    draw_plot_label(LETTERS[1:4], 
                    c(0, 0.48, 0, 0), 
                    c(1, 1, 1 - bar_height, 1 - bar_height - line_height), 
                    size = 15)
```
