diff --git a/docs/charts/box-plot.md b/docs/charts/box-plot.md index aba8d46b..8b51e0d0 100644 --- a/docs/charts/box-plot.md +++ b/docs/charts/box-plot.md @@ -1,6 +1,6 @@ --- title: Box Plot in Python -description: Box plot Python charts with xy — compare distributions across groups with quartiles, whiskers, and outliers in a fast, interactive boxplot that pans and zooms. +description: Boxplot Python charts with xy. Compare distributions across groups with quartiles, whiskers, and outliers in a fast, interactive box and whisker plot. components: - xy.box_chart --- @@ -204,8 +204,9 @@ Pass column names with `data=` instead of arrays when your data is a table. ### How do I make a box plot in Python? Pass a list of arrays to `xy.box(...)`, one per group, inside -`xy.box_chart(...)` and render it. Quartiles, whiskers, and axes are computed -for the whole box chart automatically. +`xy.box_chart(...)` and render it. That is all a Python box and whisker plot +needs: quartiles, whiskers, and axes are computed for the whole box chart +automatically. ### How do I show outliers on a box plot? diff --git a/docs/charts/contour-plot.md b/docs/charts/contour-plot.md index 24814964..883efd39 100644 --- a/docs/charts/contour-plot.md +++ b/docs/charts/contour-plot.md @@ -186,3 +186,30 @@ explicit list of values to place contours at exact levels. Set `dash_negative=True` so contours with negative values are dashed, making the sign of a signed field easy to read. + +### I use a MATLAB contour plot or matplotlib. What is the equivalent? + +Matplotlib users can often start by changing the import because `xy.pyplot` +accepts the familiar `contour()` and `contourf()` calls, although unsupported +options may still require changes. For MATLAB, translate the workflow to the +equivalent Python/NumPy syntax shown here: + +~~~python +import numpy as np +import xy.pyplot as plt + +x = np.linspace(-3, 3, 100) +y = np.linspace(-2.5, 2.5, 90) +xx, yy = np.meshgrid(x, y) +z = np.exp(-(xx**2 + yy**2)) + +fig, ax = plt.subplots() +ax.contourf(x, y, z, levels=12) +ax.set_xlabel("x") +ax.set_ylabel("y") +plt.show() +~~~ + +The result is a fully interactive contour chart. See the +[matplotlib compatibility guide](/docs/xy/integrations/matplotlib/) for the +covered surface. diff --git a/docs/charts/pie-chart.md b/docs/charts/pie-chart.md index cb20014b..bfe02ba0 100644 --- a/docs/charts/pie-chart.md +++ b/docs/charts/pie-chart.md @@ -1,19 +1,24 @@ --- title: Pie and Donut Charts in Python -description: Build polished pie, donut, progress-ring, and gauge blocks in Python with xy and Reflex. +description: Create pie charts and donut charts in Python with xy. Build pie plots, progress rings, and gauge blocks with values, percentages, and custom colors. components: - xy.pie_chart --- # Pie and Donut Charts in Python -A pie chart maps each share to an angular span. A donut uses the same sectors +A **pie chart** (also called a pie plot or pie graph) maps each share to an +angular span. A donut uses the same sectors with a positive inner radius, leaving room for a total, status, or supporting label. In XY, both are compositions of unequal-width bars inside `polar_bar_chart()`. For the standard composition, use `xy.pie_chart(labels, values, hole=...)`; the examples below use the lower-level bars directly to demonstrate custom sector geometry and dashboard layouts. +Jump to [a basic pie chart](#basic-pie-chart), +[progress rings](#progress-rings), or +[build your own pie block](#build-your-own-pie-block). + The first example keeps the chart intentionally small. The examples after it combine XY's exportable sector geometry with ordinary Reflex layout for center labels, legends, captions, and summary rows. That separation keeps the data @@ -695,9 +700,9 @@ corner-radius, clipping, and partial-sector details. ### Does XY have a dedicated pie mark? -No. Pie and donut geometry is composed from `xy.bar()` marks inside -`xy.polar_bar_chart()`. This keeps sector styling and export behavior on the -same renderer as radial bars. +No. Every pie plot and donut plot on this page is composed from `xy.bar()` +marks inside `xy.polar_bar_chart()`. This keeps sector styling and export +behavior on the same renderer as radial bars. ### How do I add space between slices? diff --git a/docs/charts/polar-chart.md b/docs/charts/polar-chart.md index cc738057..2530f506 100644 --- a/docs/charts/polar-chart.md +++ b/docs/charts/polar-chart.md @@ -1,6 +1,6 @@ --- title: Polar Charts in Python -description: Create polar lines, fields, sectors, and uncertainty charts in Python with xy. Configure partial sectors, categorical angles, holes, log radii, and pyplot polar projections. +description: Create polar charts and polar plots in Python with xy. Configure sectors, categorical angles, holes, log radii, and pyplot polar projections. components: - xy.polar_chart - xy.theta_axis @@ -9,11 +9,16 @@ components: # Polar Charts in Python -A **polar chart** places each observation by an angle (theta, or θ) and a +A **polar chart** (also called a polar plot or polar graph) places each +observation by an angle (theta, or θ) and a distance from the center (radius, or r). It is a natural fit for cyclic measurements, directional observations, antenna patterns, radar comparisons, and wind distributions. +Jump to [a polar line chart](#create-a-polar-line-chart), +[the angular axis](#configure-the-angular-axis), or +[supported marks and limits](#supported-marks-and-current-limits). + XY uses the same composition model as its Cartesian charts. Put `line`, `scatter`, `area`, `bar`, `column`, `heatmap`, `contour`, or `errorbar` marks inside `polar_chart()`. Focused helpers build diff --git a/docs/charts/radar-chart.md b/docs/charts/radar-chart.md index 71bbf17f..ebaafb3c 100644 --- a/docs/charts/radar-chart.md +++ b/docs/charts/radar-chart.md @@ -7,16 +7,20 @@ components: # Radar Charts in Python -A **radar chart** (or spider chart) 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. +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](/docs/xy/charts/polar-chart/) instead. +Jump to [filled areas or outlines](#choose-filled-areas-or-outlines), +[the data contract](#follow-the-radar-data-contract), or +[scale configuration](#configure-the-scale). + ## Create a Radar Chart Pass the category labels first, followed by one `area` or `line` mark per @@ -133,6 +137,52 @@ 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: + +~~~python demo exec +import reflex_xy +import xy + +benchmark_axes = ["Latency", "Throughput", "Accuracy", "Cost", "Memory", "Setup"] + +radar_scaled = xy.radar_chart( + benchmark_axes, + xy.area( + [78, 92, 88, 61, 74, 95], + name="Engine A", + color="#6e56cf", + line_color="#6e56cf", + opacity=0.25, + ), + xy.area( + [64, 55, 90, 72, 58, 80], + name="Engine B", + color="#2563eb", + line_color="#2563eb", + opacity=0.22, + ), + xy.area( + [52, 48, 76, 88, 83, 66], + name="Engine C", + color="#f97316", + line_color="#f97316", + opacity=0.22, + ), + xy.theta_axis(grid_shape="linear", zero="N", direction="clockwise"), + xy.r_axis(domain=(0.0, 100.0), tick_values=[25, 50, 75, 100], label="score"), + xy.legend(loc="right", title="Engine"), + title="Benchmark profile (0-100)", +) + + +def radar_scaled_demo(): + return reflex_xy.chart(radar_scaled, height="440px") +~~~ + ## Interaction and Export Radar charts use the shared polar interaction model: hover, radial-only zoom, @@ -160,6 +210,22 @@ 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: + +~~~python +spider = xy.radar_chart( + ["Speed", "Range", "Payload", "Efficiency"], + xy.area([0.9, 0.7, 0.6, 0.8], name="Model A", color="#6e56cf"), + xy.legend(), +) +~~~ + +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 @@ -167,5 +233,5 @@ Use `xy.line(values)` for each series, or pass `fill=False` to ### Why does my radar chart raise a value-count error? -Each series must have the same number of values as the category list. Add or -remove values so every category has one measurement. +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. diff --git a/docs/charts/radial-bar-chart.md b/docs/charts/radial-bar-chart.md index 9b99f0b3..80b3f847 100644 --- a/docs/charts/radial-bar-chart.md +++ b/docs/charts/radial-bar-chart.md @@ -1,16 +1,21 @@ --- title: Radial Bar Charts in Python -description: Build polished radial bar, progress-ring, and semicircular capacity blocks in Python with xy and Reflex. +description: Create radial bar charts in Python with xy. Build radial bar plots, progress rings, gauge charts, and semicircular capacity blocks. components: - xy.polar_bar_chart --- # Radial Bar Charts in Python -A **radial bar chart** encodes each value as an annular sector. The bar's first +A **radial bar chart** (also called a radial bar plot or radial bar graph) +encodes each value as an annular sector. The bar's first channel is its center angle, its second is its radial height, `width` controls the angular span, and `base` controls the inner radius. +Jump to [a basic radial bar chart](#basic-radial-bar-chart), +[inner radius and sector geometry](#inner-radius-and-sector-geometry), or +[partial gauges](#partial-gauges). + The first example keeps that geometry intentionally small. The blocks after it combine XY's exportable sectors with ordinary Reflex layout for center values, legends, rails, and summary statistics. This keeps the visualization reusable @@ -622,8 +627,8 @@ used by the progress-ring blocks above. ## Partial Gauges -Use `sector=(start, end)` for a gauge whose visible arc should expand to fill -the plot box. Rounded sectors, background-colored strokes, gradients, and +Use `sector=(start, end)` for a gauge chart whose visible arc should expand to +fill the plot box. Rounded sectors, background-colored strokes, gradients, and explicit padding work the same way on a partial layout: ~~~python diff --git a/docs/charts/wind-rose.md b/docs/charts/wind-rose.md index 8adcc174..67202880 100644 --- a/docs/charts/wind-rose.md +++ b/docs/charts/wind-rose.md @@ -7,15 +7,20 @@ components: # Wind Rose Charts in Python -A **wind rose** summarizes how often observations arrive from each compass -direction and how those observations are distributed across speed bands. XY -bins the raw direction/speed pairs in Python and renders the result as stacked -polar bars. +A **wind rose** (also called a wind rose chart, wind rose plot, or wind rose +diagram) summarizes +how often observations arrive from each compass direction and how those +observations are distributed across speed bands. XY bins the raw direction/speed +pairs in Python and renders the result as stacked polar bars. Use `wind_rose()` when you have one bearing and one magnitude per observation. If the directional counts are already aggregated, use a [radial bar chart](/docs/xy/charts/radial-bar-chart/) instead. +Jump to [directional sectors](#choose-directional-sectors), +[speed bands](#configure-speed-bands), or +[the input contract](#follow-the-input-contract). + ## Create a Wind Rose Directions are compass bearings in degrees: 0° is north and values increase @@ -85,6 +90,72 @@ values. When `speed_bins` is omitted, it derives up to four readable bands from the speed quartiles and rounds the top edge upward so every finite observation is covered. +## Set Sector Count and Band Edges Together + +Lower the sector count when the sample is small or the source records only the +eight principal bearings, and pass matching band edges so each petal stays thick +enough to read: + +~~~python demo exec +import numpy as np +import reflex_xy +import xy + +coarse_rng = np.random.default_rng(7) +coarse_directions = np.mod(coarse_rng.normal(270.0, 45.0, 500), 360.0) +coarse_speeds = np.clip(coarse_rng.gamma(shape=2.0, scale=3.0, size=500), 0.3, 17.0) + +coarse_rose = xy.wind_rose( + coarse_directions, + coarse_speeds, + sectors=8, + speed_bins=[3, 6, 9, 12, 18], + title="Eight-sector wind rose", +) + + +def wind_rose_sectors_demo(): + return reflex_xy.chart(coarse_rose, height="440px") +~~~ + +## Plot a Full Year of Observations + +With thousands of records a high sector count resolves the prevailing wind, and +narrow speed bands separate calm air from gales — add `xy.legend()` to place the +band labels where you want them: + +~~~python demo exec +import numpy as np +import reflex_xy +import xy + +met_rng = np.random.default_rng(2024) +met_directions = np.mod( + np.concatenate( + [ + met_rng.normal(240.0, 22.0, 5200), + met_rng.normal(60.0, 40.0, 1800), + met_rng.uniform(0.0, 360.0, 1000), + ] + ), + 360.0, +) +met_speeds = np.clip(met_rng.weibull(2.0, 8000) * 7.5, 0.2, 28.0) + +annual_rose = xy.wind_rose( + met_directions, + met_speeds, + xy.legend(loc="right", title="speed (m/s)"), + sectors=32, + speed_bins=[2, 4, 6, 8, 11, 14, 18, 25], + title="Wind rose, 8,000 hourly observations", +) + + +def wind_rose_annual_demo(): + return reflex_xy.chart(annual_rose, height="440px") +~~~ + ## Follow the Input Contract - `directions` and `speeds` must have the same length. @@ -144,5 +215,6 @@ edge upward to include the maximum observation. ### Why are some observations missing? -Non-finite direction/speed pairs are dropped. With authored `speed_bins`, make -sure the final upper edge covers the fastest finite observation. +Non-finite direction/speed pairs are dropped before the wind rose graph is +binned. With authored `speed_bins`, make sure the final upper edge covers the +fastest finite observation.