mgplot.fill_between_plot

Plot a filled region between two bounds.

  1"""Plot a filled region between two bounds."""
  2
  3from typing import Final, NotRequired, Unpack
  4
  5from matplotlib.axes import Axes
  6from pandas import DataFrame
  7
  8from mgplot.axis_utils import map_periodindex, set_labels
  9from mgplot.keyword_checking import BaseKwargs, report_kwargs, validate_kwargs
 10from mgplot.settings import get_setting
 11from mgplot.utilities import check_clean_timeseries, constrain_data, get_axes
 12
 13# --- constants
 14ME: Final[str] = "fill_between_plot"
 15REQUIRED_COLUMNS: Final[int] = 2
 16DEFAULT_COLOR: Final[str] = "steelblue"
 17DEFAULT_ALPHA: Final[float] = 0.3
 18
 19
 20class FillBetweenKwargs(BaseKwargs):
 21    """Keyword arguments for the fill_between_plot function."""
 22
 23    ax: NotRequired[Axes | None]
 24    color: NotRequired[str]
 25    alpha: NotRequired[float]
 26    label: NotRequired[str | None]
 27    linewidth: NotRequired[float]
 28    edgecolor: NotRequired[str | None]
 29    zorder: NotRequired[int | float]
 30    plot_from: NotRequired[int | None]
 31    max_ticks: NotRequired[int]
 32
 33
 34def fill_between_plot(data: DataFrame, **kwargs: Unpack[FillBetweenKwargs]) -> Axes:
 35    """Plot a filled region between lower and upper bounds.
 36
 37    Args:
 38        data: DataFrame - A two-column DataFrame with PeriodIndex.
 39              The first column is the lower bound, the second is the upper bound.
 40        kwargs: FillBetweenKwargs - keyword arguments for the plot.
 41
 42    Returns:
 43        Axes - matplotlib Axes object.
 44
 45    Raises:
 46        TypeError: If data is not a DataFrame.
 47        ValueError: If data does not have exactly two columns.
 48
 49    """
 50    # --- validate inputs
 51    report_kwargs(caller=ME, **kwargs)
 52    validate_kwargs(schema=FillBetweenKwargs, caller=ME, **kwargs)
 53
 54    if not isinstance(data, DataFrame):
 55        raise TypeError(f"data must be a DataFrame for {ME}()")
 56
 57    if len(data.columns) != REQUIRED_COLUMNS:
 58        raise ValueError(f"data must have exactly two columns for {ME}(), got {len(data.columns)}")
 59
 60    # --- check and constrain data
 61    data = check_clean_timeseries(data, ME)
 62    data, kwargs_d = constrain_data(data, **kwargs)
 63
 64    # --- handle PeriodIndex conversion
 65    saved_pi = map_periodindex(data)
 66    if saved_pi is not None:
 67        data = saved_pi[0]
 68
 69    # --- get axes
 70    axes, kwargs_d = get_axes(**kwargs_d)
 71
 72    if data.empty or data.isna().all().all():
 73        print(f"Warning: No data to plot in {ME}().")
 74        return axes
 75
 76    # --- extract bounds
 77    lower = data.iloc[:, 0]
 78    upper = data.iloc[:, 1]
 79
 80    # --- extract plot arguments
 81    color = kwargs_d.get("color", DEFAULT_COLOR)
 82    alpha = kwargs_d.get("alpha", DEFAULT_ALPHA)
 83    label = kwargs_d.get("label", None)
 84    linewidth = kwargs_d.get("linewidth", 0)
 85    edgecolor = kwargs_d.get("edgecolor", None)
 86    zorder = kwargs_d.get("zorder", None)
 87
 88    # --- plot
 89    axes.fill_between(
 90        data.index,
 91        lower,
 92        upper,
 93        color=color,
 94        alpha=alpha,
 95        label=label,
 96        linewidth=linewidth,
 97        edgecolor=edgecolor,
 98        zorder=zorder,
 99    )
100
101    # --- set axis labels
102    if saved_pi is not None:
103        set_labels(axes, saved_pi[1], kwargs_d.get("max_ticks", get_setting("max_ticks")))
104
105    return axes
ME: Final[str] = 'fill_between_plot'
REQUIRED_COLUMNS: Final[int] = 2
DEFAULT_COLOR: Final[str] = 'steelblue'
DEFAULT_ALPHA: Final[float] = 0.3
class FillBetweenKwargs(mgplot.keyword_checking.BaseKwargs):
21class FillBetweenKwargs(BaseKwargs):
22    """Keyword arguments for the fill_between_plot function."""
23
24    ax: NotRequired[Axes | None]
25    color: NotRequired[str]
26    alpha: NotRequired[float]
27    label: NotRequired[str | None]
28    linewidth: NotRequired[float]
29    edgecolor: NotRequired[str | None]
30    zorder: NotRequired[int | float]
31    plot_from: NotRequired[int | None]
32    max_ticks: NotRequired[int]

Keyword arguments for the fill_between_plot function.

ax: NotRequired[matplotlib.axes._axes.Axes | None]
color: NotRequired[str]
alpha: NotRequired[float]
label: NotRequired[str | None]
linewidth: NotRequired[float]
edgecolor: NotRequired[str | None]
zorder: NotRequired[int | float]
plot_from: NotRequired[int | None]
max_ticks: NotRequired[int]
def fill_between_plot( data: pandas.core.frame.DataFrame, **kwargs: Unpack[FillBetweenKwargs]) -> matplotlib.axes._axes.Axes:
 35def fill_between_plot(data: DataFrame, **kwargs: Unpack[FillBetweenKwargs]) -> Axes:
 36    """Plot a filled region between lower and upper bounds.
 37
 38    Args:
 39        data: DataFrame - A two-column DataFrame with PeriodIndex.
 40              The first column is the lower bound, the second is the upper bound.
 41        kwargs: FillBetweenKwargs - keyword arguments for the plot.
 42
 43    Returns:
 44        Axes - matplotlib Axes object.
 45
 46    Raises:
 47        TypeError: If data is not a DataFrame.
 48        ValueError: If data does not have exactly two columns.
 49
 50    """
 51    # --- validate inputs
 52    report_kwargs(caller=ME, **kwargs)
 53    validate_kwargs(schema=FillBetweenKwargs, caller=ME, **kwargs)
 54
 55    if not isinstance(data, DataFrame):
 56        raise TypeError(f"data must be a DataFrame for {ME}()")
 57
 58    if len(data.columns) != REQUIRED_COLUMNS:
 59        raise ValueError(f"data must have exactly two columns for {ME}(), got {len(data.columns)}")
 60
 61    # --- check and constrain data
 62    data = check_clean_timeseries(data, ME)
 63    data, kwargs_d = constrain_data(data, **kwargs)
 64
 65    # --- handle PeriodIndex conversion
 66    saved_pi = map_periodindex(data)
 67    if saved_pi is not None:
 68        data = saved_pi[0]
 69
 70    # --- get axes
 71    axes, kwargs_d = get_axes(**kwargs_d)
 72
 73    if data.empty or data.isna().all().all():
 74        print(f"Warning: No data to plot in {ME}().")
 75        return axes
 76
 77    # --- extract bounds
 78    lower = data.iloc[:, 0]
 79    upper = data.iloc[:, 1]
 80
 81    # --- extract plot arguments
 82    color = kwargs_d.get("color", DEFAULT_COLOR)
 83    alpha = kwargs_d.get("alpha", DEFAULT_ALPHA)
 84    label = kwargs_d.get("label", None)
 85    linewidth = kwargs_d.get("linewidth", 0)
 86    edgecolor = kwargs_d.get("edgecolor", None)
 87    zorder = kwargs_d.get("zorder", None)
 88
 89    # --- plot
 90    axes.fill_between(
 91        data.index,
 92        lower,
 93        upper,
 94        color=color,
 95        alpha=alpha,
 96        label=label,
 97        linewidth=linewidth,
 98        edgecolor=edgecolor,
 99        zorder=zorder,
100    )
101
102    # --- set axis labels
103    if saved_pi is not None:
104        set_labels(axes, saved_pi[1], kwargs_d.get("max_ticks", get_setting("max_ticks")))
105
106    return axes

Plot a filled region between lower and upper bounds.

Args: data: DataFrame - A two-column DataFrame with PeriodIndex. The first column is the lower bound, the second is the upper bound. kwargs: FillBetweenKwargs - keyword arguments for the plot.

Returns: Axes - matplotlib Axes object.

Raises: TypeError: If data is not a DataFrame. ValueError: If data does not have exactly two columns.