Update docs
This commit is contained in:
parent
598ecbce40
commit
e9421b60b0
3 changed files with 109 additions and 32 deletions
|
|
@ -1,3 +1,5 @@
|
|||
"""Miscellaneous matplotlib plots with small, focused public APIs."""
|
||||
|
||||
from .variability import variability_plot as variability_plot
|
||||
from .wmap import wafermap as wafermap
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
"""Hierarchical categorical variability charts for matplotlib."""
|
||||
|
||||
from typing import Sequence, Callable, Any, TYPE_CHECKING
|
||||
import threading
|
||||
import textwrap
|
||||
|
|
@ -16,6 +18,12 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
def twrap(text: str, **kwargs) -> str:
|
||||
"""Wrap label text while preserving underscores in the displayed output.
|
||||
|
||||
Underscores are temporarily treated as wrap-friendly separators, then
|
||||
restored after wrapping. Keyword arguments are forwarded to
|
||||
`textwrap.fill`; `width` defaults to 15 characters.
|
||||
"""
|
||||
kwargs.setdefault('width', 15)
|
||||
intxt = text.replace('_', '-')
|
||||
wrapped = textwrap.fill(intxt, **kwargs)
|
||||
|
|
@ -35,27 +43,38 @@ def variability_plot(
|
|||
boxprops: dict[str, Any] | bool = True,
|
||||
meanprops: dict[str, Any] | bool = True,
|
||||
) -> tuple[Figure, Axes, list[Axes], list[Axes]]:
|
||||
"""
|
||||
Create a variability plot (categorical box & scatter plot)
|
||||
"""Create a hierarchical categorical variability plot.
|
||||
|
||||
The main axes show jittered scatter points, optional box plots, and optional
|
||||
per-category means for `data_col`. Additional axes beneath the main plot
|
||||
show the category labels for each grouping level, with coarser groups
|
||||
spanning multiple finer groups.
|
||||
|
||||
Args:
|
||||
data_table: Dataset to plot. Passed directly to `polars.DataFrame()`.
|
||||
data_col: Column to use for box/scatterplot value.
|
||||
groups: Columns to group by. Coarsest grouping should be first, and will appear
|
||||
furthest from the scatterplot, at the bottom of the figure.
|
||||
vert_groups: Labels for these column names will be rotated (text will run vertically).
|
||||
wrap_fn: Function called to wrap label text (i.e. insert newlines).
|
||||
data_table: Dataset to plot. Passed directly to
|
||||
`polars.DataFrame`.
|
||||
data_col: Numeric column to plot on the y-axis.
|
||||
groups: Columns to group by. The coarsest grouping should be first and
|
||||
appears furthest from the scatter plot, at the bottom of the figure.
|
||||
The finest grouping should be last and appears closest to the data.
|
||||
vert_groups: Group names whose labels should be rotated vertically.
|
||||
wrap_fn: Function called to wrap label text by inserting newlines.
|
||||
Default wraps at 15 characters, preferentially on underscores or whitespace.
|
||||
mainplot_ratios: Scale factors setting the size of the main axes, relative to the size
|
||||
of other axes. Default is (10, 10).
|
||||
ylim: Y-limits for the scatter/box plot. Points which fall outside the limits are drawn
|
||||
as red triangles at the edges.
|
||||
dotprops: Passed as kwargs to scatterplot.
|
||||
boxprops: Passed as kwargs to boxplot.
|
||||
meanprops: Passed as kwargs to lineplot of means.
|
||||
mainplot_ratios: Width and height scale factors for the main data axes,
|
||||
relative to the label/header axes.
|
||||
ylim: Optional y-limits for the scatter and box plot. Points outside
|
||||
the limits are drawn as red triangles at the corresponding edge.
|
||||
dotprops: `False` disables jittered scatter points. A dictionary is
|
||||
passed as keyword arguments to `matplotlib.axes.Axes.scatter`.
|
||||
boxprops: `False` disables box plots. A dictionary is passed as
|
||||
keyword arguments to `matplotlib.axes.Axes.boxplot`.
|
||||
meanprops: `False` disables the mean trace. A dictionary is passed as
|
||||
keyword arguments to `matplotlib.axes.Axes.plot`.
|
||||
|
||||
Returns:
|
||||
figure, data axes, label axes, header axes
|
||||
Tuple of `(figure, data_axes, label_axes, header_axes)`. The label and
|
||||
header axes are ordered from the finest displayed row nearest the data
|
||||
to the coarsest displayed row at the bottom.
|
||||
"""
|
||||
vert_groups = set(vert_groups)
|
||||
|
||||
|
|
@ -261,6 +280,12 @@ def variability_plot(
|
|||
label_stack_t = list[list[tuple[int, int, str]]]
|
||||
|
||||
def debounce(func: Callable, delay_s: float = 0.05) -> Callable:
|
||||
"""Return a wrapper that calls `func` after resize events settle.
|
||||
|
||||
Repeated calls within `delay_s` cancel the previous pending call. This
|
||||
keeps expensive text-size recalculation from running continuously while a
|
||||
matplotlib window is being resized.
|
||||
"""
|
||||
timer = None
|
||||
def debounced_func(*args, **kwargs) -> None:
|
||||
nonlocal timer
|
||||
|
|
@ -272,8 +297,18 @@ def debounce(func: Callable, delay_s: float = 0.05) -> Callable:
|
|||
|
||||
|
||||
def get_label_stack(df_groups: polars.DataFrame, groups: Sequence[str], wrap_fn: Callable) -> label_stack_t:
|
||||
"""
|
||||
For each level, get (xmin_inclusive, xmax_inclusive, wrapped_text_for_label) for all the labels
|
||||
"""Build label text and x-span metadata for each grouping level.
|
||||
|
||||
Args:
|
||||
df_groups: DataFrame containing one row per plotted category
|
||||
combination and an `x_pos` column with integer plot positions.
|
||||
groups: Grouping columns ordered from coarsest to finest.
|
||||
wrap_fn: Function used to convert each label value to display text.
|
||||
|
||||
Returns:
|
||||
Nested list in grouping order. Each row contains
|
||||
`(xmin_inclusive, xmax_inclusive, wrapped_label_text)` tuples for the
|
||||
contiguous spans at that grouping level.
|
||||
"""
|
||||
label_stack = []
|
||||
for ll, level in enumerate(groups):
|
||||
|
|
@ -295,8 +330,14 @@ def get_label_stack(df_groups: polars.DataFrame, groups: Sequence[str], wrap_fn:
|
|||
|
||||
|
||||
def get_text_sizes(label_stack: label_stack_t) -> list[NDArray[numpy.float64]]:
|
||||
"""
|
||||
Transform the label stack (see `get_label_stack` into a stack of (allocated x-span, unrotated x-size, unrotated y-size)
|
||||
"""Measure unrotated label dimensions for layout calculations.
|
||||
|
||||
Args:
|
||||
label_stack: Label span metadata returned by `get_label_stack`.
|
||||
|
||||
Returns:
|
||||
One array per label level. Each row contains
|
||||
`(allocated_x_span, unrotated_width_px, unrotated_height_px)`.
|
||||
"""
|
||||
fig, ax = pyplot.subplots()
|
||||
text_obj = ax.text(0, 0, 'placeholder')
|
||||
|
|
@ -315,10 +356,21 @@ def get_text_sizes(label_stack: label_stack_t) -> list[NDArray[numpy.float64]]:
|
|||
|
||||
|
||||
def get_label_y_ratios(groups: Sequence[str], vert_groups: set[str], size_lists: list[NDArray[numpy.float64]]) -> list[float]:
|
||||
"""
|
||||
For each level, figure out max(rotated_x_size / x_span) and max(rotated_y_size).
|
||||
Normalize so that the sum of y-values is equal to the number of levels.
|
||||
Output order is reversed so that the bottom labels (most general) come last.
|
||||
"""Calculate relative GridSpec heights for hierarchical label rows.
|
||||
|
||||
The calculation accounts for vertically rotated label groups, normalizes the
|
||||
label rows so their total height equals the number of grouping levels, and
|
||||
reverses the output for the figure layout so the coarsest labels appear at
|
||||
the bottom.
|
||||
|
||||
Args:
|
||||
groups: Grouping columns ordered from coarsest to finest.
|
||||
vert_groups: Set of group names whose labels are rotated vertically.
|
||||
size_lists: Text measurement arrays returned by
|
||||
`get_text_sizes`.
|
||||
|
||||
Returns:
|
||||
Height ratios ordered for the label rows in the matplotlib GridSpec.
|
||||
"""
|
||||
grouping_rotated = numpy.array([grouping in vert_groups for grouping in groups], dtype=bool)
|
||||
level_dims = []
|
||||
|
|
@ -335,8 +387,10 @@ def get_label_y_ratios(groups: Sequence[str], vert_groups: set[str], size_lists:
|
|||
|
||||
|
||||
def _mk_data(filename: str) -> None:
|
||||
"""
|
||||
Make some dummy data and write it to a csv file
|
||||
"""Create synthetic variability-chart data for the module demo.
|
||||
|
||||
Args:
|
||||
filename: Path where the generated CSV data should be written.
|
||||
"""
|
||||
rows = []
|
||||
rng = numpy.random.default_rng(seed=0)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
"""Wafer-style tiled heat maps for matplotlib."""
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
from matplotlib import pyplot
|
||||
|
|
@ -17,12 +19,30 @@ def wafermap(
|
|||
xy2sn: Callable[[int, int], str] | None = None,
|
||||
aspect_ratio: float = 1,
|
||||
) -> tuple[Figure, Axes]:
|
||||
"""
|
||||
Wafer map plot
|
||||
"""Plot sparse `(x, y, z)` samples as a tiled wafer-style heat map.
|
||||
|
||||
This is effectively a pcolor plot which does a clearer job showing that data may come
|
||||
from anywhere within a tile, and correctly shows missing data.
|
||||
The plot bins each sample into integer-spaced x/y tiles, draws one color
|
||||
per populated tile, and leaves unpopulated tiles transparent. This is useful
|
||||
for spatial measurements where each value belongs to a die, cell, or other
|
||||
tile rather than to an exact point location.
|
||||
|
||||
Args:
|
||||
xyz: Array-like object with three columns: x coordinate, y coordinate,
|
||||
and z value to color. Coordinates are binned with unit spacing based
|
||||
on their floored values.
|
||||
pcoloropts: Optional keyword arguments passed to
|
||||
`matplotlib.axes.Axes.pcolor`. Defaults are added for
|
||||
`shading`, `edgecolors`, and `cmap` when they are not already
|
||||
present.
|
||||
zlabel: Label for the colorbar.
|
||||
xy2sn: Optional callback that receives the binned x/y indices and
|
||||
returns text to draw over populated tiles.
|
||||
aspect_ratio: Scale factor applied to x coordinates before plotting.
|
||||
Use values below 1 for horizontally compressed layouts and values
|
||||
above 1 for horizontally stretched layouts.
|
||||
|
||||
Returns:
|
||||
The created matplotlib `(figure, axes)` pair.
|
||||
"""
|
||||
fig, ax = pyplot.subplots()
|
||||
|
||||
|
|
@ -62,6 +82,7 @@ def wafermap(
|
|||
|
||||
|
||||
def example() -> None:
|
||||
"""Run an interactive wafer map example with synthetic radial data."""
|
||||
rng = numpy.random.default_rng(0)
|
||||
ar = 0.5
|
||||
ny = 10
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue