# Multi-row feature track

The multi-row feature display splits one interval file into a stack of labeled
rows, one per value of a column you nominate, and paints each feature as a
colored block. A cohort of per-sample interval data (chromatin states per cell
type, copy number per tumor, ancestry per haplotype, genotype per strain)
becomes a single track with one config, one adapter, and one fetch.

Any BED, BigBed, GFF3, or GTF track can be switched to it from the track menu:
**Display types → Multi-row feature display (painting)**.

<Figure src="/img/chromhmm.png" caption="Roadmap Epigenomics 15-state ChromHMM as a multi-row track, all 127 epigenomes over chr9 in tissue order, each row labelled and tinted by its tissue group, with NCBI RefSeq genes above. Each row takes the state color the file has: promoters are red in every tissue, and PAX5 (boxed) is transcribed with genic enhancers only in the B cell rows and GM12878." />

## Turning a feature track into rows

Picking it from **Display types** shows the rows right away, split on whichever
column the file turns out to have: a RepeatMasker table opens as ~20 rows of
`repClass`, and anything else falls back to the `name` column. Which column
assigns a feature to a row is then **One row per…** in the same track menu,
which lists the attribute names the loaded features have — so RepeatMasker also
offers `repFamily` and `name` beside the class it started on. Repartitioning
leaves a saved row order, its labels and colors and a clustering run in place:
they name rows by value, so they sit idle under the new partition and come back
with the field. Hidden legend categories clear.

<Figure src="/img/multirow/display_types_menu.png" caption="Turning the UCSC RepeatMasker track into rows: the track menu's Display types submenu (top), and the same window partitioned by repeat class (bottom). Any feature track can be switched over this way." />

To fix the column in config, set
[`rows`](https://jbrowse.org/jb2/docs/config/linearmultirowfeaturedisplay/#slot-rows) in the track
config:

```json addtrack
{
  "type": "FeatureTrack",
  "trackId": "chromhmm",
  "name": "ChromHMM states",
  "uri": "https://example.com/chromhmm.bed.gz",
  "assemblyNames": ["hg19"],
  "displays": [
    {
      "type": "LinearMultiRowFeatureDisplay",
      "displayId": "chromhmm-LinearMultiRowFeatureDisplay",
      "rows": "cellType"
    }
  ]
}
```

`rows` names the feature attribute whose value assigns a feature to a row.
Features sharing a value stack into the same row, and the value becomes the row
label.

The rows are discovered from the values the loaded region holds, so a file that
gains a sample or a category needs no config change, and a region missing one
has no row for it.

### Partitioning with a jexl expression {#when-the-category-is-not-a-column}

A file can have the category without a column for it, in which case `rows` takes
a [jexl](https://jbrowse.org/jb2/docs/config_guides/jexl) expression instead of an attribute name.
UCSC's `bigRmskBed` is the common case: the repeat class is a suffix on the name
(`L1HS#LINE/L1`), so an attribute lookup splits on the full repeat name, which
is thousands of rows rather than twenty.

```json addtrack
{
  "type": "FeatureTrack",
  "trackId": "genark_rmsk",
  "name": "RepeatMasker by class",
  "uri": "https://hgdownload.soe.ucsc.edu/hubs/GCF/019/238/085/GCF_019238085.1/bbi/GCF_019238085.1_USGS_WTPT01.rmsk.bb",
  "assemblyNames": ["GCF_019238085.1"],
  "displays": [
    {
      "type": "LinearMultiRowFeatureDisplay",
      "displayId": "genark_rmsk-LinearMultiRowFeatureDisplay",
      "rows": "jexl:split(split(feature.name,'#')[1],'/')[0]"
    }
  ]
}
```

The final `[0]` selects class; swap it for `[1]` to split by family.

The multi-row display isn't the track's default, so it needs an explicit
`displays` entry rather than the `displayDefaults` shorthand (whose `color`
would also reach the default display).

## Preparing the file

The input is one file holding every sample's intervals, with a column naming the
sample. For BED, name the columns with a `#`-prefixed header line so the adapter
picks them up, then bgzip and tabix as usual:

```text
#chrom  chromStart  chromEnd  name               score  strand  thickStart  thickEnd  itemRgb      cellType
chr1    10000       10600     15_Repetitive/CNV  0      .       10000       10600     245,245,245  GM12878
chr1    10000       10600     15_Repetitive/CNV  0      .       10000       10600     245,245,245  K562
```

Any extra column works the same way, so `rows` can be `sample`, `cellType`,
`strain`, `haplotype`, or whatever you called it. GFF3/GTF attributes and BigBed
extra fields are addressed by name identically.

## Coloring the blocks

Four sources of color, in precedence order:

- [`rowColor`](https://jbrowse.org/jb2/docs/config/linearmultirowfeaturedisplay/#slot-rowcolor) gives a
  color per row as pairs, the row values in `domain` and their colors in
  `range`, or with a `field` a color per value of a row attribute such as its
  `rowGroups` group. Use it when the row identity is the signal (one color per
  population, per treatment arm).
- [`color`](https://jbrowse.org/jb2/docs/config/linearmultirowfeaturedisplay/#slot-color) is a per-
  feature fill: a CSS color, or a field whose values each take a color through a
  scale — a color per value, a color per interval between cut points, or a
  gradient — with a key naming them.
- `itemRgb` is honored automatically. A BED9 that already has per-feature colors
  (ChromHMM state colors, for one) paints correctly with no color configuration.
- Otherwise each row is auto-assigned a distinct color from a categorical
  palette.

Binning a numeric column onto a diverging scale is the common case, e.g. segment
mean copy number, with `labels` naming each interval in the key:

```json addtrack
{
  "type": "FeatureTrack",
  "trackId": "tcga_brca_cnv",
  "name": "TCGA-BRCA copy number",
  "uri": "https://jbrowse.org/demos/tcga/tcga_brca_cnv.bed.gz",
  "assemblyNames": ["hg38"],
  "displays": [
    {
      "type": "LinearMultiRowFeatureDisplay",
      "displayId": "tcga_brca_cnv-LinearMultiRowFeatureDisplay",
      "rows": "sample",
      "color": {
        "field": "segmean",
        "scale": "threshold",
        "domain": ["-1", "-0.3", "0.3", "1"],
        "range": ["#2166ac", "#92c5de", "#f7f7f7", "#f4a582", "#b2182b"],
        "labels": [
          "Deep loss (log2 < -1)",
          "Loss",
          "Balanced",
          "Gain",
          "Amplification (log2 > 1)"
        ],
        "title": "Copy number (log2)"
      }
    }
  ]
}
```

<Figure src="/img/tcga/cohort_cnv_genome.png" caption="TCGA-BRCA copy number across 1104 primary tumors, one row per tumor, colored by the interval the caller's log2 ratio falls in. Recurrent events read as vertical stripes through the stack, under the same cohort's gain/loss frequency." />

**Show... → Show legend** keys the colors actually present, and the
**Categories** submenu beside it hides individual categories, so you can drop
the states or classes you aren't reading and leave the rest painted. The submenu
counts what is hidden and offers **Show all categories** to put them back.
`color.domain` sets the order those rows read in — the values it lists first,
the rest sorted — the same word and rule `domain` orders the track's rows by.

You can also pick a colour for single rows under **Color rows by → Each row** in
**Edit colors/arrangement...**, which writes each as a `rowColor` entry and
applies at render time, with no refetch.

## Row height

The track menu's **Row height** offers **Squeeze to fit view** (the default:
rows divide the track height, so adding samples shrinks the rows), **Normal**,
and **Compact**. With fit, drag the track taller to give a deep cohort more room
per row.

## Ordering and clustering rows

Rows start sorted by value, digits by magnitude, with the row of features with
no value last. Three ways to change that:

- **Edit colors/arrangement...** reorders, relabels or hand-picks rows in a
  dialog, and `rows.domain` pins an explicit order in config: the values it
  lists lead, and the rest sort.
- Right-click a position and choose **Sort rows by color here** to order rows by
  the value each has at that exact base, the analogue of an alignments track's
  sort-by-base. Rows sharing a value become contiguous blocks, turning a QTL
  painting at its peak into a clean split by allele.
- **Clustering → Cluster rows by similarity...** reorders rows so that samples
  with similar paintings sit together, and draws a dendrogram in the sidebar.
  See [](https://jbrowse.org/jb2/docs/user_guides/clustering).

All three write the track's
[`rows`](https://jbrowse.org/jb2/docs/config/linearmultirowfeaturedisplay/#slot-rows) setting, so undo
reaches an arrangement, a shared session contains it, and it survives turning
the track off and on. **Reset row order** appears in the track menu once any of
the three has run, and returns the rows, their labels and their colors to what
the config declares.

[`rowGroups`](https://jbrowse.org/jb2/docs/config/linearmultirowfeaturedisplay/#slot-rowgroups) tags
each row with a `group`, the first `{ match, group }` entry whose regex its name
matches, and
[`facet: "group"`](https://jbrowse.org/jb2/docs/config/linearmultirowfeaturedisplay/#slot-facet) stacks
the rows in bands by it, each band named in a strip beside the dendrogram and
keeping its rows in their arranged order; clustering then runs within each band.
A group carries no color of its own; `rowColor` colors the groups:

```json
{
  "rowGroups": [
    { "match": "^CLUP", "group": "Wolf" },
    { "match": "^CLAT", "group": "Coyote" }
  ],
  "facet": "group",
  "rowColor": {
    "field": "group",
    "domain": ["Wolf", "Coyote"],
    "range": ["rgb(27,120,55)", "rgb(224,130,20)"]
  }
}
```

Bands carry a group by position, which stays readable over many groups where
colors alone blur together, so a long list of groups reads best faceted, with
the colors as the second cue.

**Show... → Show tree** toggles the dendrogram once one has been computed, and
**Show... → Show row labels** the labels beside it, which are useful with no
clustering run.

A row with a color of its own, from
[`rowColor`](https://jbrowse.org/jb2/docs/config/linearmultirowfeaturedisplay/#slot-rowcolor) by its
name or by an attribute such as its group, draws it as a bar at the left of its
label, so a row can be found by color. A row with no value of that attribute,
such as one no `rowGroups` entry matches, takes no color unless `domain` pairs
`""` with one. On a track colored per feature (an `itemRgb` painting, a jexl
`color` slot) the bar is the only place a row's color shows.

<Figure src="/img/tcga/cohort_cnv_erbb2.png" caption="chr17:37.5-42 Mb, 1104 TCGA-BRCA tumors clustered by their copy-number profile over the 1.5 Mb around ERBB2, with the dendrogram beside them. Rows sort into amplified, gained, lost, and balanced bands." />

## Worked examples

Each of these builds the input file and the track config end to end:

- [](https://jbrowse.org/jb2/docs/tutorials/chromhmm) - many cell types from one merged BED, colored by
  `itemRgb`
- [](https://jbrowse.org/jb2/docs/tutorials/tcga_cohort_cnv) - a thousand tumors, colored by the
  interval a numeric column falls in
- [](https://jbrowse.org/jb2/docs/tutorials/bxd_qtl) - strain genotype painting beside a QTL Manhattan
  plot, sorted at the peak
- [](https://jbrowse.org/jb2/docs/tutorials/analyze_trio) - IBD blocks and local ancestry per haplotype

## See also

- [](https://jbrowse.org/jb2/docs/user_guides/quantitative_track) - the same one-row-per-sample idea
  for signal data
- [](https://jbrowse.org/jb2/docs/user_guides/multivariant_track)
- [LinearMultiRowFeatureDisplay config schema](https://jbrowse.org/jb2/docs/config/linearmultirowfeaturedisplay)
- [](https://jbrowse.org/jb2/docs/config_guides/customizing_feature_colors)
- [ROW_HEIGHT_AND_FIT.md](https://github.com/GMOD/jbrowse-components/blob/main/agent-docs/reference/ROW_HEIGHT_AND_FIT.md)
  — the same Row height menu across every multi-row display: the slot whose `0`
  means fit, and the resolved getter beside it that other plugins read

