polar
Transforms Cartesian coordinates into a polar coordinate system. The x-axis maps to angle (theta) and the y-axis maps to radius.
from gofish import chart, stack, rect, polar
chart(seafood, coord=polar()) \
.flow(stack(by="species", dir="x")) \
.mark(rect(w="count", fill="species")) \
.render(w=400, h=300)Signature
polar(
inner_radius: float | None = None, # donut hole, fraction [0,1) of outer radius
central_angle: float | None = None, # total sweep in radians (default 2π)
start_angle: float | None = None, # angle (radians) of θ=0 (default π/2)
direction: int | None = None, # +1 CCW, -1 CW (default -1)
center: tuple[float, float] | None = None, # screen-space center offset
) -> CoordParameters
All optional; the defaults reproduce a centered, full-circle disc starting at 12 o'clock and going clockwise.
Maps (θ, r) → screen. θ is axis 0 (x, or the declared name theta), r is axis 1 (y, or r); dims and dir inside it may use theta/r.
| Option | Type | Default | Description |
|---|---|---|---|
inner_radius | float | 0 | Donut hole as a fraction [0,1) of the outer radius. |
central_angle | float | 2π | Total angular sweep in radians. |
start_angle | float | π/2 | Angle (radians) of θ=0. |
direction | float | -1 | +1 counter-clockwise, -1 clockwise (numeric ±1). |
center | tuple | [0, 0] | Screen-space center offset. |
Axis names
In every coordinate space, x, y, w, and h refer to the first and second axis. Under polar, the first axis is the angle and the second is the radius, so w is an angular extent and h is a radial one.
Polar also declares its own names for the two axes: theta for the angle and r for the radius. You can use them in two places:
- in a mark's
dimskeyword, a dict keyed by axis name. A plain value is a position, likex. A dict names the parts of the axis you want to set:"min","center","max","size", and"embedded". - in an operator's
dir, as instack(dir="theta").
chart(data, coord=polar()) \
.flow(spread(by="category", dir="theta")) \
.mark(rect(dims={"theta": {"size": 0.4}, "r": {"size": "value"}},
em_x=True, em_y=True))This is the same chart as spread(by="category", dir="x") with rect(w=0.4, h="value", em_x=True, em_y=True). The names only work inside a coordinate space that declares them; anywhere else, "theta" raises an error that lists the names you can use there. A coordinate space nested inside polar that declares no names, such as wavy(), hides them too.
Each part of an axis can be set once. w=0.4 together with dims={"theta": {"size": 0.4}} is an error, because both set the angular size. x=0 together with dims={"theta": {"size": 0.4}} is fine.
A circle's own r keyword is still its radius. The polar r axis only appears as a key inside dims, so scatter(by="id", dims={"theta": "bearing", "r": "distance"}) with circle(r=4) places each dot by bearing and distance and draws it with radius 4.
Usage
Pass the coordinate transform to chart via the coord keyword:
chart(data, coord=polar()) \
.flow(...) \
.mark(...) \
.render(w=400, h=300)Coordinate Mapping
| Cartesian | Polar |
|---|---|
| x | angle (theta), 0 to 2π |
| y | radius from center |
Examples
# Basic polar chart
chart(data, coord=polar()) \
.flow(stack(by="category", dir="x")) \
.mark(rect(w="value"))
# Polar with spread for radial segments
chart(data, coord=polar()) \
.flow(spread(by="month", dir="x")) \
.mark(rect(w=1, h="value"))
# Donut: a hollow center (inner radius = 50% of the outer radius)
chart(data, coord=polar(inner_radius=0.5)) \
.flow(stack(by="category", dir="x")) \
.mark(rect(w="value"))
# Partial fan: a 270° sweep instead of the full circle
import math
chart(data, coord=polar(central_angle=3 * math.pi / 2)) \
.flow(spread(by="month", dir="x")) \
.mark(rect(w=1, h="value"))See Also
- clock — Similar to polar but with 0° at 12 o'clock
