Encodings and channels¶
An encoding maps data to a visible property. The property being controlled
is called a channel: x controls horizontal position, color controls
color, and size controls mark size. The field supplies the values, while its
data type tells GenomeSpy how those values should be interpreted.
Field and type shorthand¶
The compact string "score:Q" contains a field name (score) and a type code
(Q) separated by a colon. The shorthand and explicit forms below produce the
same encodings:
import genome_spy as gs
measurements = [
{"sample": "A", "stage": "low", "score": 2.1, "amount": 18, "confidence": 0.55},
{"sample": "A", "stage": "medium", "score": 3.4, "amount": 32, "confidence": 0.82},
{"sample": "A", "stage": "high", "score": 4.2, "amount": 24, "confidence": 0.74},
{"sample": "B", "stage": "low", "score": 1.7, "amount": 22, "confidence": 0.61},
{"sample": "B", "stage": "medium", "score": 2.8, "amount": 15, "confidence": 0.48},
{"sample": "B", "stage": "high", "score": 3.6, "amount": 36, "confidence": 0.91},
]
shorthand_chart = (
gs.Chart(measurements)
.mark_point()
.encode(
x="score:Q",
y="stage:O",
color="sample:N",
)
)
explicit_chart = (
gs.Chart(measurements)
.mark_point()
.encode(
x=gs.X("score", type="quantitative"),
y=gs.Y("stage", type="ordinal"),
color=gs.Color("sample", type="nominal"),
)
)
Use shorthand when a field and type are enough. Use a channel class such as
X, Color, or Tooltip when adding a title, scale, axis, legend, or
another channel option. Setter methods can be chained:
gs.X("score:Q").scale(zero=False).axis(grid=True).title("Score")
Data types describe meaning¶
Choose a type from what a field means, not merely from how Python stores it:
Code |
Type |
Concrete example |
|---|---|---|
|
quantitative |
A measured score such as |
|
nominal |
An unordered sample label such as |
|
ordinal |
An ordered stage such as low, medium, or high |
|
index |
A numbered sequence position with a regular slot |
|
locus |
A chromosome-aware genomic position |
A numeric identifier is usually nominal, not quantitative: sample 12 is not
twice sample 6. Likewise, an ordinal field describes order but does not claim
that the distance from low to medium equals the distance from medium to high.
The selected type affects the default scale and the guide GenomeSpy creates, as
described in
visual encoding.
Visual channels¶
Several encodings can describe different parts of the same row:
channel_chart = (
gs.Chart(measurements)
.mark_point(filled=True, stroke="white", strokeWidth=1)
.encode(
x=gs.X("score:Q").scale(zero=False).title("Score"),
y=gs.Y("stage:O").title("Stage"),
color=gs.Color("sample:N").legend(title="Sample"),
shape="sample:N",
size=gs.Size("amount:Q").legend(title="Amount"),
opacity=gs.Opacity("confidence:Q").scale(range=[0.35, 1]),
tooltip=[
gs.Tooltip("sample:N").title("Sample"),
gs.Tooltip("score:Q").title("Score"),
gs.Tooltip("confidence:Q").format(".0%").title("Confidence"),
],
)
.properties(title="Several channels can describe each row")
)
The most common channels have distinct jobs:
Channels |
Purpose |
|---|---|
|
Primary horizontal and vertical positions |
|
Secondary endpoints for ranges, rectangles, links, and arrows |
|
Visible mark properties |
|
Content drawn by a text mark |
|
Details shown when pointing at a mark |
Position is generally the easiest visual channel to compare accurately. Color and shape are useful for categories; size and opacity can show quantities but are harder to compare precisely. A tooltip adds details without replacing a clear visible encoding. The GenomeSpy documentation lists every channel, including the offset channels, in channels.
Index positions¶
The I type combines numbered positions with regular-width slots. It is useful
for bases, amino acids, matrix columns, and other ordered integer locations:
sequence = [
{"position": 0, "base": "A"},
{"position": 1, "base": "C"},
{"position": 2, "base": "G"},
{"position": 3, "base": "T"},
{"position": 4, "base": "G"},
{"position": 5, "base": "A"},
]
index_chart = (
gs.Chart(sequence)
.mark_text(size=22)
.encode(
x=gs.X("position:I").title("Zero-based index"),
y=gs.value(0.5),
text="base:N",
color=gs.Color("base:N").legend(None),
)
.properties(title="Index values occupy regular slots")
)
Unlike an ordinal category, an index remains linear and can be zoomed: index and locus scales both zoom and pan by default, with no option to set. Each integer also has a band, allowing a rectangle to fill one indexed position. The index scale describes that behavior in detail.
Genomic loci¶
The L type places positions on a chromosome-aware axis. Locus() is the
clearest form when chromosome and position are stored in separate fields:
features = [
{"chrom": "chr17", "start": 43_044_000, "end": 43_050_000, "kind": "A"},
{"chrom": "chr17", "start": 43_057_000, "end": 43_061_000, "kind": "B"},
{"chrom": "chr17", "start": 43_068_000, "end": 43_075_000, "kind": "A"},
]
locus_chart = (
gs.Chart(features)
.mark_rect()
.encode(
x=gs.Locus("chrom", "start").scale(
domain=[
{"chrom": "chr17", "pos": 43_040_000},
{"chrom": "chr17", "pos": 43_080_000},
]
),
x2=gs.Locus("chrom", "end"),
y=gs.Y("kind:N").title("Feature kind"),
color=gs.Color("kind:N").legend(None),
)
.properties(assembly="hg38", title="Locus values follow the genome")
)
The chart’s assembly="hg38" supplies chromosome names, lengths, and order.
The domain sets the initial view to a 40 kb window around these 31 kb of features,
making them immediately visible in detail. The view remains fully zoomable, so users
can zoom out to explore the broader genomic context. The genomic coordinates guide
covers locus domains and coordinate conventions in detail, as does the
locus scale in the
GenomeSpy documentation.
Field, datum, value, and expression¶
Most encodings read a field, but an encoding definition can obtain its value in four ways:
definition_chart = (
gs.Chart(measurements)
.mark_point(filled=True)
.encode(
x=gs.X("score:Q").scale(zero=False),
y=gs.datum(0, type="quantitative").scale(domain=[-1, 1]),
color=gs.value("#4c78a8"),
size=gs.Size(
gs.expr(gs.datum.amount * gs.datum.confidence),
type="quantitative",
)
.scale(domain=[0, 35])
.legend(None),
)
.properties(title="Field, datum, value, and expression definitions")
)
Definition |
Meaning |
Example above |
|---|---|---|
Field |
Read a value from every row |
|
Datum |
Use a constant in the scale’s data domain |
|
Value |
Use a constant visual value without a scale |
|
Expression |
Calculate a value while the chart runs |
A datum goes through the channel’s scale, so these points sit mid-axis; a
positional value of 0 would mean the start of the visual range instead.
Constants and expressions give a scale nothing to derive a domain from, so both
set an explicit domain. Expressions also need an explicit type. The
expression language describes
what an expression may contain.
The multiple sequence alignment example encodes index positions, text, and color from the same rows.