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 .variability import variability_plot as variability_plot
|
||||||
from .wmap import wafermap as wafermap
|
from .wmap import wafermap as wafermap
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
"""Hierarchical categorical variability charts for matplotlib."""
|
||||||
|
|
||||||
from typing import Sequence, Callable, Any, TYPE_CHECKING
|
from typing import Sequence, Callable, Any, TYPE_CHECKING
|
||||||
import threading
|
import threading
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
@ -16,6 +18,12 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
def twrap(text: str, **kwargs) -> str:
|
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)
|
kwargs.setdefault('width', 15)
|
||||||
intxt = text.replace('_', '-')
|
intxt = text.replace('_', '-')
|
||||||
wrapped = textwrap.fill(intxt, **kwargs)
|
wrapped = textwrap.fill(intxt, **kwargs)
|
||||||
|
|
@ -35,27 +43,38 @@ def variability_plot(
|
||||||
boxprops: dict[str, Any] | bool = True,
|
boxprops: dict[str, Any] | bool = True,
|
||||||
meanprops: dict[str, Any] | bool = True,
|
meanprops: dict[str, Any] | bool = True,
|
||||||
) -> tuple[Figure, Axes, list[Axes], list[Axes]]:
|
) -> tuple[Figure, Axes, list[Axes], list[Axes]]:
|
||||||
"""
|
"""Create a hierarchical categorical variability plot.
|
||||||
Create a variability plot (categorical box & scatter 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:
|
Args:
|
||||||
data_table: Dataset to plot. Passed directly to `polars.DataFrame()`.
|
data_table: Dataset to plot. Passed directly to
|
||||||
data_col: Column to use for box/scatterplot value.
|
`polars.DataFrame`.
|
||||||
groups: Columns to group by. Coarsest grouping should be first, and will appear
|
data_col: Numeric column to plot on the y-axis.
|
||||||
furthest from the scatterplot, at the bottom of the figure.
|
groups: Columns to group by. The coarsest grouping should be first and
|
||||||
vert_groups: Labels for these column names will be rotated (text will run vertically).
|
appears furthest from the scatter plot, at the bottom of the figure.
|
||||||
wrap_fn: Function called to wrap label text (i.e. insert newlines).
|
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.
|
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
|
mainplot_ratios: Width and height scale factors for the main data axes,
|
||||||
of other axes. Default is (10, 10).
|
relative to the label/header axes.
|
||||||
ylim: Y-limits for the scatter/box plot. Points which fall outside the limits are drawn
|
ylim: Optional y-limits for the scatter and box plot. Points outside
|
||||||
as red triangles at the edges.
|
the limits are drawn as red triangles at the corresponding edge.
|
||||||
dotprops: Passed as kwargs to scatterplot.
|
dotprops: `False` disables jittered scatter points. A dictionary is
|
||||||
boxprops: Passed as kwargs to boxplot.
|
passed as keyword arguments to `matplotlib.axes.Axes.scatter`.
|
||||||
meanprops: Passed as kwargs to lineplot of means.
|
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:
|
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)
|
vert_groups = set(vert_groups)
|
||||||
|
|
||||||
|
|
@ -261,6 +280,12 @@ def variability_plot(
|
||||||
label_stack_t = list[list[tuple[int, int, str]]]
|
label_stack_t = list[list[tuple[int, int, str]]]
|
||||||
|
|
||||||
def debounce(func: Callable, delay_s: float = 0.05) -> Callable:
|
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
|
timer = None
|
||||||
def debounced_func(*args, **kwargs) -> None:
|
def debounced_func(*args, **kwargs) -> None:
|
||||||
nonlocal timer
|
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:
|
def get_label_stack(df_groups: polars.DataFrame, groups: Sequence[str], wrap_fn: Callable) -> label_stack_t:
|
||||||
"""
|
"""Build label text and x-span metadata for each grouping level.
|
||||||
For each level, get (xmin_inclusive, xmax_inclusive, wrapped_text_for_label) for all the labels
|
|
||||||
|
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 = []
|
label_stack = []
|
||||||
for ll, level in enumerate(groups):
|
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]]:
|
def get_text_sizes(label_stack: label_stack_t) -> list[NDArray[numpy.float64]]:
|
||||||
"""
|
"""Measure unrotated label dimensions for layout calculations.
|
||||||
Transform the label stack (see `get_label_stack` into a stack of (allocated x-span, unrotated x-size, unrotated y-size)
|
|
||||||
|
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()
|
fig, ax = pyplot.subplots()
|
||||||
text_obj = ax.text(0, 0, 'placeholder')
|
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]:
|
def get_label_y_ratios(groups: Sequence[str], vert_groups: set[str], size_lists: list[NDArray[numpy.float64]]) -> list[float]:
|
||||||
"""
|
"""Calculate relative GridSpec heights for hierarchical label rows.
|
||||||
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.
|
The calculation accounts for vertically rotated label groups, normalizes the
|
||||||
Output order is reversed so that the bottom labels (most general) come last.
|
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)
|
grouping_rotated = numpy.array([grouping in vert_groups for grouping in groups], dtype=bool)
|
||||||
level_dims = []
|
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:
|
def _mk_data(filename: str) -> None:
|
||||||
"""
|
"""Create synthetic variability-chart data for the module demo.
|
||||||
Make some dummy data and write it to a csv file
|
|
||||||
|
Args:
|
||||||
|
filename: Path where the generated CSV data should be written.
|
||||||
"""
|
"""
|
||||||
rows = []
|
rows = []
|
||||||
rng = numpy.random.default_rng(seed=0)
|
rng = numpy.random.default_rng(seed=0)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
"""Wafer-style tiled heat maps for matplotlib."""
|
||||||
|
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
from matplotlib import pyplot
|
from matplotlib import pyplot
|
||||||
|
|
@ -17,12 +19,30 @@ def wafermap(
|
||||||
xy2sn: Callable[[int, int], str] | None = None,
|
xy2sn: Callable[[int, int], str] | None = None,
|
||||||
aspect_ratio: float = 1,
|
aspect_ratio: float = 1,
|
||||||
) -> tuple[Figure, Axes]:
|
) -> tuple[Figure, Axes]:
|
||||||
"""
|
"""Plot sparse `(x, y, z)` samples as a tiled wafer-style heat map.
|
||||||
Wafer map plot
|
|
||||||
|
|
||||||
This is effectively a pcolor plot which does a clearer job showing that data may come
|
The plot bins each sample into integer-spaced x/y tiles, draws one color
|
||||||
from anywhere within a tile, and correctly shows missing data.
|
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()
|
fig, ax = pyplot.subplots()
|
||||||
|
|
||||||
|
|
@ -62,6 +82,7 @@ def wafermap(
|
||||||
|
|
||||||
|
|
||||||
def example() -> None:
|
def example() -> None:
|
||||||
|
"""Run an interactive wafer map example with synthetic radial data."""
|
||||||
rng = numpy.random.default_rng(0)
|
rng = numpy.random.default_rng(0)
|
||||||
ar = 0.5
|
ar = 0.5
|
||||||
ny = 10
|
ny = 10
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue