Skip to content

partition ​

Divides the space into the cells of a binned key, one group per cell, and gives each group its cell. Each cell sits at its true place on one continuous scale, so a cell's width follows its width in data: a 29-day February is narrower than a 31-day March. An empty cell keeps its place. With one key per axis, it divides both axes into rectangles. With two fields binned together, it divides the plane into hexagons or Voronoi cells.

python
from gofish import Calendar, Unit, chart, field, partition, rect

chart(daily, schema={"date": Unit.time()}, axes=True).flow(
    partition(by=field("date").bin([Calendar.year, Calendar.month]), dir="x")
).mark(rect(h=field("value").sum(), inset=1)).render(w=560, h=200)

Signature ​

python
partition(*, by, dir=None, **options) -> Operator

partition has only the operator form, used inside .flow(). Its children are the groups of its key, so there is no list of children to pass it.

Parameters ​

Divide the space into the cells of a binned key, and give each group its cell. Each cell sits at its true place on one continuous scale, so a cell's width follows its width in data, and an empty cell keeps its place. A mark with no size of its own fills its cell, and a mark with a size of its own is centered in it.

OptionTypeDefaultDescription
byFieldAccessor | StructAccessorRequired. A key that has a region: a binned field whose cells lie on the line, whose cells divide the axis dir: field(x).bin(p) with p Bin.step(size), Bin.thresholds(countOrEdges), Bin.quantiles(countOrProbabilities), or a Calendar level that is a part of nothing (Calendar.year, Calendar.week); a product that starts with one, field(x).bin([Calendar.year, Calendar.month]); or a chain that starts with one, field(x).bin(Calendar.year).bin(Calendar.month). A part of a cycle (field(x).bin(Calendar.month), the month of the year) has no one region on the time line and is an error. Or one binned field per axis, { x: field(a).bin(p), y: field(b).bin(q) }, whose cells divide both axes into rectangles; this is the partition on x, then the partition on y, and it is written as those two partitions. Or two fields binned together, struct({ x: a, y: b }).bin(Bin.hex({ radius })) or .bin(Bin.voronoi({ seeds })), whose cells are polygons over both axes. A plain field, or a struct with no bin, has no region and is an error.
dirstrAxis to divide: x, y, or an axis name the enclosing coordinate space declares (polar theta/r). Required with a single key, and not allowed with a key per axis.
alignmentstr"baseline"Alignment of the children on the other axis ("start" | "middle" | "end" | "baseline"). Applies only where nothing gives the children a cell on that axis: inside a cell of another partition, each child is placed in that cell. Not allowed with a key per axis.
axesbool | dict

The key must have a region ​

by must be a key whose values have a region. That is a binned field, field(x).bin(...) whose values are cells of the line, each an interval [start, end), or a binned struct, struct(x=..., y=...).bin(...), whose values are cells of the plane (see Cells of the plane).

python
partition(by=field("date").bin([Calendar.year, Calendar.month]), dir="x")  # true month widths
partition(by=field("date").bin(Calendar.year).bin(Calendar.month), dir="x")  # the same cells
partition(by=field("rating").bin(Bin.step(0.5)), dir="x")  # a histogram

A level binned alone that is a part of a larger level is a part of a cycle, with no one region on the time line: field("date").bin(Calendar.month) is the month of the year, every June in one cell. So partition(by=field("date").bin(Calendar.month), dir="x") is an error naming the product and the chain when the chart renders.

A plain field has no region, so partition(by="date", dir="x") raises a ValueError, and so does a struct with no bin, partition(by=struct(x="lon", y="lat")). To give each value an equal slot instead, use spread with the same key.

Filling the cell ​

A mark with no size along dir fills its cell. rect is the mark for this. Its inset option draws it in from the edges of the cell by a number of pixels, so neighboring bars are drawn apart. A mark with a size of its own along dir, such as a circle or a text, keeps its size and is centered in the cell.

An operator after partition divides the cell it is given. A stack fills the cell's width, and a spread fits its groups inside the cell, so February's group is narrower than March's.

python
chart(daily, schema={"date": Unit.time()}, axes=True).flow(
    partition(by=field("date").bin([Calendar.year, Calendar.month]), dir="x"),
    spread(by="region", dir="x"),
).mark(rect(h=field("value").sum(), fill="region"))

Both axes ​

With by a dict keyed by axis, partition divides both axes. Each key must have a region, and dir and alignment are not allowed.

python
partition(by={"x": field("a").bin(Bin.step(1)), "y": field("b").bin(Bin.step(0.5))})

It is the same as the 1D partition on x, then the 1D partition on y:

python
partition(by=field("a").bin(Bin.step(1)), dir="x"),
partition(by=field("b").bin(Bin.step(0.5)), dir="y")

A partition inside a cell of another partition gives each of its groups that cell on the other axis, so nesting the two in either order places the groups the same way. There alignment has nothing to align.

Each group gets a rectangle: its x cell by its y cell. A region fills it, so a 2D histogram is a region whose fill is a count.

python
from gofish import Bin, Color, chart, field, partition, region

chart(movies, color=Color.gradient("blues"), axes=True).flow(
    partition(
        by={
            "x": field("IMDB Rating").bin(Bin.step(0.5)),
            "y": field("Rotten Tomatoes Rating").bin(Bin.step(5)),
        }
    )
).mark(region(fill=field("IMDB Rating").count())).render(w=480, h=320)

A mark with a size of its own is centered in its rectangle.

python
from gofish import Bin, chart, field, partition, text

# measured: the penguins that have both a flipper length and a body mass
chart(measured, axes=True).flow(
    partition(
        by={
            "x": field("Flipper Length (mm)").bin(Bin.step(10)),
            "y": field("Body Mass (g)").bin(Bin.step(500)),
        }
    )
).mark(text(text=field("Body Mass (g)").count())).render(w=420, h=320)

An empty cell is a group with no rows, so a count over it is 0, and the mark draws that 0.

Cells of the plane ​

With by a binned struct, partition divides both axes at once into the cells of a Bin call: Bin.hex(radius=...) for a grid of hexagons, or Bin.voronoi(seeds=...) for one cell per seed. dir and alignment are not allowed.

python
partition(by=struct(x="lon", y="lat").bin(Bin.hex(radius=0.5)))

Each group gets its cell's outline. A region draws it, so a hexbin is a region whose fill is a count. As in 1D, the cells are those of the two columns' domain in the chart's data, and an empty hexagon is a group with no rows, so it is drawn with a count of 0.

python
from gofish import Bin, Color, chart, field, partition, region, struct

# airports: the airports of the contiguous United States
chart(airports, color=Color.gradient("blues"), axes=True).flow(
    partition(by=struct(x="longitude", y="latitude").bin(Bin.hex(radius=1)))
).mark(region(fill=field("longitude").count(), stroke="white")).render(w=600, h=360)

A mark with a size of its own is centered in its cell's box, which for a hexagon is its center. The example writes each count with .label() at Place.interior(), inside the hexagon, so its color contrasts with the hexagon's fill.

With Bin.voronoi, each row goes to its nearest seed.

python
from gofish import Bin, Color, chart, circle, field, layer, partition, region, scatter, struct

# hubs: twenty hub airports, from the same table
layer([
    chart(airports, color=Color.gradient("reds")).flow(
        partition(by=struct(x="longitude", y="latitude").bin(Bin.voronoi(seeds=hubs)))
    ).mark(region(fill=field("iata").count(), stroke="white")),
    chart(hubs).flow(scatter(x="longitude", y="latitude")).mark(circle(r=3, fill="black")),
]).render(w=600, h=360, axes=True)

Axis ​

The axis along dir is continuous, and with a key per axis or a binned struct both axes are. A binned time column gives a time axis whose inner row is the cells' own partition, with each label centered under its cell, and an outer row of the parent level (years under months). A binned number column gives a numeric axis. See Underlying Space for how the layout and the axis read the cells.