Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: HistStack followup #291

Merged
merged 24 commits into from
Aug 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a2c9b51
feat: move legend into s.plot
LovelyBuggies Aug 11, 2021
0c8d16c
fix: allow passing axes
LovelyBuggies Aug 20, 2021
12cd3d0
test: omit the AxesSubplot in kwargs
LovelyBuggies Aug 20, 2021
66f29d2
feat: add changelog and s.axes
LovelyBuggies Aug 20, 2021
f5dba4b
chore: upgrade histoprint to 2.2.0
amangoel185 Aug 23, 2021
9a817c1
feat: add histoprint support to stacks
amangoel185 Aug 23, 2021
49c023b
docs: add histoprint example for stacks
amangoel185 Aug 23, 2021
0bfb6c0
feat: plot keyword draft
LovelyBuggies Aug 24, 2021
fb63c29
docs: add support information for histoprint
amangoel185 Aug 26, 2021
9ffa4a2
feat: move legend into s.plot
LovelyBuggies Aug 11, 2021
1c6e43d
fix: allow passing axes
LovelyBuggies Aug 20, 2021
4016aa0
test: omit the AxesSubplot in kwargs
LovelyBuggies Aug 20, 2021
63469f6
feat: add changelog and s.axes
LovelyBuggies Aug 20, 2021
c6db297
chore: upgrade histoprint to 2.2.0
amangoel185 Aug 23, 2021
1f3d6f7
feat: add histoprint support to stacks
amangoel185 Aug 23, 2021
ef0166a
docs: add histoprint example for stacks
amangoel185 Aug 23, 2021
e78df47
feat: plot keyword draft
LovelyBuggies Aug 24, 2021
81e18b9
docs: add support information for histoprint
amangoel185 Aug 26, 2021
1e2cdfe
Merge branch 'nino/feat/histstack-followup' of github.com:scikit-hep/…
amangoel185 Aug 26, 2021
a3714e1
chore: trigger GitHub checks
amangoel185 Aug 26, 2021
ca31b77
feat: plot keyword wrapper finished
LovelyBuggies Aug 27, 2021
11402de
Merge branch 'main' into nino/feat/histstack-followup
LovelyBuggies Aug 27, 2021
f271eb7
Merge branch 'main' into nino/feat/histstack-followup
henryiii Aug 27, 2021
6118a5e
fix: address issues with tests
henryiii Aug 27, 2021
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
2 changes: 1 addition & 1 deletion dev-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ dependencies:
- uncertainties >=3
- pip:
- mplhep>=0.1.27
- histoprint>=1.4.0
- histoprint>=2.2.0
- -e .
6 changes: 6 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ Version 2.5.0
`#281 <https://github.com/scikit-hep/hist/pull/281>`_


Smaller features or fixes:

* Support showing histogram titles in the legend, ``Stack.axes``, and histoprint for HistStack.
`#291 <https://github.com/scikit-hep/hist/pull/291>`_


Version 2.4.0
--------------------

Expand Down
21 changes: 19 additions & 2 deletions docs/user-guide/notebooks/Stack.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@
"outputs": [],
"metadata": {}
},
{
"cell_type": "markdown",
"source": [
"We can use `.show()` to access `histoprint` and print the stacked histograms to the console.\n",
"\n",
"Note: Histoprint currently supports only non-discrete axes. Hence, it supports only regular and variable axes at the moment."
],
"metadata": {}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"s.show(columns=50)"
],
"outputs": [],
"metadata": {}
},
{
"cell_type": "markdown",
"source": [
Expand Down Expand Up @@ -77,7 +95,6 @@
"# Turn an existin axis into a stack\n",
"s = h.stack(\"quality\")\n",
"s[::-1].plot(stack=True, histtype=\"fill\", color=[\"indianred\", \"steelblue\"], alpha=0.8)\n",
"plt.legend()\n",
"plt.show()"
],
"outputs": [],
Expand Down Expand Up @@ -127,7 +144,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.4"
"version": "3.9.6"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def lint(session):
session.run("pre-commit", "run", "--all-files", *session.posargs)


@nox.session(python=ALL_PYTHONS, reuse_venv=True)
@nox.session(python=ALL_PYTHONS)
def tests(session):
"""
Run the unit and regular tests.
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ project_urls =
packages = find:
install_requires =
boost-histogram~=1.1.0
histoprint>=1.6
histoprint>=2.2.0
numpy>=1.14.5
typing_extensions;python_version<"3.8"
python_requires = >=3.7
Expand Down
34 changes: 34 additions & 0 deletions src/hist/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,18 @@ def fnll(v: Iterable[np.typing.NDArray[Any]]) -> float:
return tuple(popt), pcov


def _plot_keywords_wrapper(ax: matplotlib.axes.Axes, legend: bool | None) -> None:
"""
Pandas-like wrapper that wrap several useful mpl keyword arguments.
"""
# Todo: more keywords here
if legend:
if ax.get_legend_handles_labels()[0]:
legend = ax.legend()
else:
raise ValueError("No labels to legend")


def plot2d_full(
self: hist.BaseHist,
*,
Expand Down Expand Up @@ -698,3 +710,25 @@ def plot_pie(
labels = [str(get_center(x)) for x in self.axes[0]]

return ax.pie(data, labels=labels, **kwargs)


def plot_stack(
self: hist.stack.Stack,
*,
ax: matplotlib.axes.Axes | None = None,
legend: bool | None = False,
**kwargs: Any,
) -> Any:

if self[0].ndim != 1:
raise NotImplementedError("Please project to 1D before calling plot")

if "label" not in kwargs:
if all(h.name is not None for h in self):
kwargs["label"] = [h.name for h in self]

ret = histplot(list(self), **kwargs)
ax = ret[0].stairs.axes
_plot_keywords_wrapper(ax, legend=legend)

return ret
32 changes: 24 additions & 8 deletions src/hist/stack.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
from __future__ import annotations

import sys
import typing
from typing import Any, Iterator, TypeVar

import histoprint

from .axestuple import NamedAxesTuple
from .basehist import BaseHist

if typing.TYPE_CHECKING:
from mplhep.plot import Hist1DArtists
try:
import matplotlib
except ModuleNotFoundError:
print(
"Hist requires mplhep to plot, either install hist[plot] or mplhep",
file=sys.stderr,
)
raise

__all__ = ("Stack",)

Expand Down Expand Up @@ -59,22 +69,28 @@ def __repr__(self) -> str:
str_stack = ", ".join(repr(h) for h in self)
return f"{self.__class__.__name__}({str_stack})"

def plot(self, **kwargs: Any) -> list[Hist1DArtists]:
@property
def axes(self) -> NamedAxesTuple:
return self._stack[0].axes

def plot(self, *, ax: matplotlib.axes.Axes | None = None, **kwargs: Any) -> Any:
"""
Plot method for Stack object.
"""

import hist.plot

if self[0].ndim != 1:
raise NotImplementedError("Please project to 1D before calling plot")
return hist.plot.plot_stack(self, ax=ax, **kwargs)

def show(self, **kwargs: Any) -> Any:
"""
Pretty print the stacked histograms to the console.
"""
if "label" not in kwargs:
# TODO: add .name to static typing. And runtime, for that matter.
if all(getattr(h, "name", None) is not None for h in self):
if all(h.name is not None for h in self):
kwargs["label"] = [h.name for h in self]

return hist.plot.histplot(list(self), **kwargs) # type: ignore
return histoprint.print_hist(list(self), stack=True, **kwargs)


def __dir__() -> tuple[str, ...]:
Expand Down
33 changes: 23 additions & 10 deletions tests/test_stacks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from unittest.mock import MagicMock

import numpy as np
import pytest

Expand Down Expand Up @@ -78,6 +80,9 @@ def test_stack_init(hist_1d):
assert stack[0] == h1
assert stack[1] == h2
assert stack[2] == h3
assert stack.axes == h1.axes
assert stack.axes == h2.axes
assert stack.axes == h3.axes

assert tuple(stack) == (h1, h2, h3)

Expand Down Expand Up @@ -111,12 +116,19 @@ def test_stack_constructor_fails():
Stack(int_hist, int_cat_hist, str_cat_hist)

# allow to construct stack with 2d histograms
Stack(reg_hist_2d, reg_hist_2d, reg_hist_2d)
Stack(boo_hist_2d, boo_hist_2d, boo_hist_2d)
Stack(var_hist_2d, var_hist_2d, var_hist_2d)
Stack(int_hist_2d, int_hist_2d, int_hist_2d)
Stack(int_cat_hist_2d, int_cat_hist_2d, int_cat_hist_2d)
Stack(str_cat_hist_2d, str_cat_hist_2d, str_cat_hist_2d)
s1 = Stack(reg_hist_2d, reg_hist_2d, reg_hist_2d)
s2 = Stack(boo_hist_2d, boo_hist_2d, boo_hist_2d)
s3 = Stack(var_hist_2d, var_hist_2d, var_hist_2d)
s4 = Stack(int_hist_2d, int_hist_2d, int_hist_2d)
s5 = Stack(int_cat_hist_2d, int_cat_hist_2d, int_cat_hist_2d)
s6 = Stack(str_cat_hist_2d, str_cat_hist_2d, str_cat_hist_2d)

assert s1.axes == reg_hist_2d.axes
assert s2.axes == boo_hist_2d.axes
assert s3.axes == var_hist_2d.axes
assert s4.axes == int_hist_2d.axes
assert s5.axes == int_cat_hist_2d.axes
assert s6.axes == str_cat_hist_2d.axes

# not allow to construct stack with different ndim
with pytest.raises(Exception):
Expand Down Expand Up @@ -219,21 +231,22 @@ def test_stack_method():


def collect(*args, **kwargs):
return args, kwargs
return MagicMock(), args, kwargs


def test_stack_plot(monkeypatch):
import hist.plot

monkeypatch.setattr(hist.plot, "histplot", collect)
monkeypatch.setattr(hist.plot, "_plot_keywords_wrapper", collect)

h = Hist.new.Regular(10, 0, 1).StrCategory(["one", "two"], name="str").Double()
s = h.stack(1)

args, kwargs = s.plot(silly=...)
_, args, kwargs = s.plot(silly=...)

assert len(s) == 2
assert len(list(s)) == 2

assert args == (list(s),)
assert kwargs == {"label": ["one", "two"], "silly": ...}
assert kwargs["label"] == ["one", "two"]
assert kwargs["silly"] == ...