Skip to content
dysonsphere

Marks & transforms

dysonsphere’s composite marks return native Altair layers, so you compose them with + and keep Altair’s full API.

mark_strip() draws a categorical scatter with a median tick and optional mean error bars. Points are jittered by default.

import dysonsphere as ds
from vega_datasets import data
ds.theme()
cars = ds.ensure_polars(data.cars()).drop_nulls(["Miles_per_Gallon"])
origins = ["USA", "Europe", "Japan"]
chart = ds.mark_strip(cars, "Origin", "Miles_per_Gallon", origins, yTitle="Miles per gallon")

Set scatter="beeswarm" to pack points with collision avoidance instead of random jitter:

import polars as pl
import dysonsphere as ds
from vega_datasets import data
ds.theme()
cars = ds.ensure_polars(data.cars()).drop_nulls(["Acceleration"])
# Subsample to 50 cars per origin so the swarm fits its band.
cars = cars.filter(pl.int_range(pl.len()).shuffle(seed=7).over("Origin") < 50)
origins = ["USA", "Europe", "Japan"]
# scatter="beeswarm" packs points analytically instead of random jitter.
chart = ds.mark_strip(
cars, "Origin", "Acceleration", origins,
scatter="beeswarm", yTitle="0-60 mph time (s)",
)

Overlay mean error bars with errorbars=True; errorbarExtent chooses "sem" (default) or "sd":

import dysonsphere as ds
from vega_datasets import data
ds.theme()
cars = ds.ensure_polars(data.cars()).drop_nulls(["Miles_per_Gallon"])
origins = ["USA", "Europe", "Japan"]
# Mean +/- error bars overlaid on the points; errorbarExtent is "sem" (default)
# for the standard error of the mean, or "sd" for the standard deviation.
chart = ds.mark_strip(
cars, "Origin", "Miles_per_Gallon", origins,
errorbars=True, errorbarExtent="sd", yTitle="Miles per gallon",
)

mark_violin() pairs a kernel-density silhouette with an embedded boxplot. The silhouette and the box are styled independently:

import dysonsphere as ds
from vega_datasets import data
ds.theme()
cars = ds.ensure_polars(data.cars()).drop_nulls(["Horsepower"])
origins = ["USA", "Europe", "Japan"]
chart = ds.mark_violin(cars, "Origin", "Horsepower", origins, yTitle="Horsepower")
import dysonsphere as ds
from vega_datasets import data
ds.theme()
cars = ds.ensure_polars(data.cars()).drop_nulls(["Horsepower"])
origins = ["USA", "Europe", "Japan"]
# Style the silhouette and the embedded boxplot independently.
chart = ds.mark_violin(
cars, "Origin", "Horsepower", origins,
palette=ds.palette("dusk", 3), fillOpacity=0.85,
boxplotColor="black", medianColor="white",
yTitle="Horsepower",
)

Plain Altair marks inherit the theme too. mark_boxplot() gets grey boxes, a single-stroke median, and rounded whisker caps out of the box; boxplotOutliers=False hides outlier points.

import altair as alt
import dysonsphere as ds
from vega_datasets import data
# Plain Altair marks inherit the theme too: grey boxes, single-stroke median,
# rounded whisker caps. boxplotOutliers=False hides outlier points.
ds.theme()
cars = ds.ensure_polars(data.cars()).drop_nulls(["Miles_per_Gallon"])
chart = (
alt.Chart(cars)
.mark_boxplot()
.encode(
x=alt.X("Origin:N", title=None),
y=alt.Y("Miles_per_Gallon:Q", title="Miles per gallon"),
)
)

The jitter and beeswarm offsets are also available as standalone transforms that add a column to your DataFrame, which you then feed to Altair’s xOffset encoding - handy when you want the offset on your own custom mark.

import altair as alt
import dysonsphere as ds
from vega_datasets import data
ds.theme()
cars = ds.ensure_polars(data.cars()).drop_nulls(["Miles_per_Gallon"])
# add_jitter() adds a Gaussian x-offset column; pass it to Altair's xOffset.
cars = ds.add_jitter(cars)
chart = (
alt.Chart(cars)
.mark_circle()
.encode(
x=alt.X("Origin:N", title=None),
y=alt.Y("Miles_per_Gallon:Q", title="Miles per gallon"),
xOffset=alt.XOffset("jitter_x:Q"),
)
)
import altair as alt
import dysonsphere as ds
from vega_datasets import data
ds.theme()
cars = ds.ensure_polars(data.cars()).drop_nulls(["Miles_per_Gallon"])
# add_beeswarm() computes collision-avoiding x-offsets per group.
cars = ds.add_beeswarm(cars, "Miles_per_Gallon", groupBy=["Origin"])
chart = (
alt.Chart(cars)
.mark_circle()
.encode(
x=alt.X("Origin:N", title=None),
y=alt.Y("Miles_per_Gallon:Q", title="Miles per gallon"),
xOffset=alt.XOffset("beeswarm_x:Q"),
)
)