Scales, axes, and legends¶
An encoding says which data field controls a visual channel. A scale performs the conversion from data values to visible values. Axes and legends are guides that explain the conversion to the user.
Start with automatic defaults¶
GenomeSpy chooses scales and guides from the channel and data type. This chart does not configure any of them explicitly:
import genome_spy as gs
measurements = [
{"sample": "control", "time": 1, "value": 2.1},
{"sample": "control", "time": 2, "value": 2.8},
{"sample": "control", "time": 3, "value": 3.2},
{"sample": "treated", "time": 1, "value": 2.4},
{"sample": "treated", "time": 2, "value": 3.5},
{"sample": "treated", "time": 3, "value": 4.3},
]
automatic_chart = (
gs.Chart(measurements)
.mark_point(filled=True, size=90)
.encode(
x="time:Q",
y="value:Q",
color="sample:N",
)
.properties(title="Automatic scales and guides")
)
The quantitative x and y fields create continuous positional scales and
axes. The nominal color field creates a discrete color scale and a legend.
These defaults are usually the best starting point.
Domain and range¶
Every scale maps a domain to a range:
the domain contains data values, such as times from
1to3or the sample namescontrolandtreated;the range contains visible outputs, such as pixel positions or the colors blue and red.
For an x scale, the chart width supplies the pixel range. For a color scale,
the range is a palette. GenomeSpy normally derives the domain from the data and
chooses a suitable range for the channel.
Use .scale(...) when the automatic choice does not express the intended
comparison. This example fixes the visible domains, assigns stable group
colors, and makes the horizontal scale zoomable:
customized_chart = (
gs.Chart(measurements)
.mark_point(filled=True, size=90)
.encode(
x=gs.X("time:Q")
.scale(domain=[0.5, 3.5], zoom=True)
.axis(tickMinStep=1, grid=False)
.title("Time point"),
y=gs.Y("value:Q")
.scale(domain=[1.5, 4.5], zero=False)
.axis(grid=True, tickCount=4)
.title("Response"),
color=gs.Color("sample:N")
.scale(
domain=["control", "treated"],
range=["#4c78a8", "#e45756"],
)
.legend(title="Sample group", orient="top", direction="horizontal"),
)
.properties(title="Selected scale and guide options")
)
The color mapping is now deterministic:
[control, treated] → [blue, red]. This is useful when the same category must
retain the same color across figures. The positional domain represents the
intended analytical window rather than merely the smallest and largest values
present in this small table.
Set only the options that communicate intent. For example,
.scale(zero=False) prevents an otherwise useful zero baseline from flattening
variation in measurements far from zero, while leaving the other scale choices
automatic. The GenomeSpy documentation lists the scale types and every scale
option in scale.
Axes explain positional scales¶
GenomeSpy creates axes for scaled x and y field encodings. Axis options
control the guide, not the data mapping:
gs.Y("value:Q").axis(grid=True, tickCount=4).title("Response")
Common axis adjustments include:
grid=Trueto support comparison across the plot;tickCount=4to request a readable tick density;tickMinStep=1when fractional steps would be misleading;orient="right"ororient="top"to move the guide;format=".1f"to format numeric labels.
tickCount is a request rather than an exact count. GenomeSpy may choose a
nearby set of round, readable values. A channel .title(...) supplies the
default axis title; .axis(title=...) can override it when necessary. See
ticks, labels, and grid lines
for the remaining axis options.
Zoomable positional scales¶
Set zoom=True on a positional scale to allow its visible domain to change
interactively:
gs.X("time:Q").scale(domain=[0.5, 3.5], zoom=True)
Zooming applies to quantitative, index, and locus scales. Nominal and ordinal
positional scales represent categorical data and do not support interactive
zooming, so leave zoom unset for them.
The customized chart above uses a zoomable horizontal scale. Zoom extent and domain transitions are described in zooming and panning.
Legends explain visual scales¶
Color, size, shape, and opacity encodings can produce legends. Use
.legend(...) to adjust presentation without changing the scale:
gs.Color("sample:N").legend(
title="Sample group",
orient="top",
direction="horizontal",
)
The title states what the categories mean, orient selects the side of the
plot, and direction controls how entries are arranged within the legend.
Continuous quantitative colors use a gradient legend; discrete categories use
symbols. Symbol and gradient legends, placement, and styling are documented in
legend.
Remove a guide with None¶
An axis or legend can be redundant when the same information is already clear from labels, surrounding tracks, or another shared guide:
minimal_chart = (
gs.Chart(measurements)
.mark_point(filled=True, size=90)
.encode(
x=gs.X("time:Q").axis(None),
y=gs.Y("value:Q").scale(zero=False).title("Response"),
color=gs.Color("sample:N").legend(None),
)
.properties(title="Guides can be removed independently")
)
.axis(None) and .legend(None) hide guides but retain their scales. The
points therefore keep their positions and colors. By contrast, .scale(None)
disables the scale itself and sends values directly to the visual channel; use
that only when the data already contains suitable visual values.
Removing a guide also removes its explanation. Keep at least one clear label or guide for every visual distinction the user must interpret. The GenomeSpy documentation covers the alternatives in disabling legends.