Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dev = [
"setuptools-scm>=8.3.1",
"tox>=4.25.0",
"twine>=6.1.0",
"types-deprecated>=1.2.15.20250304",
"types-pyyaml>=6.0.12.20250402",
"types-seaborn>=0.13.2.20250111",
]
Expand Down Expand Up @@ -53,10 +54,13 @@ classifiers = [
"License :: OSI Approved :: BSD License",
]
dependencies = [
"coverage==7.8.0",
"deprecated>=1.2.18",
"loguru>=0.7.2",
"numpy!=1.26",
"pandas[excel]>=2.2.2",
"pydantic>=2.8.2",
"pytest-cov==6.1.1",
"pyyaml>=6.0.2",
"scipy>=1.14.1",
"seaborn>=0.13.2",
Expand Down
7 changes: 2 additions & 5 deletions src/soundscapy/plotting/iso_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Example:
-------
>>> from soundscapy import isd, surveys
>>> from soundscapy.plotting.iso_plot_new import ISOPlot
>>> from soundscapy.plotting.iso_plot import ISOPlot
>>> df = isd.load()
>>> df = surveys.add_iso_coords(df)
>>> sub_df = isd.select_location_ids(df, ['CamdenTown', 'RegentsParkJapan'])
Expand Down Expand Up @@ -339,10 +339,7 @@ def _check_data_x_y(
)

data = pd.DataFrame({xcol: x, ycol: y})
x = xcol
y = ycol

return data, x, y
return data, xcol, ycol

# If data is None, and x and y are strings:
if isinstance(x, str) and isinstance(y, str):
Expand Down
91 changes: 91 additions & 0 deletions src/soundscapy/plotting/new/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Refactored Plotting Module

This directory contains a refactored implementation of the plotting functionality in soundscapy. The refactoring focuses
on:

1. Using composition instead of inheritance
2. Simplifying the relationships between components
3. Consolidating parameter models
4. Improving type safety

## Architecture

The new architecture consists of the following components:

### Core Components

- **ISOPlot**: The main entry point for creating plots. Uses composition to delegate functionality to specialized
managers.
- **PlotContext**: Manages data, state, and parameters for a plot or subplot. The central component that owns parameter
models.
- **Layer**: Base class for visualization layers. Layers know how to render themselves onto a PlotContext's axes.

### Managers

- **LayerManager**: Manages the creation and rendering of visualization layers.
- **StyleManager**: Manages the styling of plots.
- **SubplotManager**: Manages the creation and configuration of subplots.

### Parameter Models

- **BaseParams**: Base model for all parameter types.
- **AxisParams**: Parameters for axis configuration.
- **SeabornParams**: Base parameters for seaborn plotting functions.
- **ScatterParams**: Parameters for scatter plot functions.
- **DensityParams**: Parameters for density plot functions.
- **SimpleDensityParams**: Parameters for simple density plots.
- **SPISeabornParams**: Base parameters for SPI plotting functions.
- **SPISimpleDensityParams**: Parameters for SPI simple density plots.
- **StyleParams**: Configuration options for styling circumplex plots.
- **SubplotsParams**: Parameters for subplot configuration.

### Layer Types

- **ScatterLayer**: Layer for rendering scatter plots.
- **DensityLayer**: Layer for rendering density plots.
- **SimpleDensityLayer**: Layer for rendering simple density plots.
- **SPISimpleLayer**: Layer for rendering SPI simple density plots.

### Protocols

- **RenderableLayer**: Protocol defining what a renderable layer must implement.
- **ParameterProvider**: Protocol defining how parameters are provided.
- **ParamModel**: Protocol defining the interface for parameter models.
- **PlotContext**: Protocol defining the interface for plot contexts.

## Usage

The new implementation maintains the same public API as the original, so existing code should continue to work with
minimal changes:

```python
from soundscapy.plotting.new import ISOPlot

# Create a plot
plot = ISOPlot(data=data, hue="LocationID")

# Add layer_mgr
plot.create_subplots()
plot.add_scatter()
plot.add_density()
plot.apply_styling()

# Show the plot
plot.show()
```

## Benefits of the New Architecture

1. **Clearer Separation of Concerns**: Each component has a well-defined responsibility.
2. **Reduced Coupling**: Components are less tightly coupled, making the code more maintainable.
3. **Improved Type Safety**: Better use of type hints and protocols for structural typing.
4. **More Flexible Composition**: Easier to extend with new layer types and customize behavior.
5. **Reduced Duplication**: Single source of truth for parameters.
6. **Simplified Testing**: Components can be tested in isolation.

## Implementation Notes

- The refactored code is in a separate directory to avoid breaking existing code.
- The parameter models use Pydantic for validation, maintaining the type safety of the original implementation.
- The layer system has been simplified, with a focus on using parameters from the context.
- The ISOPlot class uses composition instead of inheritance, delegating functionality to specialized managers.
77 changes: 77 additions & 0 deletions src/soundscapy/plotting/new/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
Refactored plotting module for soundscapy.

This module provides a refactored implementation of the plotting functionality
in soundscapy, using composition instead of inheritance and with a cleaner
architecture. The main entry point is the ISOPlot class.

Examples
--------
Create a simple scatter plot:

>>> import pandas as pd
>>> import numpy as np
>>> from soundscapy.plotting.new import ISOPlot
>>> # Create some sample data
>>> rng = np.random.default_rng(42)
>>> data = pd.DataFrame(
... rng.multivariate_normal([0.2, 0.15], [[0.1, 0], [0, 0.2]], 100),
... columns=['ISOPleasant', 'ISOEventful']
... )
>>> # Create a plot with multiple layers
>>> plot = (ISOPlot(data=data)
... .create_subplots()
... .add_scatter()
... .add_simple_density(fill=False)
... .apply_styling(
... xlim=(-1, 1),
... ylim=(-1, 1),
... primary_lines=True
... ))
>>> isinstance(plot, ISOPlot)
True
>>> # plot.show() # Uncomment to display the plot

"""

from soundscapy.plotting.new.iso_plot import ISOPlot
from soundscapy.plotting.new.layer import (
DensityLayer,
Layer,
ScatterLayer,
SimpleDensityLayer,
SPISimpleLayer,
)
from soundscapy.plotting.new.parameter_models import (
BaseParams,
DensityParams,
ScatterParams,
SimpleDensityParams,
SPISeabornParams,
SPISimpleDensityParams,
StyleParams,
SubplotsParams,
)
from soundscapy.plotting.new.plot_context import PlotContext

__all__ = [
# Parameter models
"BaseParams",
"DensityLayer",
"DensityParams",
# Main plotting class
"ISOPlot",
# Layer classes
"Layer",
# Context
"PlotContext",
"SPISeabornParams",
"SPISimpleDensityParams",
"SPISimpleLayer",
"ScatterLayer",
"ScatterParams",
"SimpleDensityLayer",
"SimpleDensityParams",
"StyleParams",
"SubplotsParams",
]
46 changes: 46 additions & 0 deletions src/soundscapy/plotting/new/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""
Constants for soundscape plotting functions.

This module provides common constants used for various plot types,
including default column names, limits, and other configuration values.
These constants are used by the parameter models to provide default values.
"""

# Basic defaults
DEFAULT_XCOL = "ISOPleasant"
DEFAULT_YCOL = "ISOEventful"
DEFAULT_XLIM = (-1, 1)
DEFAULT_YLIM = (-1, 1)

DEFAULT_FIGSIZE = (5, 5)
DEFAULT_POINT_SIZE = 20
DEFAULT_BW_ADJUST = 1.2

DEFAULT_COLOR = "#0173B2" # First color from colorblind palette

RECOMMENDED_MIN_SAMPLES = 30

# Default font settings for axis labels
DEFAULT_FONTDICT = {
"family": "sans-serif",
"fontstyle": "normal",
"fontsize": "large",
"fontweight": "medium",
"parse_math": True,
"c": "black",
"alpha": 1,
}

# Default SPI text settings
DEFAULT_SPI_TEXT_KWARGS = {
"x": 0,
"y": -0.85,
"fontsize": 10,
"bbox": {
"facecolor": "white",
"edgecolor": "black",
"boxstyle": "round,pad=0.3",
},
"ha": "center",
"va": "center",
}
Loading
Loading