Aggregating Samples¶
Developer Documentation
This page is intended for users who develop tailored visualizations using the GenomeSpy app.
aggregateSamples adds one or more summary tracks to a sample view. Use it to
compare patterns across sample subgroups in addition to inspecting individual
samples.
In the GenomeSpy paper, a summary track shows a copy-number landscape above the main heatmap. The summary is computed separately for each visible group, making recurrent amplification and deletion patterns easier to compare between groups.
Each entry in aggregateSamples is a normal unit or layer spec. It may define
its own transform, encoding, and params. GenomeSpy prepends a
mergeFacets transform automatically and removes the sample channel from the
summary encoding.
sampleCount¶
Aggregate tracks receive the sampleCount parameter. It contains the number of
samples in the current group and can normalize summary values.
{
"type": "formula",
"expr": "datum.coverage / sampleCount",
"as": "coverage"
}
Examples¶
The examples below use synthetic toy data for demonstration purposes.
Copy-number landscape¶
This example shows per-sample segments with layered aggregate amplification and deletion tracks.
The two aggregate layers first select positive or negative logR values. Each
then uses the "coverage" transform to
split overlapping segments into non-overlapping intervals and sum their logR
values. Dividing that sum by sampleCount normalizes the track by the number of
samples in the current group.
{
"description": "Aggregated copy-number-style segments with logR values across a toy genome indexed from 0 to 100.",
"data": {
"values": [
{ "sample": "A", "start": 0, "end": 18, "logR": -0.3 },
{ "sample": "A", "start": 18, "end": 42, "logR": 0.0 },
{ "sample": "A", "start": 42, "end": 58, "logR": 0.45 },
{ "sample": "A", "start": 58, "end": 80, "logR": -0.15 },
{ "sample": "A", "start": 80, "end": 100, "logR": 0.25 },
{ "sample": "B", "start": 0, "end": 25, "logR": 0.1 },
{ "sample": "B", "start": 25, "end": 50, "logR": -0.4 },
{ "sample": "B", "start": 50, "end": 67, "logR": 0.35 },
{ "sample": "B", "start": 67, "end": 100, "logR": 0.05 },
{ "sample": "C", "start": 0, "end": 30, "logR": -0.1 },
{ "sample": "C", "start": 30, "end": 55, "logR": 0.2 },
{ "sample": "C", "start": 55, "end": 72, "logR": 0.6 },
{ "sample": "C", "start": 72, "end": 100, "logR": -0.25 },
{ "sample": "D", "start": 0, "end": 20, "logR": 0.05 },
{ "sample": "D", "start": 20, "end": 45, "logR": 0.4 },
{ "sample": "D", "start": 45, "end": 70, "logR": -0.2 },
{ "sample": "D", "start": 70, "end": 100, "logR": 0.15 },
{ "sample": "E", "start": 0, "end": 15, "logR": -0.5 },
{ "sample": "E", "start": 15, "end": 38, "logR": 0.0 },
{ "sample": "E", "start": 38, "end": 62, "logR": 0.3 },
{ "sample": "E", "start": 62, "end": 88, "logR": -0.1 },
{ "sample": "E", "start": 88, "end": 100, "logR": 0.2 },
{ "sample": "F", "start": 0, "end": 22, "logR": 0.2 },
{ "sample": "F", "start": 22, "end": 48, "logR": -0.15 },
{ "sample": "F", "start": 48, "end": 77, "logR": 0.55 },
{ "sample": "F", "start": 77, "end": 100, "logR": -0.35 }
]
},
"samples": {},
"spec": {
"name": "copy-numbers",
"view": { "fill": "#f4f4f4" },
"mark": {
"type": "rect",
"minOpacity": 1.0,
"minWidth": 1.0
},
"encoding": {
"sample": { "field": "sample" },
"x": {
"field": "start",
"type": "index",
"scale": {
"domain": [0, 100],
"zoom": true
},
"axis": { "grid": true }
},
"x2": { "field": "end" },
"fill": {
"field": "logR",
"type": "quantitative",
"scale": {
"domain": [-2.5, 0, 2.5],
"range": ["#0050f8", "#f6f6f6", "#ff3000"],
"clamp": true
}
}
},
"aggregateSamples": [
{
"name": "logR-summary",
"view": { "stroke": "lightgray" },
"title": {
"text": "logR summary",
"style": "overlay-title"
},
"height": 50,
"padding": { "bottom": 4 },
"transform": [
{
"type": "filter",
"expr": "abs(datum.logR) > 0.1"
},
{
"type": "project",
"fields": ["sample", "start", "end", "logR"]
}
],
"encoding": {
"y": {
"field": "coverage",
"type": "quantitative",
"title": null,
"axis": { "grid": true },
"scale": { "nice": true }
}
},
"templates": {
"coverage": {
"transform": [
{
"type": "filter",
"expr": "logRSign * datum.logR > 0"
},
{
"type": "coverage",
"start": "start",
"end": "end",
"weight": "logR"
},
{
"type": "formula",
"expr": "datum.coverage / sampleCount",
"as": "coverage"
}
],
"encoding": {
"fill": { "value": { "expr": "color" } }
},
"mark": {
"type": "rect",
"tooltip": null
}
}
},
"layer": [
{
"name": "amplification",
"params": { "logRSign": 1, "color": "#e45756" },
"import": { "template": "coverage" }
},
{
"name": "deletion",
"params": { "logRSign": -1, "color": "#4c78a8" },
"import": { "template": "coverage" }
}
]
}
]
}
}
Further reading¶
This technique was used in the GenomeSpy paper to visualize the copy-number landscape of the DECIDER cohort.
Mean z-score example¶
This example aggregates each gene's z-scores with the
"aggregate" transform. It displays the
mean z-score for the samples in the current group as a bar extending from zero,
using the same color scale as the per-sample heatmap.
{
"description": "Toy expression heatmap with five samples and gene-wise z-scores.",
"data": {
"values": [
{
"gene": "TP53",
"S1.z": 1.8,
"S2.z": 1.2,
"S3.z": 0.7,
"S4.z": -0.1,
"S5.z": -0.8
},
{
"gene": "BRCA1",
"S1.z": -0.6,
"S2.z": -0.9,
"S3.z": -1.3,
"S4.z": -1.0,
"S5.z": -0.5
},
{
"gene": "EGFR",
"S1.z": 0.2,
"S2.z": 0.6,
"S3.z": 1.0,
"S4.z": 1.5,
"S5.z": 2.1
},
{
"gene": "CDKN2A",
"S1.z": -1.7,
"S2.z": -1.3,
"S3.z": -0.8,
"S4.z": -0.2,
"S5.z": 0.1
},
{
"gene": "MYC",
"S1.z": 0.4,
"S2.z": 0.8,
"S3.z": 1.1,
"S4.z": 1.4,
"S5.z": 1.0
},
{
"gene": "PTEN",
"S1.z": -0.3,
"S2.z": -0.5,
"S3.z": -0.7,
"S4.z": -1.0,
"S5.z": -1.4
}
]
},
"transform": [
{
"type": "regexFold",
"columnRegex": ["^(S[1-5])\\.z$"],
"asKey": "sample",
"asValue": ["zScore"]
}
],
"samples": {},
"spec": {
"name": "expression-zscores",
"view": { "fill": "#f4f4f4" },
"mark": {
"type": "rect",
"stroke": "white",
"strokeWidth": 1
},
"encoding": {
"sample": { "field": "sample" },
"x": {
"field": "gene",
"type": "nominal",
"scale": {
"domain": ["TP53", "BRCA1", "EGFR", "CDKN2A", "MYC", "PTEN"]
},
"axis": {
"title": "Gene",
"grid": true
}
},
"fill": {
"field": "zScore",
"type": "quantitative",
"scale": {
"domain": [-2.5, 0, 2.5],
"range": ["#2166ac", "#f7f7f7", "#b2182b"],
"clamp": true
}
}
},
"aggregateSamples": [
{
"name": "mean-zscores",
"height": 70,
"view": { "stroke": "lightgray" },
"padding": { "bottom": 4 },
"transform": [
{
"type": "aggregate",
"groupby": ["gene"],
"ops": ["mean"],
"fields": ["zScore"],
"as": ["meanZScore"]
}
],
"mark": {
"type": "rect",
"minWidth": 1,
"stroke": "white",
"strokeWidth": 1,
"tooltip": null
},
"encoding": {
"x": {
"field": "gene",
"type": "nominal"
},
"y": {
"field": "meanZScore",
"type": "quantitative",
"axis": {
"title": "Mean z-score",
"grid": true
},
"scale": {
"nice": true
}
},
"y2": { "datum": 0, "type": "quantitative" },
"fill": {
"field": "meanZScore",
"type": "quantitative",
"scale": {
"domain": [-2.5, 0, 2.5],
"range": ["#2166ac", "#f7f7f7", "#b2182b"],
"clamp": true
},
"legend": null
}
}
}
]
}
}