From 27524f26d08966bd95aa5e82ed3cb159a4a32d47 Mon Sep 17 00:00:00 2001 From: s-weigand Date: Sat, 4 Nov 2023 13:50:04 +0100 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=91=8C=20Add=20das=5Fcycler=20and=20s?= =?UTF-8?q?vd=5Fcycler=20to=20plot=20collection=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyglotaran_extras/plotting/plot_data.py | 14 +++++++++- pyglotaran_extras/plotting/plot_overview.py | 31 ++++++++++++++++++--- pyglotaran_extras/plotting/plot_spectra.py | 14 ++++++++-- pyglotaran_extras/types.py | 12 ++++++++ 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/pyglotaran_extras/plotting/plot_data.py b/pyglotaran_extras/plotting/plot_data.py index 67ee2f46..3e4f0746 100644 --- a/pyglotaran_extras/plotting/plot_data.py +++ b/pyglotaran_extras/plotting/plot_data.py @@ -12,6 +12,7 @@ from pyglotaran_extras.plotting.plot_svd import plot_lsv_data from pyglotaran_extras.plotting.plot_svd import plot_rsv_data from pyglotaran_extras.plotting.plot_svd import plot_sv_data +from pyglotaran_extras.plotting.style import PlotStyle from pyglotaran_extras.plotting.utils import MinorSymLogLocator from pyglotaran_extras.plotting.utils import not_single_element_dims from pyglotaran_extras.plotting.utils import shift_time_axis_by_irf_location @@ -22,6 +23,7 @@ from collections.abc import Hashable import xarray as xr + from cycler import Cycler from glotaran.project.result import Result from matplotlib.figure import Figure from matplotlib.pyplot import Axes @@ -41,6 +43,7 @@ def plot_data_overview( cmap: str = "PuRd", vmin: float | None = None, vmax: float | None = None, + svd_cycler: Cycler | None = PlotStyle().cycler, ) -> tuple[Figure, Axes] | tuple[Figure, Axis]: """Plot data as filled contour plot and SVD components. @@ -71,6 +74,8 @@ def plot_data_overview( Lower value to anchor the colormap. Defaults to None meaning it inferred from the data. vmax : float | None Lower value to anchor the colormap. Defaults to None meaning it inferred from the data. + svd_cycler : Cycler | None + Plot style cycler to use for SVD plots. Defaults to ``PlotStyle().cycler``. Returns ------- @@ -111,9 +116,16 @@ def plot_data_overview( linlog=linlog, linthresh=linthresh, irf_location=irf_location, + cycler=svd_cycler, ) plot_sv_data(dataset, sv_ax) - plot_rsv_data(dataset, rsv_ax, indices=range(nr_of_data_svd_vectors), show_legend=False) + plot_rsv_data( + dataset, + rsv_ax, + indices=range(nr_of_data_svd_vectors), + show_legend=False, + cycler=svd_cycler, + ) if show_data_svd_legend is True: rsv_ax.legend(title="singular value index", loc="lower right", bbox_to_anchor=(1.13, 1)) fig.suptitle(title, fontsize=16) diff --git a/pyglotaran_extras/plotting/plot_overview.py b/pyglotaran_extras/plotting/plot_overview.py index 87fd31cf..fabe58fe 100644 --- a/pyglotaran_extras/plotting/plot_overview.py +++ b/pyglotaran_extras/plotting/plot_overview.py @@ -21,6 +21,7 @@ from pyglotaran_extras.plotting.style import PlotStyle from pyglotaran_extras.plotting.utils import add_cycler_if_not_none from pyglotaran_extras.plotting.utils import extract_irf_location +from pyglotaran_extras.types import Unset if TYPE_CHECKING: from cycler import Cycler @@ -29,6 +30,7 @@ from matplotlib.pyplot import Axes from pyglotaran_extras.types import DatasetConvertible + from pyglotaran_extras.types import UnsetType def plot_overview( @@ -48,6 +50,8 @@ def plot_overview( show_residual_svd_legend: bool = True, show_irf_dispersion_center: bool = True, show_zero_line: bool = True, + das_cycler: Cycler | None | UnsetType = Unset, + svd_cycler: Cycler | None | UnsetType = Unset, ) -> tuple[Figure, Axes]: """Plot overview of the optimization result. @@ -95,6 +99,12 @@ def plot_overview( show_zero_line : bool Whether or not to add a horizontal line at zero to the plots of the spectra. Defaults to True. + das_cycler : Cycler | None | UnsetType + Plot style cycler to use for DAS plots. Defaults to ``Unset`` which means that the value + of ``cycler`` is used. + svd_cycler : Cycler | None | UnsetType + Plot style cycler to use for SVD plots. Defaults to ``Unset`` which means that the value + of ``cycler`` is used. Returns ------- @@ -102,6 +112,11 @@ def plot_overview( """ res = load_data(result, _stacklevel=3) + if das_cycler is Unset: + das_cycler = cycler + if svd_cycler is Unset: + svd_cycler = cycler + if res.coords["time"].to_numpy().size == 1: fig, axes = plot_guidance(res) if figure_only is not None: @@ -125,13 +140,15 @@ def plot_overview( main_irf_nr=main_irf_nr, cycler=cycler, ) - plot_spectra(res, axes[0:2, 1:3], cycler=cycler, show_zero_line=show_zero_line) + plot_spectra( + res, axes[0:2, 1:3], cycler=cycler, show_zero_line=show_zero_line, das_cycler=das_cycler + ) plot_svd( res, axes[2:4, 0:3], linlog=linlog, linthresh=linthresh, - cycler=cycler, + cycler=svd_cycler, nr_of_data_svd_vectors=nr_of_data_svd_vectors, nr_of_residual_svd_vectors=nr_of_residual_svd_vectors, show_data_svd_legend=show_data_svd_legend, @@ -161,6 +178,7 @@ def plot_simple_overview( figure_only: bool | None = None, show_irf_dispersion_center: bool = True, show_data: bool | None = False, + svd_cycler: Cycler | None | UnsetType = Unset, ) -> tuple[Figure, Axes]: """Plot simple overview. @@ -182,12 +200,17 @@ def plot_simple_overview( show_data : bool | None Whether to show the input data or residual. If set to ``None`` the plot is skipped which improves plotting performance for big datasets. Defaults to False. + svd_cycler : Cycler | None | UnsetType + Plot style cycler to use for SVD plots. Defaults to ``Unset`` which means that the value + of ``cycler`` is used. Returns ------- tuple[Figure, Axes] """ res = load_data(result, _stacklevel=3) + if svd_cycler is Unset: + svd_cycler = cycler fig, axes = plt.subplots(2, 3, figsize=figsize, constrained_layout=True) for ax in axes.flatten(): @@ -200,8 +223,8 @@ def plot_simple_overview( irf_location = extract_irf_location(res, center_λ=res.coords["spectral"].to_numpy()[0]) - plot_lsv_residual(res, ax=axes[1, 0], irf_location=irf_location) - plot_rsv_residual(res, ax=axes[1, 1]) + plot_lsv_residual(res, ax=axes[1, 0], irf_location=irf_location, cycler=svd_cycler) + plot_rsv_residual(res, ax=axes[1, 1], cycler=svd_cycler) plot_residual( res, diff --git a/pyglotaran_extras/plotting/plot_spectra.py b/pyglotaran_extras/plotting/plot_spectra.py index cc0df1e9..15597bb5 100644 --- a/pyglotaran_extras/plotting/plot_spectra.py +++ b/pyglotaran_extras/plotting/plot_spectra.py @@ -7,6 +7,7 @@ from pyglotaran_extras.plotting.style import PlotStyle from pyglotaran_extras.plotting.utils import add_cycler_if_not_none +from pyglotaran_extras.types import Unset if TYPE_CHECKING: import xarray as xr @@ -14,12 +15,15 @@ from matplotlib.axis import Axis from matplotlib.pyplot import Axes + from pyglotaran_extras.types import UnsetType + def plot_spectra( res: xr.Dataset, axes: Axes, cycler: Cycler | None = PlotStyle().cycler, show_zero_line: bool = True, + das_cycler: Cycler | None | UnsetType = Unset, ) -> None: """Plot spectra such as SAS and DAS as well as their normalize version on ``axes``. @@ -33,11 +37,17 @@ def plot_spectra( Plot style cycler to use. Defaults to PlotStyle().cycler. show_zero_line : bool Whether or not to add a horizontal line at zero. Defaults to True. + das_cycler : Cycler | None | UnsetType + Plot style cycler to use for DAS plots. Defaults to ``Unset`` which means that the value + of ``cycler`` is used. """ + if das_cycler is Unset: + das_cycler = cycler + plot_sas(res, axes[0, 0], cycler=cycler, show_zero_line=show_zero_line) - plot_das(res, axes[0, 1], cycler=cycler, show_zero_line=show_zero_line) + plot_das(res, axes[0, 1], cycler=das_cycler, show_zero_line=show_zero_line) plot_norm_sas(res, axes[1, 0], cycler=cycler, show_zero_line=show_zero_line) - plot_norm_das(res, axes[1, 1], cycler=cycler, show_zero_line=show_zero_line) + plot_norm_das(res, axes[1, 1], cycler=das_cycler, show_zero_line=show_zero_line) def plot_sas( diff --git a/pyglotaran_extras/types.py b/pyglotaran_extras/types.py index 9e988bb9..55659dd1 100644 --- a/pyglotaran_extras/types.py +++ b/pyglotaran_extras/types.py @@ -10,6 +10,18 @@ import xarray as xr from glotaran.project.result import Result + +class UnsetType: + """Type for the ``Unset`` singleton.""" + + def __repr__(self) -> str: # noqa: DOC + """Representation of instances in editors.""" + return "Unset" + + +Unset = UnsetType() +"""Singleton to use when arguments are not provided where None is a valid value.""" + DatasetConvertible: TypeAlias = xr.Dataset | xr.DataArray | str | Path """Types of data which can be converted to a dataset.""" ResultLike: TypeAlias = ( From a2ed2599edef5a5de24858c435027496d8eab327 Mon Sep 17 00:00:00 2001 From: s-weigand Date: Sat, 4 Nov 2023 14:15:26 +0100 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F=20Deprecate=20cycler?= =?UTF-8?q?=20argument=20in=20plot=5Fsv=5Fdata=20and=20plot=5Fsv=5Fresidua?= =?UTF-8?q?l?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn't have an effect since there is only a single scatter lines plot where the style is overwritten in the plot method call --- pyglotaran_extras/plotting/plot_svd.py | 33 ++++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/pyglotaran_extras/plotting/plot_svd.py b/pyglotaran_extras/plotting/plot_svd.py index ffb167bc..fc80f16f 100644 --- a/pyglotaran_extras/plotting/plot_svd.py +++ b/pyglotaran_extras/plotting/plot_svd.py @@ -5,10 +5,13 @@ from glotaran.io.prepare_dataset import add_svd_to_dataset +from pyglotaran_extras.deprecation import warn_deprecated from pyglotaran_extras.plotting.style import PlotStyle from pyglotaran_extras.plotting.utils import MinorSymLogLocator from pyglotaran_extras.plotting.utils import add_cycler_if_not_none from pyglotaran_extras.plotting.utils import shift_time_axis_by_irf_location +from pyglotaran_extras.types import Unset +from pyglotaran_extras.types import UnsetType if TYPE_CHECKING: from collections.abc import Sequence @@ -80,7 +83,7 @@ def plot_svd( show_legend=show_residual_svd_legend, irf_location=irf_location, ) - plot_sv_residual(res, axes[0, 2], cycler=cycler) + plot_sv_residual(res, axes[0, 2]) add_svd_to_dataset(dataset=res, name="data") plot_lsv_data( res, @@ -100,7 +103,7 @@ def plot_svd( show_legend=show_data_svd_legend, irf_location=irf_location, ) - plot_sv_data(res, axes[1, 2], cycler=cycler) + plot_sv_data(res, axes[1, 2]) def plot_lsv_data( @@ -181,7 +184,7 @@ def plot_sv_data( res: xr.Dataset, ax: Axis, indices: Sequence[int] = range(10), - cycler: Cycler | None = PlotStyle().cycler, + cycler: Cycler | None | UnsetType = Unset, ) -> None: """Plot singular values of the data matrix. @@ -193,10 +196,15 @@ def plot_sv_data( Axis to plot on. indices : Sequence[int] Indices of the singular vector to plot. Defaults to range(10). - cycler : Cycler | None - Plot style cycler to use. Defaults to PlotStyle().cycler. + cycler : Cycler | None | UnsetType + Deprecated since it has no effect. Defaults to Unset. """ - add_cycler_if_not_none(ax, cycler) + if cycler is not Unset: + warn_deprecated( + deprecated_qual_name_usage="'cycler' argument in 'plot_sv_data'", + new_qual_name_usage="matplotlib on the axis directly", + to_be_removed_in_version="0.9.0", + ) dSV = res.data_singular_values # noqa: N806 dSV.sel(singular_value_index=indices[: len(dSV.singular_value_index)]).plot.line( "ro-", yscale="log", ax=ax @@ -288,7 +296,7 @@ def plot_sv_residual( res: xr.Dataset, ax: Axis, indices: Sequence[int] = range(10), - cycler: Cycler | None = PlotStyle().cycler, + cycler: Cycler | None | UnsetType = Unset, ) -> None: """Plot singular values of the residual matrix. @@ -300,10 +308,15 @@ def plot_sv_residual( Axis to plot on. indices : Sequence[int] Indices of the singular vector to plot. Defaults to range(10). - cycler : Cycler | None - Plot style cycler to use. Defaults to PlotStyle().cycler. + cycler : Cycler | None | UnsetType + Deprecated since it has no effect. Defaults to Unset. """ - add_cycler_if_not_none(ax, cycler) + if cycler is not Unset: + warn_deprecated( + deprecated_qual_name_usage="'cycler' argument in 'plot_sv_residual'", + new_qual_name_usage="matplotlib on the axis directly", + to_be_removed_in_version="0.9.0", + ) if "weighted_residual_singular_values" in res: rSV = res.weighted_residual_singular_values # noqa: N806 else: From 793b9cad7661e977554bec0766196c81185f4ab6 Mon Sep 17 00:00:00 2001 From: s-weigand Date: Sat, 4 Nov 2023 14:42:48 +0100 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=9A=A7=F0=9F=93=9A=20Added=20changes?= =?UTF-8?q?=20since=200.7.1=20to=20changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/changelog.md b/changelog.md index 06ba4620..81b34a91 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,15 @@ ## 0.8.0 (Unreleased) +- 🧰👌 Switch tooling to ruff (#197) +- 🩹 Fix crash when plotting spectral model result (#200) +- 👷♻️ Use hatch as build backend (#204) +- 🧰 Use black-pre-commit-mirror for 2x speedup (#205) +- 🧰🚀 Use ruff for formatting (#214) +- 👌 Use weighted residual instead of residual plots if present (#216) +- 👌 Add color map arguments to plot_data_overview (#217) +- 👌 Add das_cycler and svd_cycler to plot collection functions (#218) + (changes-0_7_1)= ## 0.7.1 (2023-07-27) From fe49609627a5a118b09c85f9baff2ef43cdd1ead Mon Sep 17 00:00:00 2001 From: s-weigand Date: Sat, 4 Nov 2023 20:22:16 +0100 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=93=9A=F0=9F=91=8C=20Better=20descrip?= =?UTF-8?q?tion=20for=20Unset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyglotaran_extras/types.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyglotaran_extras/types.py b/pyglotaran_extras/types.py index 55659dd1..c4f84ddf 100644 --- a/pyglotaran_extras/types.py +++ b/pyglotaran_extras/types.py @@ -20,7 +20,10 @@ def __repr__(self) -> str: # noqa: DOC Unset = UnsetType() -"""Singleton to use when arguments are not provided where None is a valid value.""" +"""Value to use as default for an arguments where None is a meaningful value. + +This way we can prevent regressions. +""" DatasetConvertible: TypeAlias = xr.Dataset | xr.DataArray | str | Path """Types of data which can be converted to a dataset."""