Skip to content

Commit

Permalink
feat: HistStack for #169 (#244)
Browse files Browse the repository at this point in the history
* feat: begin histstack

* Update .pre-commit-config.yaml

* feat: make HistStack work

* Modify checks for categorical axis

* Add check for matching axes types

* feat & test: add a judgement for Hist.plot and tests for stacks

* fix: skip Stack tests for Python 3.6

* fix: make axes match for Stack

* feat: change Stack plotting implementation

* test: remove tests for stack with different axes

* feat: check mplhep dependency and allow Stack(unnamed_hist, named_hist)

* feat: allow stack(ax1, ax2) but not allow plot, and without tests

* fix: change the axes check back

* feat: h.stack(name/idx)

* test: add test for Stack reprs

* test: add some tests for h.stack and Stack(axes)

* fix: solve the circular import problem

* refactor: working on Stack

Co-authored-by: Aman Goel <[email protected]>
Co-authored-by: Henry Schreiner <[email protected]>
  • Loading branch information
3 people authored Jul 7, 2021
1 parent b22d4dc commit 5d9edac
Show file tree
Hide file tree
Showing 6 changed files with 474 additions and 2 deletions.
170 changes: 170 additions & 0 deletions notebooks/HistStack.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0ada0219-170a-418a-a6a5-b241b9b9fe42",
"metadata": {},
"source": [
"# HistStack"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "6ed3ec2c-9d11-4c69-b7ce-0365079b22ff",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[StairsArtists(stairs=<matplotlib.patches.StepPatch object at 0x17c000e80>, errorbar=<ErrorbarContainer object of 3 artists>, legend_artist=<ErrorbarContainer object of 3 artists>),\n",
" StairsArtists(stairs=<matplotlib.patches.StepPatch object at 0x17c045af0>, errorbar=<ErrorbarContainer object of 3 artists>, legend_artist=<ErrorbarContainer object of 3 artists>),\n",
" StairsArtists(stairs=<matplotlib.patches.StepPatch object at 0x17c0687f0>, errorbar=<ErrorbarContainer object of 3 artists>, legend_artist=<ErrorbarContainer object of 3 artists>)]"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"from hist import Hist, Stack, axis, NamedHist, BaseHist\n",
"import numpy as np\n",
"\n",
"ax = axis.Regular(50, -5, 5, underflow=False, overflow=False)\n",
"\n",
"h1 = Hist(ax).fill(2 * np.random.normal(size=500) + 2 * np.ones((500,)))\n",
"\n",
"h2 = Hist(ax).fill(2 * np.random.normal(size=500) - 2 * np.ones((500,)))\n",
"\n",
"h3 = Hist(ax).fill(np.random.normal(size=600))\n",
"\n",
"Stack(h1, h2, h3).plot()"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "f8d441be-c7aa-4b5e-93a0-8dd2647b6eca",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Stack[\"Regular(50, -5, 5, underflow=False, overflow=False, name='A', label='a [unit]')\", \"Regular(50, -5, 5, underflow=False, overflow=False, name='A', label='a [unit]')\"]\n",
"Stack[\"NamedHist(\\n Regular(50, -5, 5, underflow=False, overflow=False, name='A', label='a [unit]'),\\n Regular(50, -5, 5, underflow=False, overflow=False, name='B', label='b [unit]'),\\n storage=Double())\"]\n"
]
}
],
"source": [
"named_ax1 = axis.Regular(\n",
" 50, -5, 5, name=\"A\", label=\"a [unit]\", underflow=False, overflow=False\n",
")\n",
"named_ax2 = axis.Regular(\n",
" 50, -5, 5, name=\"B\", label=\"b [unit]\", underflow=False, overflow=False\n",
")\n",
"h4 = NamedHist(named_ax1, named_ax2)\n",
"print(repr(Stack(named_ax1, named_ax1))) # not plotable\n",
"print(repr(Stack(h4))) # plotable"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "379e0186-6cc7-4cec-baa1-340a4821bd85",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Stack[\"Regular(50, -5, 5, underflow=False, overflow=False, name='B', label='b [unit]')\", \"Regular(50, -5, 5, underflow=False, overflow=False, name='B', label='b [unit]')\"]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"h4.stack(1, 1) # h4.stack(0, 1) could not work as names are different"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "a5fef029-3d52-46f9-91ed-6ad204ac87c7",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Stack[\"Regular(50, -5, 5, underflow=False, overflow=False, name='B', label='b [unit]')\", \"Regular(50, -5, 5, underflow=False, overflow=False, name='B', label='b [unit]')\"]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"h4.stack(\"B\", \"B\")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "20770a1c-4426-4a23-977e-9cc7f1238d24",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Stack[\"Hist(Regular(50, -5, 5, name='C', label='C'), storage=Double())\", \"Hist(Regular(50, -5, 5, name='C', label='C'), storage=Double())\"]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"h5 = Hist.new.Reg(50, -5, 5, name=\"C\").StrCat([\"one\", \"two\"], name=\"y\").Double()\n",
"Stack(h5[:, \"one\"], h5[:, \"two\"])"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "hist",
"language": "python",
"name": "hist"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
2 changes: 2 additions & 0 deletions src/hist/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .basehist import BaseHist
from .hist import Hist
from .namedhist import NamedHist
from .stack import Stack
from .tag import loc, overflow, rebin, sum, underflow

# Convenient access to the version number
Expand All @@ -21,6 +22,7 @@
"Hist",
"BaseHist",
"NamedHist",
"Stack",
"accumulators",
"axis",
"loc",
Expand Down
19 changes: 17 additions & 2 deletions src/hist/basehist.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import functools
import operator
import typing
import warnings
from typing import (
TYPE_CHECKING,
Any,
Callable,
Dict,
Iterator,
List,
Mapping,
Optional,
Expand All @@ -29,7 +30,7 @@
from .svgplots import html_hist, svg_hist_1d, svg_hist_1d_c, svg_hist_2d, svg_hist_nd
from .typing import ArrayLike, SupportsIndex

if TYPE_CHECKING:
if typing.TYPE_CHECKING:
from builtins import ellipsis

import matplotlib.axes
Expand Down Expand Up @@ -470,3 +471,17 @@ def plot_pie(
import hist.plot

return hist.plot.plot_pie(self, ax=ax, **kwargs)

def stack(self, axis: Union[int, str]) -> "hist.stack.Stack":
"""
Returns a stack from a normal histogram axes.
"""
if self.ndim < 2:
raise RuntimeError("Cannot stack with less than two axis")
stack_histograms: Iterator[BaseHist] = (
self[{axis: i}] for i in range(len(self.axes[axis])) # type: ignore
)
for name, h in zip(self.axes[axis], stack_histograms):
h.name = name # type: ignore

return hist.stack.Stack(*stack_histograms)
64 changes: 64 additions & 0 deletions src/hist/stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import sys
import typing
from typing import Any, Iterator, List, Tuple, Union

from .basehist import BaseHist

if typing.TYPE_CHECKING:
from mplhep.plot import Hist1DArtists


class Stack:
def __init__(
self,
*args: BaseHist,
) -> None:
"""
Initialize Stack of histograms.
"""

self._stack = args

if len(args) == 0:
raise ValueError("There should be histograms in the Stack")

if not all(isinstance(a, BaseHist) for a in args):
raise ValueError("There should be only histograms in Stack")

first_axes = args[0].axes
for a in args[1:]:
if first_axes != a.axes:
raise ValueError("The Histogram axes don't match")

def __repr__(self) -> str:
str_stack = ", ".join(repr(h) for h in self._stack)
return f"{self.__class__.__name__}({str_stack})"

def __getitem__(
self, val: Union[int, slice]
) -> Union[BaseHist, Tuple[BaseHist, ...]]:
return self._stack.__getitem__(val)

def __iter__(self) -> Iterator[BaseHist]:
return iter(self._stack)

def plot(self, *, overlay: None = None, **kwargs: Any) -> "List[Hist1DArtists]":
"""
Plot method for Stack object.
"""
if overlay is not None:
raise NotImplementedError("Currently overlay is not supported")

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

try:
import mplhep.plot
except ModuleNotFoundError:
print(
f"{self.__class__.__name__}.plot() requires mplhep to plot, either install hist[plot] or mplhep",
file=sys.stderr,
)
raise

return mplhep.plot.histplot(list(self._stack), **kwargs) # type: ignore
15 changes: 15 additions & 0 deletions tests/test_reprs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from hist import Hist, Stack, axis


def test_1D_empty_repr(named_hist):

h = named_hist.new.Reg(10, -1, 1, name="x", label="y").Double()
Expand Down Expand Up @@ -83,3 +86,15 @@ def test_ND_empty_repr(named_hist):
assert "label='y'" in repr(h)
assert "label='q'" in repr(h)
assert "label='b'" in repr(h)


def test_stack_repr(named_hist):

a1 = axis.Regular(
50, -5, 5, name="A", label="a [unit]", underflow=False, overflow=False
)
a2 = axis.Regular(
50, -5, 5, name="A", label="a [unit]", underflow=False, overflow=False
)
assert "name='A'" in repr(Stack(Hist(a1), Hist(a2)))
assert "label='a [unit]'" in repr(Stack(Hist(a1), Hist(a2)))
Loading

0 comments on commit 5d9edac

Please sign in to comment.