Skip to content

Plotting Layers

soundscapy.plotting.layers

Layer-based visualization components for plotting.

This module provides a system of layer classes that implement different visualization techniques for ISO plots. Each layer encapsulates a specific visualization method and knows how to render itself on a given context.

CLASS DESCRIPTION
Layer

Base class for all visualization layers.

ScatterLayer

Layer for rendering scatter plots.

DensityLayer

Layer for rendering kernel density plots.

SimpleDensityLayer

Layer for rendering simplified density plots with fewer contour levels.

SPILayer

Layer for rendering SPI plots.

SPISimpleLayer

Layer for rendering simplified SPI plots with fewer contour levels.

SPIDensityLayer

Layer for rendering simplified SPI plots with fewer contour levels.

SPIScatterLayer

Layer for rendering simplified SPI plots with fewer contour levels.

Layer

Layer(
    custom_data: DataFrame | None = None,
    param_model: type[SeabornParams] = SeabornParams,
    **params: Any,
)

Base class for all visualization layers.

A Layer encapsulates a specific visualization technique and its associated parameters. Layers know how to render themselves onto a PlotContext's axes.

ATTRIBUTE DESCRIPTION
custom_data

Optional custom data for this specific layer, overriding context data

TYPE: DataFrame | None

params

Parameter model instance for this layer

TYPE: ParamModel

Initialize a Layer.

PARAMETER DESCRIPTION
custom_data

Optional custom data for this specific layer, overriding context data

TYPE: DataFrame | None DEFAULT: None

param_model

The parameter model class to use, if None uses a generic ParamModel

TYPE: type[SeabornParams] DEFAULT: SeabornParams

**params

Parameters for the layer

TYPE: Any DEFAULT: {}

METHOD DESCRIPTION
render

Render this layer on the given context.

Source code in src/soundscapy/plotting/layers.py
def __init__(
    self,
    custom_data: pd.DataFrame | None = None,
    param_model: type[SeabornParams] = SeabornParams,
    **params: Any,
) -> None:
    """
    Initialize a Layer.

    Parameters
    ----------
    custom_data
        Optional custom data for this specific layer, overriding context data
    param_model
        The parameter model class to use, if None uses a generic ParamModel
    **params
        Parameters for the layer

    """
    self.custom_data = custom_data
    # Create parameter model instance
    self.params = param_model(**params)

render

render(context: PlotContext) -> None

Render this layer on the given context.

PARAMETER DESCRIPTION
context

The context containing data and axes for rendering

TYPE: PlotContext

Source code in src/soundscapy/plotting/layers.py
def render(self, context: PlotContext) -> None:
    """
    Render this layer on the given context.

    Parameters
    ----------
    context
        The context containing data and axes for rendering

    """
    if context.ax is None:
        msg = "Cannot render layer: context has no associated axes"
        raise ValueError(msg)

    # Use custom data if provided, otherwise context data
    data = self.custom_data if self.custom_data is not None else context.data

    if data is None:
        msg = "No data available for rendering layer"
        raise ValueError(msg)

    self._render_implementation(data, context, context.ax)

ScatterLayer

ScatterLayer(
    custom_data: DataFrame | None = None, **params: Any
)

Bases: Layer

Layer for rendering scatter plots.

Initialize a ScatterLayer.

PARAMETER DESCRIPTION
custom_data

Optional custom data for this specific layer

TYPE: DataFrame | None DEFAULT: None

**params

Parameters for the scatter plot

TYPE: Any DEFAULT: {}

Source code in src/soundscapy/plotting/layers.py
def __init__(self, custom_data: pd.DataFrame | None = None, **params: Any) -> None:
    """
    Initialize a ScatterLayer.

    Parameters
    ----------
    custom_data
        Optional custom data for this specific layer
    **params
        Parameters for the scatter plot

    """
    super().__init__(custom_data=custom_data, param_model=ScatterParams, **params)

DensityLayer

DensityLayer(
    custom_data: DataFrame | None = None,
    *,
    param_model: type[DensityParams] = DensityParams,
    include_outline: bool = False,
    **params: Any,
)

Bases: Layer

Layer for rendering kernel density plots.

Initialize a DensityLayer.

PARAMETER DESCRIPTION
custom_data

Optional custom data for this specific layer

TYPE: DataFrame | None DEFAULT: None

include_outline

Whether to include an outline around the density plot

TYPE: bool DEFAULT: False

**params

Parameters for the density plot

TYPE: Any DEFAULT: {}

Source code in src/soundscapy/plotting/layers.py
def __init__(
    self,
    custom_data: pd.DataFrame | None = None,
    *,
    param_model: type[DensityParams] = DensityParams,
    include_outline: bool = False,
    **params: Any,
) -> None:
    """
    Initialize a DensityLayer.

    Parameters
    ----------
    custom_data
        Optional custom data for this specific layer
    include_outline
        Whether to include an outline around the density plot
    **params
        Parameters for the density plot

    """
    self.include_outline = include_outline
    self.params: DensityParams
    super().__init__(custom_data=custom_data, param_model=param_model, **params)

SimpleDensityLayer

SimpleDensityLayer(
    custom_data: DataFrame | None = None,
    *,
    include_outline: bool = True,
    param_model: type[
        SimpleDensityParams
    ] = SimpleDensityParams,
    **params: Any,
)

Bases: DensityLayer

Layer for rendering simplified density plots with fewer contour levels.

Initialize a SimpleDensityLayer.

PARAMETER DESCRIPTION
custom_data

Optional custom data for this specific layer

TYPE: DataFrame | None DEFAULT: None

include_outline

Whether to include an outline around the density plot

TYPE: bool DEFAULT: True

**params

Parameters for the density plot

TYPE: Any DEFAULT: {}

Source code in src/soundscapy/plotting/layers.py
def __init__(
    self,
    custom_data: pd.DataFrame | None = None,
    *,
    include_outline: bool = True,
    param_model: type[SimpleDensityParams] = SimpleDensityParams,
    **params: Any,
) -> None:
    """
    Initialize a SimpleDensityLayer.

    Parameters
    ----------
    custom_data
        Optional custom data for this specific layer
    include_outline
        Whether to include an outline around the density plot
    **params
        Parameters for the density plot

    """
    super().__init__(
        custom_data=custom_data,
        include_outline=include_outline,  # Could move into params now
        param_model=param_model,
        **params,
    )

SPILayer

SPILayer(
    spi_target_data: DataFrame | ndarray | None = None,
    *,
    msn_params: DirectParams | CentredParams | None = None,
    n: int = 10000,
    param_model: type[SPISeabornParams] = SPISeabornParams,
    **params: Any,
)

Bases: Layer

Layer for rendering SPI plots.

Initialize an SPILayer.

PARAMETER DESCRIPTION
spi_target_data

Pre-sampled data for SPI target distribution. When None, msn_params must be provided.

TYPE: DataFrame | ndarray | None DEFAULT: None

msn_params

Parameters to generate SPI data if no spi_target_data is provided

TYPE: DirectParams | CentredParams | None DEFAULT: None

n

Number of samples to generate if using msn_params

TYPE: int DEFAULT: 10000

param_model

The parameter model class to use

TYPE: type[SPISeabornParams] DEFAULT: SPISeabornParams

**params

Parameters for the layer. For compatibility with other layers, if 'custom_data' is present and spi_target_data is None, custom_data will be used as the SPI target data.

TYPE: Any DEFAULT: {}

Notes

Either spi_target_data or msn_params must be provided, but not both. The test data for SPI calculations will be retrieved from the plot context.

METHOD DESCRIPTION
render

Render this layer on the given context.

show_score

Show the SPI score on the plot.

Source code in src/soundscapy/plotting/layers.py
def __init__(
    self,
    spi_target_data: pd.DataFrame | np.ndarray | None = None,
    *,
    # TODO(MitchellAcoustics): Allow passing raw param values,
    #  not just Param objects
    msn_params: DirectParams | CentredParams | None = None,
    n: int = 10000,
    param_model: type[SPISeabornParams] = SPISeabornParams,
    **params: Any,
) -> None:
    """
    Initialize an SPILayer.

    Parameters
    ----------
    spi_target_data
        Pre-sampled data for SPI target distribution.
        When None, msn_params must be provided.
    msn_params
        Parameters to generate SPI data if no spi_target_data is provided
    n
        Number of samples to generate if using msn_params
    param_model
        The parameter model class to use
    **params
        Parameters for the layer. For compatibility with other layers,
        if 'custom_data' is present and spi_target_data is None,
        custom_data will be used as the SPI target data.

    Notes
    -----
    Either spi_target_data or msn_params must be provided, but not both.
    The test data for SPI calculations will be retrieved from the plot context.

    """
    # The custom_data passed when adding this layer should be the spi_data.
    # We will retrieve the test_data from the subplot context, so real data layers
    # need to be passed before this one, or use the data from the
    # main ISOPlot context
    custom_data = params.pop("custom_data", None)
    if custom_data is not None and spi_target_data is None:
        logger.warning(
            "`spi_target_data` not found, but `custom_data` was found. "
            "Using `custom_data` as the SPI target data. "
            "\nNote: Passing the SPI data to `spi_target_data` is preferred."
        )
        spi_target_data = custom_data

    # Check that we have the information needed to generate SPI target data
    # (either the spi_data or msn_params)
    spi_target_data, self.spi_params = self._validate_spi_inputs(
        spi_target_data, msn_params
    )
    # Generate the spi target data
    self.spi_data: pd.DataFrame = self._generate_spi_data(
        spi_target_data, self.spi_params, n
    )
    params["n"] = n
    self.params: SPISeabornParams

    super().__init__(custom_data=self.spi_data, param_model=param_model, **params)

render

render(context: PlotContext) -> None

Render this layer on the given context.

PARAMETER DESCRIPTION
context

The context containing data and axes for rendering

TYPE: PlotContext

Source code in src/soundscapy/plotting/layers.py
def render(self, context: PlotContext) -> None:
    """
    Render this layer on the given context.

    Parameters
    ----------
    context
        The context containing data and axes for rendering

    """
    if context.ax is None:
        msg = "Cannot render layer: context has no associated axes"
        raise ValueError(msg)

    target_data = self.spi_data
    # Mutate spi_data to match the context test_data.
    target_data = self._process_spi_data(target_data, context)

    if target_data is None:
        msg = "No data available for rendering layer"
        raise ValueError(msg)

    # Now we have the SPI target_data and the test data in context.data
    # SPILayer._render_implementation() will handle calculating the SPI score
    # against this particular context / ax test data

    self._render_implementation(target_data, context, context.ax)

show_score

show_score(
    spi_sc: int | None,
    show_score: Literal["on axis", "under title"],
    context: PlotContext,
    ax: Axes,
    axis_text_kwargs: dict[str, Any],
) -> None

Show the SPI score on the plot.

PARAMETER DESCRIPTION
spi_sc

The SPI score to show

TYPE: int | None

show_score

Where to show the score

TYPE: Literal['on axis', 'under title']

context

The context containing data and axes for rendering

TYPE: PlotContext

ax

The axes to render the score on

TYPE: Axes

axis_text_kwargs

Additional arguments for the axis text

TYPE: dict[str, Any]

Source code in src/soundscapy/plotting/layers.py
def show_score(
    self,
    spi_sc: int | None,
    show_score: Literal["on axis", "under title"],
    context: PlotContext,
    ax: Axes,
    axis_text_kwargs: dict[str, Any],
) -> None:
    """
    Show the SPI score on the plot.

    Parameters
    ----------
    spi_sc
        The SPI score to show
    show_score
        Where to show the score
    context
        The context containing data and axes for rendering
    ax
        The axes to render the score on
    axis_text_kwargs
        Additional arguments for the axis text

    """
    if spi_sc is not None:
        if show_score == "on axis":
            self._add_score_as_text(
                ax=ax,
                spi_sc=spi_sc,
                **axis_text_kwargs,
            )
        elif show_score == "under title":
            self._add_score_under_title(
                context=context,
                ax=ax,
                spi_sc=spi_sc,
            )

SPISimpleLayer

SPISimpleLayer(
    spi_target_data: DataFrame | ndarray | None = None,
    *,
    msn_params: DirectParams | CentredParams | None = None,
    include_outline: bool = True,
    **params: Any,
)

Bases: SPILayer, SimpleDensityLayer

Layer for rendering simplified SPI plots with fewer contour levels.

Initialize an SPISimpleLayer.

PARAMETER DESCRIPTION
spi_target_data

Optional SPI target data for this specific layer

TYPE: DataFrame | ndarray | None DEFAULT: None

msn_params

Parameters to generate SPI data if no custom data is provided

TYPE: DirectParams | CentredParams | None DEFAULT: None

include_outline

Whether to include an outline around the density plot

TYPE: bool DEFAULT: True

**params

Parameters for the density plot

TYPE: Any DEFAULT: {}

Source code in src/soundscapy/plotting/layers.py
def __init__(
    self,
    spi_target_data: pd.DataFrame | np.ndarray | None = None,
    *,
    msn_params: DirectParams | CentredParams | None = None,
    include_outline: bool = True,
    **params: Any,
) -> None:
    """
    Initialize an SPISimpleLayer.

    Parameters
    ----------
    spi_target_data
        Optional SPI target data for this specific layer
    msn_params
        Parameters to generate SPI data if no custom data is provided
    include_outline
        Whether to include an outline around the density plot
    **params
        Parameters for the density plot

    """
    self.params: SPISimpleDensityParams
    super().__init__(
        spi_target_data=spi_target_data,
        include_outline=include_outline,
        param_model=SPISimpleDensityParams,
        msn_params=msn_params,
        **params,
    )

SPIDensityLayer

SPIDensityLayer()

Bases: SPILayer, DensityLayer

Layer for rendering simplified SPI plots with fewer contour levels.

Initialize SPIDensityLayer.

This initialization is not supported and will raise NotImplementedError. Use SPISimpleLayer instead.

Source code in src/soundscapy/plotting/layers.py
def __init__(self) -> None:
    """
    Initialize SPIDensityLayer.

    This initialization is not supported and will raise NotImplementedError.
    Use SPISimpleLayer instead.
    """
    msg = (
        "Only the simple density layer type is currently supported for SPI plots. "
        "Please use SPISimpleLayer"
    )
    raise NotImplementedError(msg)

SPIScatterLayer

SPIScatterLayer()

Bases: SPILayer, ScatterLayer

Layer for rendering simplified SPI plots with fewer contour levels.

Initialize SPIScatterLayer.

This initialization is not supported and will raise NotImplementedError. Use SPISimpleLayer instead.

Source code in src/soundscapy/plotting/layers.py
def __init__(self) -> None:
    """
    Initialize SPIScatterLayer.

    This initialization is not supported and will raise NotImplementedError.
    Use SPISimpleLayer instead.
    """
    msg = (
        "Only the simple density layer type is currently supported for SPI plots. "
        "Please use SPISimpleLayer"
    )
    raise NotImplementedError(msg)