morphospace_plot#

ktch.plot.morphospace_plot(data: Any | None = None, *, x: str | npt.ArrayLike | None = None, y: str | npt.ArrayLike | None = None, hue: str | npt.ArrayLike | None = None, reducer: Any | None = None, reducer_inverse_transform: Callable[[np.ndarray], np.ndarray] | None = None, n_components: int | None = None, descriptor: Any | None = None, descriptor_inverse_transform: Callable[[np.ndarray], np.ndarray] | None = None, components: tuple[int, int] = (0, 1), shape_type: str = 'auto', render_fn: Callable[..., None] | None = None, n_dim: int | None = None, links: Sequence[Sequence[int]] | None = None, n_shapes: int = 5, shape_scale: float = 1.0, shape_color: str = 'lightgray', shape_alpha: float = 0.7, palette: str | Sequence | None = None, hue_order: Sequence | None = None, scatter_kw: dict[str, Any] | None = None, ax: object | None = None, **render_kw: Any) object[source]#

Scatter plot of specimens in morphospace with shape insets.

Draws a scatter plot of scores from dimension reduction (reducer) and overlays reconstructed shapes at a regular grid of positions in the low-dimensional space.

The function uses the same two-stage inverse transform pipeline as shape_variation_plot(): scores -> [reducer_inverse_transform] -> coefficients -> [descriptor_inverse_transform] -> shape coordinates.

Shape insets are parented to ax via matplotlib.axes.Axes.inset_axes() with transform=ax.transData, so they follow the parent axes automatically under tight_layout, subplots_adjust, and constrained_layout.

Parameters:
dataDataFrame, optional

DataFrame containing scores and metadata. If provided, x, y, hue refer to column names.

xstr or array-like, optional

Horizontal axis values (column name or array).

ystr or array-like, optional

Vertical axis values (column name or array).

huestr or array-like, optional

Grouping variable for scatter coloring.

reducerfitted estimator, optional

Convenience parameter. Extracts reducer_inverse_transform via .inverse_transform and n_components via .n_components_ (fallback to .n_components).

reducer_inverse_transformcallable, optional

Overrides reducer.inverse_transform.

n_componentsint, optional

Overrides reducer.n_components_.

descriptorfitted estimator, optional

Convenience parameter. Extracts descriptor_inverse_transform via .inverse_transform.

descriptor_inverse_transformcallable, optional

Overrides descriptor.inverse_transform.

componentstuple of (int, int)

0-indexed component indices for (horizontal, vertical) axes.

shape_typestr

Shape rendering type. One of "auto", "curve_2d", "curve_3d", "surface_3d", "landmarks_2d", "landmarks_3d".

render_fncallable, optional

Custom renderer (coords, ax, **kw) -> None.

n_dimint, optional

Spatial dimensionality (for GPA identity case).

linkssequence of sequence of int, optional

Landmark link pairs.

n_shapesint

Number of shapes along each axis (total: n_shapes * n_shapes).

shape_scalefloat

Scale factor for inset shape size, expressed as a fraction of the parent ax’s per-cell extent (the data range divided by n_shapes). 1.0 fills each cell.

shape_colorstr

Color for reconstructed shapes.

shape_alphafloat

Transparency for reconstructed shapes.

palettestr or sequence, optional

Forwarded to sns.scatterplot.

hue_ordersequence, optional

Forwarded to sns.scatterplot.

scatter_kwdict, optional

Additional kwargs forwarded to sns.scatterplot.

axmatplotlib.axes.Axes, optional

Pre-existing axes. If None, creates new figure and axes.

**render_kw

Forwarded to the shape renderer.

Returns:
axmatplotlib.axes.Axes

The main scatter plot axes.

Raises:
ImportError

If matplotlib or seaborn are not installed.

ValueError

If required parameters cannot be resolved.

See also

shape_variation_plot

Shape grid along component axes.

explained_variance_ratio_plot

Scree plot of explained variance.

Notes

When shape_type="auto" (the default), the type is inferred from a single specimen (batch dimension removed) of the descriptor inverse transform output:

  • (m, n, 3) (ndim=3) -> "surface_3d"

  • (t, 2) (ndim=2, last dim 2) -> "curve_2d"

  • (t, k) (ndim=2, last dim >= 3) -> "curve_3d"

  • No descriptor (identity / GPA case) with n_dim=2 -> "landmarks_2d"

  • No descriptor (identity / GPA case) with n_dim=3 -> "landmarks_3d"

For per-specimen shapes with shape[-1] == 3 and ndim=2, auto-detection chooses "curve_3d". If the data represents landmarks, specify shape_type="landmarks_3d" explicitly.

3-D shape types ("surface_3d", "curve_3d", "landmarks_3d") use matplotlib 3-D projection for each inset, which is significantly slower. For 3-D surfaces (e.g., SHA), consider using n_shapes <= 3 and reducing surface resolution via a descriptor_inverse_transform wrapper.

For 3-D shape types, descriptor.inverse_transform typically dominates the runtime. morphospace_plot issues a single batched call covering all n_shapes ** 2 grid points, so descriptors that parallelize across the batch dimension speed up plotting directly. SphericalHarmonicAnalysis, EllipticFourierAnalysis, and DiskHarmonicAnalysis all accept n_jobs at construction time:

sha = SphericalHarmonicAnalysis(n_harmonics=20, n_jobs=-1)
morphospace_plot(..., descriptor=sha)

Examples

>>> from ktch.plot import morphospace_plot
>>> ax = morphospace_plot(
...     data=df_pca,
...     x="PC1", y="PC2", hue="genus",
...     reducer=pca,
...     descriptor=efa,
...     palette="Paired",
...     n_shapes=5,
...     shape_scale=0.8,
... )