Skip to content

Commit e665505

Browse files
committed
wip
1 parent cf862b7 commit e665505

File tree

5 files changed

+90
-21
lines changed

5 files changed

+90
-21
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,6 @@ venv.bak/
127127
/docs/source/_generated/
128128
/tmp/
129129
/docs/source/_static/_generated/
130+
# pixi environments
131+
.pixi/*
132+
!.pixi/config.toml

pyproject.toml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,42 @@ flake8-builtins.ignorelist = ["copyright", "range"]
215215
"{new_docs,docs,examples}/**" = ["INP001", "S", "T20", "LOG015"]
216216
"src/mplhep/_dev.py" = ["T20"]
217217
"src/mplhep/styles/*.py" = ["FLY002"]
218+
219+
[tool.pixi.workspace]
220+
channels = ["conda-forge"]
221+
platforms = ["linux-64", "osx-64", "osx-arm64", "win-64"]
222+
223+
[tool.pixi.dependencies]
224+
matplotlib = "==3.10.6"
225+
numpy = ">=1.16.0"
226+
packaging = "*"
227+
pytest = ">=6.0"
228+
229+
[tool.pixi.pypi-dependencies]
230+
mplhep = { path = ".", editable = true }
231+
mplhep-data = ">=0.0.4"
232+
uhi = ">=0.2.0"
233+
boost_histogram = "*"
234+
hist = "*"
235+
pytest-mock = "*"
236+
pytest-mpl = "*"
237+
pytest-xdist = "*"
238+
pytest-benchmark = "*"
239+
scikit-hep-testdata = "*"
240+
uproot = "*"
241+
uproot4 = "*"
242+
243+
[tool.pixi.environments]
244+
default = {features = ["all"], solve-group = "default" }
245+
all = { features = ["all"], solve-group = "default" }
246+
dev = { features = ["dev"], solve-group = "default" }
247+
docs = { features = ["docs"], solve-group = "default" }
248+
test = { features = ["test"], solve-group = "default" }
249+
250+
[tool.pixi.tasks]
251+
install = "echo 'Package is already installed in editable mode'"
252+
test = "pytest"
253+
lint = "ruff check src"
254+
format = "ruff format src"
255+
prek = "prek run --all-files"
256+
pre-commit = "prek run --all-files"

src/mplhep/comparison_plotters.py

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -279,17 +279,26 @@ def comparison(
279279
if flow == "show":
280280
# Try to get flow bins if they exist (from original histogram objects, not plottable)
281281
try:
282-
# Access flow bins from the original histogram objects
283-
h1_flow_values = h1.values(flow=True)
284-
285-
# Check if histogram actually has flow bins (length should be +2)
286-
if len(h1_flow_values) == len(h1_plottable.values()) + 2:
287-
# Use the original histograms which already have flow bins
288-
h1_for_comparison = h1
289-
h2_for_comparison = h2
290-
used_flow_bins = True
282+
# First check if both histograms support flow=True without actually calling it
283+
# This prevents side effects from accessing flow values on histograms that don't have them
284+
if (hasattr(h1, 'values') and hasattr(h2, 'values') and
285+
hasattr(h1.values, '__call__') and hasattr(h2.values, '__call__')):
286+
287+
# Access flow bins from the original histogram objects
288+
h1_flow_values = h1.values(flow=True)
289+
290+
# Check if histogram actually has flow bins (length should be +2)
291+
if len(h1_flow_values) == len(h1_plottable.values()) + 2:
292+
# Use the original histograms which already have flow bins
293+
h1_for_comparison = h1
294+
h2_for_comparison = h2
295+
used_flow_bins = True
296+
else:
297+
# No actual flow bins, use regular histograms
298+
h1_for_comparison = h1_plottable
299+
h2_for_comparison = h2_plottable
291300
else:
292-
# No actual flow bins, use regular histograms
301+
# Histograms don't support flow parameter
293302
h1_for_comparison = h1_plottable
294303
h2_for_comparison = h2_plottable
295304
except (AttributeError, TypeError):
@@ -349,13 +358,15 @@ def comparison(
349358
0.05 * (final_bins[-1] - final_bins[0]), np.mean(np.diff(final_bins))
350359
)
351360

352-
# Check if underflow/overflow bins exist and extend edges accordingly
361+
# For flow="show", we need to always extend edges to match flow values length
362+
# because flow=True always includes underflow/overflow positions
353363
flow_edges = np.copy(final_bins)
354364
h2_flow_values = h2_for_comparison.values(flow=True)
355-
if h2_flow_values[0] > 0: # Underflow exists
356-
flow_edges = np.insert(flow_edges, 0, flow_edges[0] - _flow_bin_size)
357-
if h2_flow_values[-1] > 0: # Overflow exists
358-
flow_edges = np.append(flow_edges, flow_edges[-1] + _flow_bin_size)
365+
366+
# Always add underflow and overflow edges when using flow="show"
367+
# to match the structure of flow=True values
368+
flow_edges = np.insert(flow_edges, 0, flow_edges[0] - _flow_bin_size)
369+
flow_edges = np.append(flow_edges, flow_edges[-1] + _flow_bin_size)
359370

360371
comparison_plottable = EnhancedPlottableHistogram(
361372
comparison_values,
@@ -389,14 +400,20 @@ def comparison(
389400
if hasattr(comparison_plottable, "errors"):
390401
comparison_plottable.errors()
391402

403+
# Filter out comparison-specific parameters that shouldn't be passed to histplot
404+
_valid_histplot_kwargs = {
405+
k: v for k, v in histplot_kwargs.items()
406+
if k not in ['ratio', 'comparison', 'comparison_ylabel', 'comparison_ylim']
407+
}
408+
392409
if comparison == "pull":
393-
histplot_kwargs.setdefault("histtype", "fill")
394-
histplot_kwargs.setdefault("color", "darkgrey")
395-
histplot(comparison_plottable, ax=ax, flow=flow, **histplot_kwargs)
410+
_valid_histplot_kwargs.setdefault("histtype", "fill")
411+
_valid_histplot_kwargs.setdefault("color", "darkgrey")
412+
histplot(comparison_plottable, ax=ax, flow=flow, **_valid_histplot_kwargs)
396413
else:
397-
histplot_kwargs.setdefault("color", "black")
398-
histplot_kwargs.setdefault("histtype", "errorbar")
399-
histplot(comparison_plottable, ax=ax, flow=flow, **histplot_kwargs)
414+
_valid_histplot_kwargs.setdefault("color", "black")
415+
_valid_histplot_kwargs.setdefault("histtype", "errorbar")
416+
histplot(comparison_plottable, ax=ax, flow=flow, **_valid_histplot_kwargs)
400417

401418
if comparison in ["ratio", "split_ratio", "relative_difference"]:
402419
if comparison_ylim is None:

src/mplhep/plot.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,16 @@ def histplot(
391391
edges = bool(edges)
392392
binticks = bool(binticks)
393393

394+
# Handle ratio plots - redirect to comparison functionality
395+
if kwargs.get('ratio', False):
396+
from .comparison_plotters import hists
397+
# Remove ratio from kwargs before passing to comparison function
398+
_kwargs = {k: v for k, v in kwargs.items() if k != 'ratio'}
399+
if isinstance(H, (list, tuple)) and len(H) >= 2:
400+
return hists(H[0], H[1], comparison="ratio", flow=flow, **_kwargs)
401+
else:
402+
raise ValueError("ratio=True requires at least 2 histograms for comparison")
403+
394404
# Process input
395405
hists = list(process_histogram_parts(H, bins))
396406
final_bins, xtick_labels = _get_plottable_protocol_bins(hists[0].axes[0])
-262 Bytes
Loading

0 commit comments

Comments
 (0)