For AI agents: the complete XY documentation index is at llms.txt. Markdown versions are available by appending .md or sending Accept: text/markdown.
Chart Gallery

/

Radar Charts in Python

Radar Charts in Python

A radar chart (also called a spider chart, spider plot, or radar plot) compares several measurements across the same categorical dimensions. XY places the categories at evenly spaced angles, draws one spoke for each category, and closes every series across the circular seam.

Use radar charts for compact profile comparisons such as product capabilities, model scores, survey dimensions, and operational health. When the angular position is itself a measured quantity rather than a category, start with the polar chart overview instead.

Jump to filled areas or outlines, the data contract, or scale configuration.

Create a Radar Chart

Pass the category labels first, followed by one area or line mark per series. Each mark supplies exactly one value per category:

The helper assigns the category labels to the theta axis and closes the first and last category with a straight chord. You do not need to repeat the first value at the end of a series.

Choose Filled Areas or Outlines

Use xy.area(values) for a filled profile or xy.line(values) for an outline. When an existing composition already uses area marks, set fill=False on the chart to turn every area into an outline without rewriting the series:

When fill=False rebuilds an area as a line, the outline inherits line_color, line_width, line_opacity, curve, and dash settings. If line_color is omitted, it falls back to the area's color.

Filled profiles work best with some transparency so overlapping series remain readable. Give each series a name and add xy.legend() when the chart contains more than one profile.

Follow the Radar Data Contract

Radar charts have a deliberately narrow input contract:

  • Supply at least three categories.
  • Every series must contain exactly one value per category.
  • Use only area and line marks.
  • Put values directly on the mark. Column-name strings are not resolved because the category list supplies the angular positions.

XY raises ValueError for a category/value mismatch instead of silently dropping a dimension or drawing a malformed polygon.

Configure the Scale

Add xy.r_axis(domain=(minimum, maximum)) when several charts need a common comparison scale. The default radial range begins at zero and extends to the largest value. Use tick_values= for exact rings and label= when the score has a named unit.

The category list owns the theta labels. An authored xy.theta_axis() can customize their style; grid_shape="linear" joins the spokes into polygonal rings, as in the live example. General polar_chart() compositions also accept category strings directly. radar_chart() additionally validates that each series matches the shared category count and closes profiles for you.

Compare Three Profiles on a Fixed Scale

Overlay one mark per candidate, pin the radial domain and its rings with xy.r_axis(), and rotate the first category to the top with xy.theta_axis() so repeated benchmarks stay directly comparable:

Interaction and Export

Radar charts use the shared polar interaction model: hover, radial-only zoom, and reset are available; rotation, box zoom, selection, brushing, and crosshairs are disabled. Browser, SVG, PDF, and native raster exports share the same radar geometry.

See Polar chart interaction and limits for the complete coordinate-system contract.

API Reference

xy.radar_chart

A radar (spider) chart: one closed polygon per series.

Props

PropTypeDescription
categoriesSequence[str]

Spoke labels, one per value.

*childrenComponent

`area` (filled) or `line` (outline) marks carrying values, plus any axis/legend children.

fillbool

When False, filled `area` children are rebuilt as `line` outlines, so one call switches a whole chart between filled and outline radar without editing every mark.

titleOptional[str]

Title shown above the plot.

widthint | str

Chart width in pixels or a CSS size such as ``"100%"``.

heightint | str

Chart height in pixels or a CSS size such as ``"100%"``.

paddingUnion[float, Sequence[float], None]

Plot margins, as one value or a sequence of side values. Use zero for an edge-to-edge sparkline.

dataTableLike

Chart-level data used by marks that omit their own ``data``.

class_nameOptional[str]

CSS class applied to the chart container.

class_namesOptional[dict[str, str]]

CSS classes keyed by stable chart DOM slot.

styleOptional[dict[str, StyleValue]]

Inline style overrides for the chart container.

stylesOptional[dict[str, dict[str, StyleValue]]]

Inline style mappings keyed by stable chart DOM slot.

on_hoverOptional[Callable[[dict], None]]

Callback receiving hover event payloads.

on_clickOptional[Callable[[dict], None]]

Callback receiving picked-mark click payloads.

on_brushOptional[Callable[[dict], None]]

Callback receiving brush event payloads.

on_selectOptional[Callable[[Selection], None]]

Callback receiving data-space selections.

on_view_changeOptional[Callable[[dict], None]]

Callback receiving viewport change payloads.

hoverOptional[bool]

Whether pointer movement emits hover events.

clickOptional[bool]

Whether picked marks emit click events.

selectOptional[bool]

Whether shift-drag box selection is enabled.

brushOptional[bool]

Whether brush selection is enabled.

crosshairOptional[bool]

Whether plot-aligned hover guides are shown.

navigationOptional[bool]

Whether browser pan and zoom navigation is enabled.

panOptional[bool]

Whether plain-drag panning is enabled.

pan_axesOptional[tuple[str, ...]]

Declared axis IDs translated by pan gestures.

zoomOptional[bool]

Whether viewport zoom is enabled.

default_drag_actionOptional[DefaultDragAction]

Initial action performed by a plain plot drag.

zoom_axesOptional[tuple[str, ...]]

Declared axis IDs changed by zoom gestures and controls.

zoom_limitsOptional[ZoomLimits]

Minimum and maximum magnification globally or by axis.

wheel_zoomOptional[bool]

Whether wheel and trackpad zoom is available.

box_zoomOptional[bool]

Whether box zoom is available as a drag action.

zoom_buttonsOptional[bool]

Whether modebar Zoom In/Out commands are available.

double_click_resetOptional[bool]

Whether double-click restores ``reset_axes``.

reset_axesOptional[tuple[str, ...]]

Declared axis IDs restored by reset.

link_groupOptional[str]

Identifier used to synchronize charts in the browser.

link_axesOptional[tuple[str, ...]]

Axes synchronized within the link group.

coordsstr

Coordinate system, ``"cartesian"`` (default) or ``"polar"``. Under ``"polar"`` each mark's first channel is the angle and its second is the radius. Prefer ``xy.polar_chart(...)``, which sets this for you.

FAQ

What is the difference between a radar chart and a polar line chart?

A radar chart assigns evenly spaced angles to category labels and closes the profile automatically. A polar line chart expects explicit numeric angles and radii.

How do I build a spider chart in Python?

Call xy.radar_chart(categories, ...) with one xy.area() or xy.line() mark per series and render it:

The category list drives the spokes, so a spider plot needs no manual angle math and no repeated closing value.

How do I draw an unfilled spider chart?

Use xy.line(values) for each series, or pass fill=False to xy.radar_chart() when the children are area marks.

Why does my radar chart raise a value-count error?

Each series in a radar graph must have the same number of values as the category list. Add or remove values so every category has one measurement.

Built with Reflex