Brush-linked penguin summaries¶
Select penguins in a scatter plot and count the selection by species and sex.
Drag a rectangle over the scatter plot to select penguins. Selected points keep their species colors, and the bar charts count the selection by species and recorded sex. Before a selection is made, the summaries include all rows.
Data use and provenance
The Palmer Penguins table is loaded from the versioned Vega datasets collection. Source: Gorman KB, Williams TD, Fraser WR (2014), doi:10.1371/journal.pone.0090081. GenomeSpy filters and counts the selected rows in the browser.
Adapted from the official GenomeSpy example.
Code¶
"""Brush-linked penguin summaries.
Select penguins in a scatter plot and count the selection by species and sex.
"""
import genome_spy as gs
data = gs.Data(
url="https://cdn.jsdelivr.net/npm/vega-datasets@2.9.0/data/penguins.json"
)
species = ["Chinstrap", "Adelie", "Gentoo"]
colors = gs.Scale(domain=species, range=["#BF5CCA", "#FF6C02", "#0F7574"])
# Store the selection on the parent so both views can use it.
brush = gs.param("brush")
brush_update = gs.selection_interval("brush", encodings=["x", "y"], push="outer")
points = (
gs.Chart()
.mark_point(filled=False, size=40, opacity=0.7)
.encode(
x=gs.X("Beak Length (mm):Q").scale(zero=False, padding=0.1),
y=gs.Y("Beak Depth (mm):Q").scale(zero=False, padding=0.1),
color=gs.when(brush_update)
.then(gs.Color("Species:N").scale(colors).legend(orient="top-right", offset=0))
.otherwise(gs.value("lightgrey")),
)
.add_params(brush_update)
.properties(title=gs.Title(text="Palmer Penguins", style="overlay-title"))
)
# Keep every category on the axis even when its selected count is zero.
by_species = (
gs.Chart()
.transform_aggregate(groupby=["Species"])
.mark_rect()
.encode(
x=gs.X("Species:N").scale(domain=species, padding=0.3).axis(labelAngle=0),
color=gs.Color("Species:N").legend(None),
)
)
by_sex = (
gs.Chart()
.transform_filter((gs.datum.Sex == "MALE") | (gs.datum.Sex == "FEMALE"))
.transform_aggregate(groupby=["Sex"])
.mark_rect()
.encode(
x=gs.X("Sex:N")
.scale(domain=["MALE", "FEMALE"], padding=0.3)
.axis(labelAngle=0),
color=gs.value("gray"),
)
)
summaries = (
(by_species & by_sex)
# Match the brush coordinates to the columns used by the scatter plot.
.transform_filter(
brush_update, fields={"x": "Beak Length (mm)", "y": "Beak Depth (mm)"}
)
.encode(y=gs.Y("count:Q"))
.resolve_scale(x="independent", color="shared")
)
chart = (
(points | summaries)
.properties(data=data, padding=10, spacing=30)
.transform_collect()
.add_params(brush)
.resolve_scale(color="shared", y="independent")
.resolve_legend(color="independent")
)