Skip to content
Merged
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
Binary file added docs/_static/plotting/marginals-plot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
</br></br>
</div>


## Getting started

::::{grid} 2
Expand Down Expand Up @@ -120,6 +119,12 @@

:::

:::{grid-item-card} Marginals plot
:link: plotting/marginals.ipynb
:img-bottom: _static/plotting/marginals-plot.png

:::

::::

## Custom figures
Expand Down
7 changes: 7 additions & 0 deletions docs/plotting/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@

:::

:::{grid-item-card} Marginals plot
:link: marginals.ipynb
:img-bottom: ../_static/plotting/marginals-plot.png

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you also need to add it to the top-level index (currently does not show up there).


:::

::::

```{toctree}
Expand All @@ -73,4 +79,5 @@ super-plot
scatter-plot
scatter3d-plot
mesh3d-plot
marginals
```
106 changes: 106 additions & 0 deletions docs/plotting/marginals.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"source": [
"# Marginals plot\n",
"\n",
"The marginals plot shows a 2-dimensional image together with the 1-dimensional lines obtained from summing over each dimension."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1",
"metadata": {},
"outputs": [],
"source": [
"import plopp as pp"
]
},
{
"cell_type": "markdown",
"id": "2",
"metadata": {},
"source": [
"## Basic marginals plot\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3",
"metadata": {},
"outputs": [],
"source": [
"da = pp.data.data2d()\n",
Comment thread
nvaytet marked this conversation as resolved.
Resolved
"pp.marginals(da)"
]
},
{
"cell_type": "markdown",
"id": "4",
"metadata": {},
"source": [
"## Control the side of the vertical plot\n",
"\n",
"Above, the 1D plot as a function of `y` is placed on the left and its data axis increases from right to left.\n",
"We can alternatively show it on the right. This also results in the axis increasing from left to right as is the more common case:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5",
"metadata": {},
"outputs": [],
"source": [
"pp.marginals(da, side=\"right\")"
]
},
{
"cell_type": "markdown",
"id": "6",
"metadata": {},
"source": [
"## Extra arguments\n",
"\n",
"`marginals` supports some additional arguments like `title`. It also forward any extra arguments to the underlying plot functions, in this example, `grid`. Note that the arguments are passed to *all* three subplots."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7",
"metadata": {},
"outputs": [],
"source": [
"pp.marginals(da, title=\"My plot\", grid=True)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
1 change: 1 addition & 0 deletions src/plopp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
'plotting': [
'DimensionSlicer',
'inspector',
'marginals',
'mesh3d',
'plot',
'scatter',
Expand Down
2 changes: 2 additions & 0 deletions src/plopp/plotting/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Copyright (c) 2025 Scipp contributors (https://github.com/scipp)

from ._inspector import inspector
from ._marginals import marginals
from ._mesh3d import mesh3d
from ._plot import plot
from ._scatter import scatter
Expand All @@ -13,6 +14,7 @@ from ._xyplot import xyplot
__all__ = [
'DimensionSlicer',
'inspector',
'marginals',
'mesh3d',
'plot',
'scatter',
Expand Down
128 changes: 128 additions & 0 deletions src/plopp/plotting/_marginals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2026 Scipp contributors (https://github.com/scipp)

from functools import partial
from typing import Any, Literal

import matplotlib.pyplot as plt
import scipp as sc
from mpl_toolkits.axes_grid1 import make_axes_locatable

from ..backends.matplotlib.utils import make_figure
from ..core import Node
from ..core.typing import FigureLike
from ..graphics import imagefigure, linefigure
from .common import input_to_nodes, preprocess


def marginals(
data: sc.DataArray | Node,
*,
side: Literal["left", "right"] = "left",
title: str = "",
cbar: bool = True,
ax: plt.Axes | None = None,
ignore_size: bool = False,
ignore_sorted: bool = False,
coords=None,
**plot_kwargs: Any,
) -> FigureLike:
"""Plot 2D data and the sums over each dim as a 1D plot.

This function plots ``data`` as a 2D image with a colorbar.
It also sums over each of its dimensions and plots the resulting 1D data
next to and above the main image.

.. versionadded:: 26.11.0

Parameters
----------
data:
The data to plot. Must be 2-dimensional.
side:
Which side to show the 1D plot along the y-axis.
Note that when ``side == "left"``, the x-axis of the side plot is inverted,
i.e., it increases from right to left.
title:
Title of the plot.
cbar:
Whether to use a colorbar.
ax:
Optional axes to plot into. If None, ``marginals_plot`` creates new Axes.
ignore_size:
If ``True``, skip the check that prevents the rendering of very large data.
ignore_sorted:
Do not check if the coordinates are sorted before plotting.
coords:
If supplied, use these coords instead of the input's dimension coordinates.
**plot_kwargs:
Forwarded to *all* subplots :func:`plopp.graphics.linefigure`,
and :func:`plopp.graphics.imagefigure`.

Returns
-------
:
A figure that contains all axes.
"""
if ax is None:
ax_2d = make_figure().subplots()
else:
ax_2d = ax

divider = make_axes_locatable(ax_2d)
ax_top = divider.append_axes("top", 1.2, pad=0.1, sharex=ax_2d)
ax_side = divider.append_axes(side, 1.2, pad=0.1, sharey=ax_2d)
cax = divider.append_axes("right", "4%", pad="5%") if cbar else None

main_data_node = input_to_nodes(
data,
processor=partial(
preprocess,
ignore_size=ignore_size,
ignore_sorted=ignore_sorted,
coords=coords,
),
)

if len(main_data_node) > 1:
raise ValueError("The marginals plot accepts only a single data input.")
main_data_node = main_data_node[0]

fig = imagefigure(main_data_node, ax=ax_2d, cax=cax, cbar=cbar, **plot_kwargs)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're tired of trying to get this working for everything, I can take over.

I just realised that the plot won't work for inputs that are missing coords (and probably other things).
We can either just use plot instead of imagefigure and linefigure as they also accept nodes as inputs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worked around one issue when building the side data. But plot fails because the coord is unsorted. How would you deal with that? (Feel free to push a change if that is easier)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed an update with a flag to suppress the unsorted coord warning.
Now it also works with raw numpy arrays as inputs.


# Main plot (2D)
y_label = ax_2d.get_ylabel()
if side == "left":
ax_2d.yaxis.set_tick_params(labelleft=False)
ax_2d.set_ylabel("")

# Top Plot
top_node = Node(lambda da: da.sum(da.dims[0]), main_data_node)
linefigure(top_node, ax=ax_top, title=title, **plot_kwargs)
ax_top.xaxis.set_tick_params(labelbottom=False)
ax_top.set_xlabel("")

# Side plot
def make_side_data(da: sc.DataArray) -> sc.DataArray:
if da.coords.is_edges(da.dims[0]):
raise sc.DimensionError(
"marginals() does not support bin edges along the y-axis."
)
summed = da.sum(da.dims[1])
return sc.DataArray(
data=summed.coords[summed.dim],
# rename to work around https://github.com/scipp/plopp/issues/616.
coords={summed.dim: summed.data},
).rename({summed.dim: " "})

side_node = Node(make_side_data, main_data_node)
linefigure(side_node, ax=ax_side, **plot_kwargs)
ax_side.set_xlabel(ax_top.get_ylabel())
if side == "left":
ax_side.set_ylabel(y_label)
ax_side.invert_xaxis()
else:
ax_side.yaxis.set_tick_params(labelleft=False)
ax_side.set_ylabel("")

return fig
11 changes: 10 additions & 1 deletion src/plopp/plotting/_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def plot(
figsize: tuple[float, float] | None = None,
grid: bool = False,
ignore_size: bool = False,
ignore_sorted: bool = False,
legend: bool | tuple[float, float] = True,
hide_log_buttons: bool = False,
logc: bool | None = None,
Expand Down Expand Up @@ -85,6 +86,8 @@ def plot(
If ``True``, the interactive log buttons will be hidden.
ignore_size:
If ``True``, skip the check that prevents the rendering of very large data.
ignore_sorted:
Do not check if the coordinates are sorted before plotting.
legend:
Show legend if ``True``. If ``legend`` is a tuple, it should contain the
``(x, y)`` coordinates of the legend's anchor point in axes coordinates.
Expand Down Expand Up @@ -170,7 +173,13 @@ def plot(
)

nodes = input_to_nodes(
obj, processor=partial(preprocess, ignore_size=ignore_size, coords=coords)
obj,
processor=partial(
preprocess,
ignore_size=ignore_size,
ignore_sorted=ignore_sorted,
coords=coords,
),
)

ndims = set()
Expand Down
Loading
Loading