FUCKROOT 0.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fuckroot-0.0.0/FUCKROOT/HEP_PLOT/HepFigure.py +395 -0
- fuckroot-0.0.0/FUCKROOT/HEP_PLOT/__init__.py +36 -0
- fuckroot-0.0.0/FUCKROOT/HEP_PLOT/lhcbStyle.C +195 -0
- fuckroot-0.0.0/FUCKROOT/ROOT_BASE/ROOT.py +106 -0
- fuckroot-0.0.0/FUCKROOT/ROOT_BASE/Tree.py +79 -0
- fuckroot-0.0.0/FUCKROOT/ROOT_BASE/__init__.py +4 -0
- fuckroot-0.0.0/FUCKROOT/__init__.py +4 -0
- fuckroot-0.0.0/FUCKROOT.egg-info/PKG-INFO +135 -0
- fuckroot-0.0.0/FUCKROOT.egg-info/SOURCES.txt +14 -0
- fuckroot-0.0.0/FUCKROOT.egg-info/dependency_links.txt +1 -0
- fuckroot-0.0.0/FUCKROOT.egg-info/requires.txt +5 -0
- fuckroot-0.0.0/FUCKROOT.egg-info/top_level.txt +1 -0
- fuckroot-0.0.0/PKG-INFO +135 -0
- fuckroot-0.0.0/README.md +123 -0
- fuckroot-0.0.0/pyproject.toml +28 -0
- fuckroot-0.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
from matplotlib.axes import Axes
|
|
2
|
+
from matplotlib.projections import register_projection
|
|
3
|
+
from matplotlib.ticker import AutoMinorLocator
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
import numpy as np
|
|
6
|
+
from tqdm import tqdm
|
|
7
|
+
import matplotlib
|
|
8
|
+
import warnings
|
|
9
|
+
# matplotlib.use("tkAgg")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
LHCB_FONT_FAMILY = "Times New Roman"
|
|
13
|
+
LHCB_LINE_WIDTH = 1.0
|
|
14
|
+
LHCB_TEXT_SIZE = 14
|
|
15
|
+
LHCB_MAJOR_TICK_LENGTH = 8
|
|
16
|
+
LHCB_MINOR_TICK_LENGTH = 4
|
|
17
|
+
# Follow named ROOT color order: black, red, green, blue, yellow, magenta, cyan, purple.
|
|
18
|
+
LHCB_COLOR_CYCLE = ["black", "red", "green", "blue", "yellow", "magenta", "cyan", "purple"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class HepAxes(Axes):
|
|
22
|
+
name = "hep"
|
|
23
|
+
|
|
24
|
+
def __init__(self, *args, **kwargs):
|
|
25
|
+
self._hep_font_family = LHCB_FONT_FAMILY
|
|
26
|
+
super().__init__(*args, **kwargs)
|
|
27
|
+
self._apply_lhcb_style()
|
|
28
|
+
self.hist_datas = []
|
|
29
|
+
self._hist_artists = []
|
|
30
|
+
self.grid(True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _apply_lhcb_style(self):
|
|
35
|
+
# ROOT lhcbStyle-like appearance, applied only to this projection.
|
|
36
|
+
self.set_facecolor("white")
|
|
37
|
+
self.figure.set_facecolor("white")
|
|
38
|
+
|
|
39
|
+
# Add both major and minor ticks on x/y axes.
|
|
40
|
+
self.xaxis.set_minor_locator(AutoMinorLocator())
|
|
41
|
+
self.yaxis.set_minor_locator(AutoMinorLocator())
|
|
42
|
+
self.minorticks_on()
|
|
43
|
+
|
|
44
|
+
self.tick_params(
|
|
45
|
+
axis="both",
|
|
46
|
+
which="major",
|
|
47
|
+
direction="in",
|
|
48
|
+
top=True,
|
|
49
|
+
right=True,
|
|
50
|
+
width=LHCB_LINE_WIDTH,
|
|
51
|
+
length=LHCB_MAJOR_TICK_LENGTH,
|
|
52
|
+
labelsize=LHCB_TEXT_SIZE,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self.tick_params(
|
|
56
|
+
axis="both",
|
|
57
|
+
which="minor",
|
|
58
|
+
direction="in",
|
|
59
|
+
top=True,
|
|
60
|
+
right=True,
|
|
61
|
+
width=0.8 * LHCB_LINE_WIDTH,
|
|
62
|
+
length=LHCB_MINOR_TICK_LENGTH,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
for spine in self.spines.values():
|
|
66
|
+
spine.set_linewidth(LHCB_LINE_WIDTH)
|
|
67
|
+
|
|
68
|
+
self.set_prop_cycle(color=LHCB_COLOR_CYCLE)
|
|
69
|
+
self.title.set_fontsize(1.2 * LHCB_TEXT_SIZE)
|
|
70
|
+
|
|
71
|
+
def plot(self, *args, **kwargs):
|
|
72
|
+
# Match lhcbStyle defaults for line and marker appearance.
|
|
73
|
+
kwargs.setdefault("linestyle", "-")
|
|
74
|
+
kwargs.setdefault("linewidth", LHCB_LINE_WIDTH)
|
|
75
|
+
kwargs.setdefault("marker", "o")
|
|
76
|
+
kwargs.setdefault("markersize", 4)
|
|
77
|
+
|
|
78
|
+
return super().plot(*args, **kwargs)
|
|
79
|
+
|
|
80
|
+
def set_title(self, label, *args, **kwargs):
|
|
81
|
+
# Match lhcbStyle title size and weight.
|
|
82
|
+
kwargs.setdefault("fontsize", 1.5 * LHCB_TEXT_SIZE)
|
|
83
|
+
# kwargs.setdefault("fontweight", "bold")
|
|
84
|
+
|
|
85
|
+
return super().set_title(label, *args, **kwargs)
|
|
86
|
+
|
|
87
|
+
def set_xlabel(self, xlabel, *args, **kwargs):
|
|
88
|
+
# Put x label on the right end of the axis.
|
|
89
|
+
kwargs.setdefault("loc", "right")
|
|
90
|
+
kwargs.setdefault("fontsize", 1.1 * LHCB_TEXT_SIZE)
|
|
91
|
+
|
|
92
|
+
return super().set_xlabel(xlabel, *args, **kwargs)
|
|
93
|
+
|
|
94
|
+
def set_ylabel(self, ylabel, *args, **kwargs):
|
|
95
|
+
# Keep y label vertical and move it to the top of the axis.
|
|
96
|
+
kwargs.setdefault("loc", "top")
|
|
97
|
+
kwargs.setdefault("rotation", 90)
|
|
98
|
+
kwargs.setdefault("fontsize", 1.1 * LHCB_TEXT_SIZE)
|
|
99
|
+
|
|
100
|
+
return super().set_ylabel(ylabel, *args, **kwargs)
|
|
101
|
+
|
|
102
|
+
def grid(self, visible=None, which="both", axis="both", **kwargs):
|
|
103
|
+
if visible is False:
|
|
104
|
+
if which in ("both", "major"):
|
|
105
|
+
super().grid(visible=False, which="major", axis=axis)
|
|
106
|
+
if which in ("both", "minor"):
|
|
107
|
+
super().grid(visible=False, which="minor", axis=axis)
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
if which in ("both", "major"):
|
|
111
|
+
major_kwargs = {
|
|
112
|
+
"linestyle": "-",
|
|
113
|
+
"linewidth": 0.8 * LHCB_LINE_WIDTH,
|
|
114
|
+
"alpha": 0.5,
|
|
115
|
+
}
|
|
116
|
+
major_kwargs.update(kwargs)
|
|
117
|
+
super().grid(visible=visible, which="major", axis=axis, **major_kwargs)
|
|
118
|
+
|
|
119
|
+
if which in ("both", "minor"):
|
|
120
|
+
minor_kwargs = {
|
|
121
|
+
"linestyle": "--",
|
|
122
|
+
"linewidth": 0.6 * LHCB_LINE_WIDTH,
|
|
123
|
+
"alpha": 0.25,
|
|
124
|
+
}
|
|
125
|
+
minor_kwargs.update(kwargs)
|
|
126
|
+
super().grid(visible=visible, which="minor", axis=axis, **minor_kwargs)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def hist(self, *args, stat=True, errors=True, **kwargs):
|
|
130
|
+
# Match lhcbStyle defaults for histogram appearance.
|
|
131
|
+
kwargs.setdefault("histtype", "step")
|
|
132
|
+
kwargs.setdefault("linewidth", LHCB_LINE_WIDTH)
|
|
133
|
+
label = kwargs.get("label", None)
|
|
134
|
+
if stat:
|
|
135
|
+
stat_text = self._format_hist_stat(args)
|
|
136
|
+
if label is None:
|
|
137
|
+
kwargs["label"] = stat_text
|
|
138
|
+
elif "$\\mu$=" not in label and "$\\sigma$=" not in label:
|
|
139
|
+
kwargs["label"] = f"{label} {stat_text}"
|
|
140
|
+
|
|
141
|
+
# remove stat and errors from kwargs before caching (will handle separately)
|
|
142
|
+
self.hist_datas.append({"args": args, "kwargs": kwargs.copy(), "errors": errors})
|
|
143
|
+
|
|
144
|
+
common_bins, common_range = self._get_common_hist_config()
|
|
145
|
+
|
|
146
|
+
# Remove old histogram artists before full redraw.
|
|
147
|
+
for artist in self._hist_artists:
|
|
148
|
+
if hasattr(artist, "remove"):
|
|
149
|
+
artist.remove()
|
|
150
|
+
self._hist_artists = []
|
|
151
|
+
|
|
152
|
+
# Reset property cycle so full redraw keeps stable color/marker order.
|
|
153
|
+
self.set_prop_cycle(color=LHCB_COLOR_CYCLE)
|
|
154
|
+
|
|
155
|
+
last_result = None
|
|
156
|
+
for hist_data in self.hist_datas:
|
|
157
|
+
draw_kwargs = hist_data["kwargs"].copy()
|
|
158
|
+
draw_kwargs["bins"] = common_bins
|
|
159
|
+
if common_range is not None:
|
|
160
|
+
draw_kwargs["range"] = common_range
|
|
161
|
+
else:
|
|
162
|
+
draw_kwargs.pop("range", None)
|
|
163
|
+
|
|
164
|
+
last_result = super().hist(*hist_data["args"], **draw_kwargs)
|
|
165
|
+
self._hist_artists.extend(self._flatten_artists(last_result[2]))
|
|
166
|
+
|
|
167
|
+
# Add error bars if requested
|
|
168
|
+
errors_type = hist_data.get("errors", None)
|
|
169
|
+
if errors_type is not False and errors_type is not None and last_result is not []:
|
|
170
|
+
error_artists = self._draw_hist_errors(
|
|
171
|
+
hist_data["args"], last_result, errors_type, **draw_kwargs
|
|
172
|
+
)
|
|
173
|
+
self._hist_artists.extend(error_artists)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
self.set_title("Histogram")
|
|
177
|
+
self.set_ylabel("Entries")
|
|
178
|
+
# self.set_xlabel("Value")
|
|
179
|
+
|
|
180
|
+
return last_result
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def twinx(self):
|
|
184
|
+
warnings.warn(
|
|
185
|
+
|
|
186
|
+
"\n\033[1;93m#### [NOT RECOMMENDED] HepAxes.twinx() have issues in current version ####\033[0m\n" \
|
|
187
|
+
"\033[1;93mtwin_ax do not share the same bin with the main ax, so histograms drawn on twin_ax will not be perfectly aligned with those on the main ax!!!\033[0m\n" \
|
|
188
|
+
"Will be fixed in future versions.",
|
|
189
|
+
UserWarning,
|
|
190
|
+
stacklevel=2,
|
|
191
|
+
)
|
|
192
|
+
# Create a twin axes with the same custom projection so hep-specific
|
|
193
|
+
# features (e.g. hist stat/errors kwargs) work on the right axis too.
|
|
194
|
+
twin = self._make_twin_axes(sharex=self, projection=self.name)
|
|
195
|
+
twin.yaxis.tick_right()
|
|
196
|
+
twin.yaxis.set_label_position('right')
|
|
197
|
+
twin.yaxis.set_offset_position('right')
|
|
198
|
+
twin.set_autoscalex_on(self.get_autoscalex_on())
|
|
199
|
+
self.yaxis.tick_left()
|
|
200
|
+
twin.xaxis.set_visible(False)
|
|
201
|
+
twin.patch.set_visible(False)
|
|
202
|
+
twin.xaxis.units = self.xaxis.units
|
|
203
|
+
|
|
204
|
+
twin._hep_font_family = self._hep_font_family
|
|
205
|
+
return twin
|
|
206
|
+
|
|
207
|
+
def _format_hist_stat(self, hist_args):
|
|
208
|
+
values = []
|
|
209
|
+
for dataset in self._extract_hist_datasets(hist_args):
|
|
210
|
+
finite = dataset[np.isfinite(dataset)]
|
|
211
|
+
if finite.size:
|
|
212
|
+
values.append(finite)
|
|
213
|
+
|
|
214
|
+
if not values:
|
|
215
|
+
return "(N=0), $\\mu$=nan, $\\sigma$=nan"
|
|
216
|
+
|
|
217
|
+
stacked = np.concatenate(values)
|
|
218
|
+
count = int(stacked.size)
|
|
219
|
+
mean = float(np.mean(stacked))
|
|
220
|
+
sigma = float(np.std(stacked))
|
|
221
|
+
return f"(N={count})\n$\\mu$={mean:.2g}, $\\sigma$={sigma:.2g}"
|
|
222
|
+
|
|
223
|
+
def _get_common_hist_config(self):
|
|
224
|
+
explicit_bins = None
|
|
225
|
+
explicit_range = None
|
|
226
|
+
for hist_data in self.hist_datas:
|
|
227
|
+
draw_kwargs = hist_data["kwargs"]
|
|
228
|
+
if "bins" in draw_kwargs:
|
|
229
|
+
explicit_bins = draw_kwargs["bins"]
|
|
230
|
+
if "range" in draw_kwargs:
|
|
231
|
+
explicit_range = draw_kwargs["range"]
|
|
232
|
+
|
|
233
|
+
bins = explicit_bins if explicit_bins is not None else 10
|
|
234
|
+
if explicit_range is not None:
|
|
235
|
+
return bins, explicit_range
|
|
236
|
+
|
|
237
|
+
values = []
|
|
238
|
+
for hist_data in self.hist_datas:
|
|
239
|
+
for dataset in self._extract_hist_datasets(hist_data["args"]):
|
|
240
|
+
finite = dataset[np.isfinite(dataset)]
|
|
241
|
+
if finite.size:
|
|
242
|
+
values.append(finite)
|
|
243
|
+
|
|
244
|
+
if not values:
|
|
245
|
+
return bins, None
|
|
246
|
+
|
|
247
|
+
stacked = np.concatenate(values)
|
|
248
|
+
x_min = float(np.min(stacked))
|
|
249
|
+
x_max = float(np.max(stacked))
|
|
250
|
+
if x_min == x_max:
|
|
251
|
+
delta = 0.5 if x_min == 0.0 else abs(0.5 * x_min)
|
|
252
|
+
x_min -= delta
|
|
253
|
+
x_max += delta
|
|
254
|
+
|
|
255
|
+
return bins, (x_min, x_max)
|
|
256
|
+
|
|
257
|
+
def _extract_hist_datasets(self, hist_args):
|
|
258
|
+
if not hist_args:
|
|
259
|
+
return []
|
|
260
|
+
|
|
261
|
+
x = hist_args[0]
|
|
262
|
+
if isinstance(x, (list, tuple)):
|
|
263
|
+
if not x:
|
|
264
|
+
return []
|
|
265
|
+
if np.isscalar(x[0]):
|
|
266
|
+
return [np.asarray(x).ravel()]
|
|
267
|
+
return [np.asarray(dataset).ravel() for dataset in x]
|
|
268
|
+
|
|
269
|
+
x_array = np.asarray(x)
|
|
270
|
+
if x_array.ndim <= 1:
|
|
271
|
+
return [x_array.ravel()]
|
|
272
|
+
|
|
273
|
+
return [x_array[:, i].ravel() for i in range(x_array.shape[1])]
|
|
274
|
+
|
|
275
|
+
def _flatten_artists(self, maybe_nested):
|
|
276
|
+
if isinstance(maybe_nested, (list, tuple)):
|
|
277
|
+
artists = []
|
|
278
|
+
for item in maybe_nested:
|
|
279
|
+
artists.extend(self._flatten_artists(item))
|
|
280
|
+
return artists
|
|
281
|
+
|
|
282
|
+
return [maybe_nested]
|
|
283
|
+
|
|
284
|
+
def _draw_hist_errors(self, hist_args, hist_result, errors, **kwargs):
|
|
285
|
+
"""Draw error bars on top of histogram. Returns list of artist objects."""
|
|
286
|
+
# bins = kwargs["bins"]
|
|
287
|
+
# data_range = kwargs["range"]
|
|
288
|
+
if errors is True:
|
|
289
|
+
errors_type = "stat"
|
|
290
|
+
elif type(errors) is np.ndarray:
|
|
291
|
+
errors_type = "custom"
|
|
292
|
+
else:
|
|
293
|
+
raise ValueError(f"Invalid errors argument: {errors}. Must be True, False, or array-like.")
|
|
294
|
+
|
|
295
|
+
# Extract histogram values and bin edges
|
|
296
|
+
hist_counts = hist_result[0] # bin heights
|
|
297
|
+
bin_edges = hist_result[1] # bin edges
|
|
298
|
+
|
|
299
|
+
# Compute bin centers
|
|
300
|
+
bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2.0
|
|
301
|
+
bin_width = bin_edges[1] - bin_edges[0]
|
|
302
|
+
|
|
303
|
+
# Compute errors based on type
|
|
304
|
+
if errors_type == "stat":
|
|
305
|
+
errors = np.sqrt(np.maximum(hist_counts, 0))
|
|
306
|
+
if "density" in kwargs and kwargs["density"]:
|
|
307
|
+
# For density histograms, propagate Poisson errors from raw bin counts:
|
|
308
|
+
# density_i = n_i / (N * bin_width_i), sigma_i = sqrt(n_i) / (N * bin_width_i)
|
|
309
|
+
data = np.asarray(hist_args[0]).ravel()
|
|
310
|
+
finite = data[np.isfinite(data)]
|
|
311
|
+
raw_counts, _ = np.histogram(finite, bins=bin_edges)
|
|
312
|
+
total = np.sum(raw_counts)
|
|
313
|
+
if total > 0:
|
|
314
|
+
bin_widths = np.diff(bin_edges)
|
|
315
|
+
errors = np.sqrt(raw_counts) / (total * bin_widths)
|
|
316
|
+
else:
|
|
317
|
+
errors = np.zeros_like(hist_counts, dtype=float)
|
|
318
|
+
elif errors_type == "custom":
|
|
319
|
+
if len(errors) != len(hist_counts):
|
|
320
|
+
raise ValueError(f"Custom errors array length {len(errors)} does not match number of bins {len(hist_counts)}.")
|
|
321
|
+
errors = np.asarray(errors)
|
|
322
|
+
else:
|
|
323
|
+
raise ValueError(f"Unknown errors type: {errors_type}")
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
# Get current color from the last-drawn histogram line
|
|
328
|
+
color = None
|
|
329
|
+
for artist in self._flatten_artists(hist_result[2]):
|
|
330
|
+
if hasattr(artist, "get_edgecolor"):
|
|
331
|
+
color = artist.get_edgecolor()
|
|
332
|
+
break
|
|
333
|
+
|
|
334
|
+
# Draw error bars
|
|
335
|
+
bin_width = bin_edges[1] - bin_edges[0]
|
|
336
|
+
errorbar_artists = self.errorbar(
|
|
337
|
+
bin_centers, hist_counts, yerr=errors,
|
|
338
|
+
fmt=".", elinewidth=LHCB_LINE_WIDTH,
|
|
339
|
+
color=color, alpha=0.7
|
|
340
|
+
)
|
|
341
|
+
artists = []
|
|
342
|
+
|
|
343
|
+
# 手动画 caps(用数据坐标)
|
|
344
|
+
cap_width = bin_width * 0.5 # 或者你想要的比例
|
|
345
|
+
|
|
346
|
+
for x, y, err in zip(bin_centers, hist_counts, errors):
|
|
347
|
+
cap_artistup = self.hlines(y + err, x - 0.5*cap_width, x + 0.5*cap_width,
|
|
348
|
+
color=color, linewidth=LHCB_LINE_WIDTH)
|
|
349
|
+
cap_artistdown = self.hlines(y - err, x - 0.5*cap_width, x + 0.5*cap_width,
|
|
350
|
+
color=color, linewidth=LHCB_LINE_WIDTH)
|
|
351
|
+
|
|
352
|
+
artists.append(cap_artistup)
|
|
353
|
+
artists.append(cap_artistdown)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
# Return all artist objects from errorbar
|
|
357
|
+
if hasattr(errorbar_artists, '__iter__'):
|
|
358
|
+
artists.extend(self._flatten_artists(errorbar_artists))
|
|
359
|
+
else:
|
|
360
|
+
artists.append(errorbar_artists)
|
|
361
|
+
return artists
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def draw(self, renderer, *args, **kwargs):
|
|
365
|
+
# Apply font only to text artists in this axes.
|
|
366
|
+
for text in self.findobj(match=lambda artist: hasattr(artist, "set_fontfamily")):
|
|
367
|
+
text.set_fontfamily(self._hep_font_family)
|
|
368
|
+
|
|
369
|
+
return super().draw(renderer, *args, **kwargs)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
# Register the projection at import time so pyplot can resolve "hep".
|
|
373
|
+
register_projection(HepAxes)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
if __name__ == "__main__":
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
fig, ax = plt.subplots(subplot_kw={"projection": "hep"})
|
|
380
|
+
ax2 = ax.twinx()
|
|
381
|
+
# ax.plot([1, 2, 3], [1, 2, 3], label="Data 1")
|
|
382
|
+
# ax.plot([1, 2, 3], [4, 5, 6], label="Data 2")
|
|
383
|
+
ax.hist(np.random.randn(1000)**2, bins=100, histtype="step", label="Data 1", stat=True, errors=True)
|
|
384
|
+
ax2.hist(np.random.randn(1000)+3, bins=100, histtype="step", label="Data 2", stat=True, errors=np.linspace(5, 20, 100))
|
|
385
|
+
ax.set_xlabel("X-axis")
|
|
386
|
+
ax.set_ylabel("Y-axis")
|
|
387
|
+
ax.grid(True)
|
|
388
|
+
|
|
389
|
+
ax.legend()
|
|
390
|
+
|
|
391
|
+
ax.set_title("HepAxes Example")
|
|
392
|
+
|
|
393
|
+
plt.tight_layout()
|
|
394
|
+
plt.savefig("hep_axes_example.png", dpi=6000)
|
|
395
|
+
plt.show()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
from .HepFigure import HepAxes
|
|
3
|
+
|
|
4
|
+
__all__ = ["HepAxes", "enable_default_hep_subplots"]
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
_ORIGINAL_SUBPLOTS = plt.subplots
|
|
8
|
+
_HEP_DEFAULT_ENABLED = False
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def enable_default_hep_subplots(enable=True):
|
|
12
|
+
"""Enable or disable hep projection as the default for ``plt.subplots``.
|
|
13
|
+
|
|
14
|
+
When enabled, ``plt.subplots()`` will behave like:
|
|
15
|
+
``plt.subplots(subplot_kw={"projection": "hep"})`` unless the caller
|
|
16
|
+
explicitly provides a projection in ``subplot_kw``.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
global _HEP_DEFAULT_ENABLED
|
|
20
|
+
|
|
21
|
+
if enable and not _HEP_DEFAULT_ENABLED:
|
|
22
|
+
def _hep_default_subplots(*args, **kwargs):
|
|
23
|
+
subplot_kw = dict(kwargs.get("subplot_kw") or {})
|
|
24
|
+
subplot_kw.setdefault("projection", "hep")
|
|
25
|
+
kwargs["subplot_kw"] = subplot_kw
|
|
26
|
+
return _ORIGINAL_SUBPLOTS(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
plt.subplots = _hep_default_subplots
|
|
29
|
+
_HEP_DEFAULT_ENABLED = True
|
|
30
|
+
elif not enable and _HEP_DEFAULT_ENABLED:
|
|
31
|
+
plt.subplots = _ORIGINAL_SUBPLOTS
|
|
32
|
+
_HEP_DEFAULT_ENABLED = False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Default-on so users can call plt.subplots() directly after importing HEP_PLOT.
|
|
36
|
+
enable_default_hep_subplots(True)
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// all users - please change the name of this file to lhcbStyle.C
|
|
2
|
+
// Commits to lhcbdocs svn of .C files are not allowed
|
|
3
|
+
{
|
|
4
|
+
|
|
5
|
+
// define names for colours
|
|
6
|
+
Int_t black = 1;
|
|
7
|
+
Int_t red = 2;
|
|
8
|
+
Int_t green = 3;
|
|
9
|
+
Int_t blue = 4;
|
|
10
|
+
Int_t yellow = 5;
|
|
11
|
+
Int_t magenta= 6;
|
|
12
|
+
Int_t cyan = 7;
|
|
13
|
+
Int_t purple = 9;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
////////////////////////////////////////////////////////////////////
|
|
17
|
+
// PURPOSE:
|
|
18
|
+
//
|
|
19
|
+
// This macro defines a standard style for (black-and-white)
|
|
20
|
+
// "publication quality" LHCb ROOT plots.
|
|
21
|
+
//
|
|
22
|
+
// USAGE:
|
|
23
|
+
//
|
|
24
|
+
// Include the lines
|
|
25
|
+
// gROOT->ProcessLine(".L lhcbstyle.C");
|
|
26
|
+
// lhcbStyle();
|
|
27
|
+
// at the beginning of your root macro.
|
|
28
|
+
//
|
|
29
|
+
// Example usage is given in myPlot.C
|
|
30
|
+
//
|
|
31
|
+
// COMMENTS:
|
|
32
|
+
//
|
|
33
|
+
// Font:
|
|
34
|
+
//
|
|
35
|
+
// The font is chosen to be 132, this is Times New Roman (like the text of
|
|
36
|
+
// your document) with precision 2.
|
|
37
|
+
//
|
|
38
|
+
// "Landscape histograms":
|
|
39
|
+
//
|
|
40
|
+
// The style here is designed for more or less square plots.
|
|
41
|
+
// For longer histograms, or canvas with many pads, adjustements are needed.
|
|
42
|
+
// For instance, for a canvas with 1x5 histograms:
|
|
43
|
+
// TCanvas* c1 = new TCanvas("c1", "L0 muons", 600, 800);
|
|
44
|
+
// c1->Divide(1,5);
|
|
45
|
+
// Adaptions like the following will be needed:
|
|
46
|
+
// gStyle->SetTickLength(0.05,"x");
|
|
47
|
+
// gStyle->SetTickLength(0.01,"y");
|
|
48
|
+
// gStyle->SetLabelSize(0.15,"x");
|
|
49
|
+
// gStyle->SetLabelSize(0.1,"y");
|
|
50
|
+
// gStyle->SetStatW(0.15);
|
|
51
|
+
// gStyle->SetStatH(0.5);
|
|
52
|
+
//
|
|
53
|
+
// Authors: Thomas Schietinger, Andrew Powell, Chris Parkes, Niels Tuning
|
|
54
|
+
// Maintained by Editorial board member (currently Niels)
|
|
55
|
+
///////////////////////////////////////////////////////////////////
|
|
56
|
+
|
|
57
|
+
// Use times new roman, precision 2
|
|
58
|
+
Int_t lhcbFont = 132; // Old LHCb style: 62;
|
|
59
|
+
// Line thickness
|
|
60
|
+
Double_t lhcbWidth = 2.00; // Old LHCb style: 3.00;
|
|
61
|
+
// Text size
|
|
62
|
+
Double_t lhcbTSize = 0.06;
|
|
63
|
+
|
|
64
|
+
// use plain black on white colors
|
|
65
|
+
gROOT->SetStyle("Plain");
|
|
66
|
+
TStyle *lhcbStyle= new TStyle("lhcbStyle","LHCb plots style");
|
|
67
|
+
|
|
68
|
+
//lhcbStyle->SetErrorX(0); // don't suppress the error bar along X
|
|
69
|
+
|
|
70
|
+
lhcbStyle->SetFillColor(1);
|
|
71
|
+
lhcbStyle->SetFillStyle(1001); // solid
|
|
72
|
+
lhcbStyle->SetFrameFillColor(0);
|
|
73
|
+
lhcbStyle->SetFrameBorderMode(0);
|
|
74
|
+
lhcbStyle->SetPadBorderMode(0);
|
|
75
|
+
lhcbStyle->SetPadColor(0);
|
|
76
|
+
lhcbStyle->SetCanvasBorderMode(0);
|
|
77
|
+
lhcbStyle->SetCanvasColor(0);
|
|
78
|
+
lhcbStyle->SetStatColor(0);
|
|
79
|
+
lhcbStyle->SetLegendBorderSize(0);
|
|
80
|
+
|
|
81
|
+
// If you want the usual gradient palette (blue -> red)
|
|
82
|
+
lhcbStyle->SetPalette(1);
|
|
83
|
+
// If you want colors that correspond to gray scale in black and white:
|
|
84
|
+
int colors[8] = {0,5,7,3,6,2,4,1};
|
|
85
|
+
lhcbStyle->SetPalette(8,colors);
|
|
86
|
+
|
|
87
|
+
// set the paper & margin sizes
|
|
88
|
+
lhcbStyle->SetPaperSize(20,26);
|
|
89
|
+
lhcbStyle->SetPadTopMargin(0.05);
|
|
90
|
+
lhcbStyle->SetPadRightMargin(0.05); // increase for colz plots
|
|
91
|
+
lhcbStyle->SetPadBottomMargin(0.16);
|
|
92
|
+
lhcbStyle->SetPadLeftMargin(0.14);
|
|
93
|
+
|
|
94
|
+
// use large fonts
|
|
95
|
+
lhcbStyle->SetTextFont(lhcbFont);
|
|
96
|
+
lhcbStyle->SetTextSize(lhcbTSize);
|
|
97
|
+
lhcbStyle->SetLabelFont(lhcbFont,"x");
|
|
98
|
+
lhcbStyle->SetLabelFont(lhcbFont,"y");
|
|
99
|
+
lhcbStyle->SetLabelFont(lhcbFont,"z");
|
|
100
|
+
lhcbStyle->SetLabelSize(lhcbTSize,"x");
|
|
101
|
+
lhcbStyle->SetLabelSize(lhcbTSize,"y");
|
|
102
|
+
lhcbStyle->SetLabelSize(lhcbTSize,"z");
|
|
103
|
+
lhcbStyle->SetTitleFont(lhcbFont);
|
|
104
|
+
lhcbStyle->SetTitleFont(lhcbFont,"x");
|
|
105
|
+
lhcbStyle->SetTitleFont(lhcbFont,"y");
|
|
106
|
+
lhcbStyle->SetTitleFont(lhcbFont,"z");
|
|
107
|
+
lhcbStyle->SetTitleSize(1.2*lhcbTSize,"x");
|
|
108
|
+
lhcbStyle->SetTitleSize(1.2*lhcbTSize,"y");
|
|
109
|
+
lhcbStyle->SetTitleSize(1.2*lhcbTSize,"z");
|
|
110
|
+
|
|
111
|
+
// use medium bold lines and thick markers
|
|
112
|
+
lhcbStyle->SetLineWidth(lhcbWidth);
|
|
113
|
+
lhcbStyle->SetFrameLineWidth(lhcbWidth);
|
|
114
|
+
lhcbStyle->SetHistLineWidth(lhcbWidth);
|
|
115
|
+
lhcbStyle->SetFuncWidth(lhcbWidth);
|
|
116
|
+
lhcbStyle->SetGridWidth(lhcbWidth);
|
|
117
|
+
lhcbStyle->SetLineStyleString(2,"[12 12]"); // postscript dashes
|
|
118
|
+
lhcbStyle->SetMarkerStyle(20);
|
|
119
|
+
lhcbStyle->SetMarkerSize(1.0);
|
|
120
|
+
|
|
121
|
+
// label offsets
|
|
122
|
+
lhcbStyle->SetLabelOffset(0.010,"X");
|
|
123
|
+
lhcbStyle->SetLabelOffset(0.010,"Y");
|
|
124
|
+
|
|
125
|
+
// by default, do not display histogram decorations:
|
|
126
|
+
//lhcbStyle->SetOptStat(0);
|
|
127
|
+
lhcbStyle->SetOptStat("emr"); // show only nent -e , mean - m , rms -r
|
|
128
|
+
// full opts at http://root.cern.ch/root/html/TStyle.html#TStyle:SetOptStat
|
|
129
|
+
lhcbStyle->SetStatFormat("6.3g"); // specified as c printf options
|
|
130
|
+
lhcbStyle->SetOptTitle(0);
|
|
131
|
+
lhcbStyle->SetOptFit(0);
|
|
132
|
+
//lhcbStyle->SetOptFit(1011); // order is probability, Chi2, errors, parameters
|
|
133
|
+
//titles
|
|
134
|
+
lhcbStyle->SetTitleOffset(0.95,"X");
|
|
135
|
+
lhcbStyle->SetTitleOffset(0.95,"Y");
|
|
136
|
+
lhcbStyle->SetTitleOffset(1.2,"Z");
|
|
137
|
+
lhcbStyle->SetTitleFillColor(0);
|
|
138
|
+
lhcbStyle->SetTitleStyle(0);
|
|
139
|
+
lhcbStyle->SetTitleBorderSize(0);
|
|
140
|
+
lhcbStyle->SetTitleFont(lhcbFont,"title");
|
|
141
|
+
lhcbStyle->SetTitleX(0.0);
|
|
142
|
+
lhcbStyle->SetTitleY(1.0);
|
|
143
|
+
lhcbStyle->SetTitleW(1.0);
|
|
144
|
+
lhcbStyle->SetTitleH(0.05);
|
|
145
|
+
|
|
146
|
+
// look of the statistics box:
|
|
147
|
+
lhcbStyle->SetStatBorderSize(0);
|
|
148
|
+
lhcbStyle->SetStatFont(lhcbFont);
|
|
149
|
+
lhcbStyle->SetStatFontSize(0.05);
|
|
150
|
+
lhcbStyle->SetStatX(0.9);
|
|
151
|
+
lhcbStyle->SetStatY(0.9);
|
|
152
|
+
lhcbStyle->SetStatW(0.25);
|
|
153
|
+
lhcbStyle->SetStatH(0.15);
|
|
154
|
+
|
|
155
|
+
// put tick marks on top and RHS of plots
|
|
156
|
+
lhcbStyle->SetPadTickX(1);
|
|
157
|
+
lhcbStyle->SetPadTickY(1);
|
|
158
|
+
|
|
159
|
+
// histogram divisions: only 5 in x to avoid label overlaps
|
|
160
|
+
lhcbStyle->SetNdivisions(505,"x");
|
|
161
|
+
lhcbStyle->SetNdivisions(510,"y");
|
|
162
|
+
|
|
163
|
+
gROOT->SetStyle("lhcbStyle");
|
|
164
|
+
gROOT->ForceStyle();
|
|
165
|
+
/*
|
|
166
|
+
// add LHCb label
|
|
167
|
+
lhcbName = new TPaveText(gStyle->GetPadLeftMargin() + 0.05,
|
|
168
|
+
0.87 - gStyle->GetPadTopMargin(),
|
|
169
|
+
gStyle->GetPadLeftMargin() + 0.20,
|
|
170
|
+
0.95 - gStyle->GetPadTopMargin(),
|
|
171
|
+
"BRNDC");
|
|
172
|
+
lhcbName->AddText("LHCb");
|
|
173
|
+
lhcbName->SetFillColor(0);
|
|
174
|
+
lhcbName->SetTextAlign(12);
|
|
175
|
+
lhcbName->SetBorderSize(0);
|
|
176
|
+
*/
|
|
177
|
+
TText *lhcbLabel = new TText();
|
|
178
|
+
lhcbLabel->SetTextFont(lhcbFont);
|
|
179
|
+
lhcbLabel->SetTextColor(1);
|
|
180
|
+
lhcbLabel->SetTextSize(lhcbTSize);
|
|
181
|
+
lhcbLabel->SetTextAlign(12);
|
|
182
|
+
|
|
183
|
+
TLatex *lhcbLatex = new TLatex();
|
|
184
|
+
lhcbLatex->SetTextFont(lhcbFont);
|
|
185
|
+
lhcbLatex->SetTextColor(1);
|
|
186
|
+
lhcbLatex->SetTextSize(lhcbTSize);
|
|
187
|
+
lhcbLatex->SetTextAlign(12);
|
|
188
|
+
|
|
189
|
+
//std::cout << "-------------------------" << std::endl;
|
|
190
|
+
//std::cout << "Set LHCb Style - Feb 2012" << std::endl;
|
|
191
|
+
//std::cout << "-------------------------" << std::endl;
|
|
192
|
+
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import uproot
|
|
5
|
+
from matplotlib import pyplot as plt
|
|
6
|
+
|
|
7
|
+
from .Tree import Tree
|
|
8
|
+
from ..HEP_PLOT import HepAxes # noqa: F401
|
|
9
|
+
|
|
10
|
+
class ROOT:
|
|
11
|
+
def __init__(self, filename=None):
|
|
12
|
+
self.rt_file = None
|
|
13
|
+
self.load(filename=filename)
|
|
14
|
+
|
|
15
|
+
def __str__(self):
|
|
16
|
+
rt_info = ""
|
|
17
|
+
if self.rt_file is None:
|
|
18
|
+
return "No ROOT file is currently loaded"
|
|
19
|
+
else:
|
|
20
|
+
rt_info += f"File: {self.filename} is opened\n"
|
|
21
|
+
|
|
22
|
+
rt_info += self._directory_tree()
|
|
23
|
+
|
|
24
|
+
return rt_info
|
|
25
|
+
|
|
26
|
+
def __getitem__(self, key):
|
|
27
|
+
|
|
28
|
+
return Tree(self.rt_file[key])
|
|
29
|
+
|
|
30
|
+
def _directory_tree(self, max_depth=None):
|
|
31
|
+
"""Return a pretty string of the ROOT file structure; limit depth with max_depth (None = no limit)."""
|
|
32
|
+
if self.rt_file is None:
|
|
33
|
+
return "No ROOT file loaded"
|
|
34
|
+
|
|
35
|
+
lines = []
|
|
36
|
+
|
|
37
|
+
def _recurse(directory, prefix="", level=0):
|
|
38
|
+
if max_depth is not None and level >= max_depth:
|
|
39
|
+
return
|
|
40
|
+
items = list(directory.items(recursive=False))
|
|
41
|
+
for idx, (key, obj) in enumerate(items):
|
|
42
|
+
name = key.split(";")[0]
|
|
43
|
+
connector = "├── " if idx < len(items) - 1 else "└── "
|
|
44
|
+
|
|
45
|
+
lines.append(f"{prefix}{connector}{name}")
|
|
46
|
+
|
|
47
|
+
if isinstance(obj, uproot.reading.ReadOnlyDirectory):
|
|
48
|
+
child_prefix = prefix + ("│ " if idx < len(items) - 1 else " ")
|
|
49
|
+
_recurse(obj, child_prefix, level + 1)
|
|
50
|
+
|
|
51
|
+
_recurse(self.rt_file)
|
|
52
|
+
return "\n".join(lines) if lines else "(empty file)"
|
|
53
|
+
|
|
54
|
+
def load(self, filename):
|
|
55
|
+
self.filename = filename
|
|
56
|
+
try:
|
|
57
|
+
print(f"Loading ROOT file: {filename}")
|
|
58
|
+
self.rt_file = uproot.open(filename)
|
|
59
|
+
print(self.rt_file)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
print(f"Error loading file: {e}")
|
|
62
|
+
self.rt_file = None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
# rt = ROOT("/home/easonfu/pyproj/FUCKROOT/large_10G.root")
|
|
74
|
+
rt = ROOT("/home/easonfu/pyproj/FUCKROOT/tuple_reco_baseline.root")
|
|
75
|
+
print(rt)
|
|
76
|
+
|
|
77
|
+
rt_layer1 = rt["FakeClusteringUP_46bab5f1/layer0;1"]
|
|
78
|
+
# rt_layer1 = rt["tree"]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
print(rt_layer1)
|
|
82
|
+
print(rt_layer1.keys())
|
|
83
|
+
|
|
84
|
+
fig, ax = plt.subplots(subplot_kw={"projection": "hep"})
|
|
85
|
+
|
|
86
|
+
# ax2 = ax.twinx()
|
|
87
|
+
# normalize
|
|
88
|
+
# rt_layer1.hist1d("var_1", errors=True, ax=ax)
|
|
89
|
+
# rt_layer1.hist1d("x", errors=True, ax=ax)
|
|
90
|
+
# rt_layer1.hist1d("y", errors=True, ax=ax)
|
|
91
|
+
rt_layer1.hist1d("z", errors=True, ax=ax, bins=100)
|
|
92
|
+
|
|
93
|
+
ax.set_xlabel("X Label")
|
|
94
|
+
# ax.set_ylabel("Y Label")
|
|
95
|
+
|
|
96
|
+
plt.show()
|
|
97
|
+
|
|
98
|
+
# for I in range(10):
|
|
99
|
+
# t0 = time.time()
|
|
100
|
+
# print(f"Time taken: {time.time() - t0:.2f} seconds")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from ..HEP_PLOT import HepAxes # noqa: F401
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
|
|
7
|
+
class Tree:
|
|
8
|
+
def __init__(self, rt):
|
|
9
|
+
self.rt = rt
|
|
10
|
+
self._rt_cache = {}
|
|
11
|
+
self._keys = rt.keys()
|
|
12
|
+
|
|
13
|
+
self.data_len = rt.num_entries if hasattr(rt, "num_entries") else 0
|
|
14
|
+
if self.data_len < 1000:
|
|
15
|
+
self._rt_df = rt.arrays(library="pd")
|
|
16
|
+
else:
|
|
17
|
+
self._rt_df = pd.concat([
|
|
18
|
+
rt.arrays(entry_start=0, entry_stop=500, library="pd"),
|
|
19
|
+
rt.arrays(entry_start=self.data_len-500, entry_stop=self.data_len, library="pd")
|
|
20
|
+
])
|
|
21
|
+
|
|
22
|
+
def __str__(self):
|
|
23
|
+
return str(self._rt_df)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def keys(self):
|
|
27
|
+
return self._keys
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def hist1d(self, keys, histtype="step", bins=40, label=None, stat=True, errors=True, **kwargs):
|
|
31
|
+
'''
|
|
32
|
+
:param keys:
|
|
33
|
+
:param kwargs:
|
|
34
|
+
:return:
|
|
35
|
+
'''
|
|
36
|
+
if keys not in self._keys:
|
|
37
|
+
raise KeyError(f"Key ['{keys}'] not in the tree.")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if keys in self._rt_cache:
|
|
41
|
+
data = self._rt_cache[keys]
|
|
42
|
+
else:
|
|
43
|
+
data = self.rt[keys].array(library="np")
|
|
44
|
+
self._rt_cache[keys] = data
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
kwargs.setdefault("histtype", histtype)
|
|
48
|
+
kwargs.setdefault("bins", bins)
|
|
49
|
+
kwargs.setdefault("label", label or keys)
|
|
50
|
+
|
|
51
|
+
fig, ax = plt.subplots()
|
|
52
|
+
ax.hist(data, stat=stat, errors=errors, **kwargs)
|
|
53
|
+
ax.set_xlabel(keys)
|
|
54
|
+
ax.set_ylabel("Entries")
|
|
55
|
+
ax.grid(True)
|
|
56
|
+
ax.legend()
|
|
57
|
+
ax.set_title(f"Histogram of {keys}")
|
|
58
|
+
plt.tight_layout()
|
|
59
|
+
plt.show(block=False)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
return fig
|
|
63
|
+
|
|
64
|
+
def __getitem__(self, key):
|
|
65
|
+
if key not in self._keys:
|
|
66
|
+
raise KeyError(f"Key ['{key}'] not in the tree.")
|
|
67
|
+
|
|
68
|
+
if key in self._rt_cache:
|
|
69
|
+
return self._rt_cache[key]
|
|
70
|
+
else:
|
|
71
|
+
data = self.rt[key].array(library="np")
|
|
72
|
+
self._rt_cache[key] = data
|
|
73
|
+
return data
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if __name__ == "__main__":
|
|
79
|
+
pass
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: FUCKROOT
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: FUCKROOT toolkit for ROOT I/O and HEP-style plotting
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: matplotlib>=3.8
|
|
8
|
+
Requires-Dist: numpy>=1.24
|
|
9
|
+
Requires-Dist: pandas>=2.0
|
|
10
|
+
Requires-Dist: uproot>=5.0
|
|
11
|
+
Requires-Dist: tqdm>=4.65
|
|
12
|
+
|
|
13
|
+
# FUCKROOT
|
|
14
|
+
|
|
15
|
+
FUCKROOT 是一个面向高能物理(HEP)分析场景的小工具包,提供两类核心能力:
|
|
16
|
+
|
|
17
|
+
- ROOT 文件读取与树结构访问(基于 `uproot`)
|
|
18
|
+
- Matplotlib 的 HEP 风格绘图投影(`projection="hep"`)
|
|
19
|
+
|
|
20
|
+
当前版本:`0.1.1`
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- `ROOT` 类:打开 ROOT 文件并打印目录树结构
|
|
25
|
+
- `Tree` 类:
|
|
26
|
+
- 缓存分支数据,支持 `tree["branch"]` 直接取 NumPy 数组
|
|
27
|
+
- 快速查看分支名 `tree.keys()`
|
|
28
|
+
- `hist1d(...)` 一键绘制 1D 直方图
|
|
29
|
+
- `HepAxes` 自定义投影:
|
|
30
|
+
- LHCb 风格默认样式(刻度、线宽、配色、字体)
|
|
31
|
+
- `ax.hist(..., stat=True, errors=True)` 自动统计文本与误差棒
|
|
32
|
+
- 多次 `hist` 自动使用统一 bin/range,便于叠图比较
|
|
33
|
+
- 支持 `twinx()`(当前版本有已知对齐限制,见下方注意事项)
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
要求:Python `>= 3.10`
|
|
38
|
+
|
|
39
|
+
推荐在虚拟环境中安装:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
python -m venv .venv
|
|
43
|
+
source .venv/bin/activate
|
|
44
|
+
pip install -e .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
项目依赖(由 `pyproject.toml` 管理):
|
|
48
|
+
|
|
49
|
+
- `matplotlib>=3.8`
|
|
50
|
+
- `numpy>=1.24`
|
|
51
|
+
- `pandas>=2.0`
|
|
52
|
+
- `uproot>=5.0`
|
|
53
|
+
- `tqdm>=4.65`
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
### 1) 打开 ROOT 文件并查看目录
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from FUCKROOT import ROOT
|
|
61
|
+
|
|
62
|
+
rt = ROOT("your_file.root")
|
|
63
|
+
print(rt) # 打印 ROOT 目录树
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2) 访问树并读取分支
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
tree = rt["tree"]
|
|
70
|
+
print(tree.keys())
|
|
71
|
+
|
|
72
|
+
x = tree["var_0"] # 返回 numpy.ndarray
|
|
73
|
+
print(x.shape)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3) 画 HEP 风格直方图
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import matplotlib.pyplot as plt
|
|
80
|
+
|
|
81
|
+
fig, ax = plt.subplots(subplot_kw={"projection": "hep"})
|
|
82
|
+
ax.hist(x, bins=60, label="var_0", stat=True, errors=True)
|
|
83
|
+
ax.set_xlabel("var_0")
|
|
84
|
+
ax.set_ylabel("Entries")
|
|
85
|
+
ax.legend()
|
|
86
|
+
plt.show()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
说明:导入 `FUCKROOT.HEP_PLOT` 后,`plt.subplots()` 默认会启用 `projection="hep"`。
|
|
90
|
+
|
|
91
|
+
## Public API
|
|
92
|
+
|
|
93
|
+
`FUCKROOT/__init__.py` 暴露以下对象:
|
|
94
|
+
|
|
95
|
+
- `ROOT`
|
|
96
|
+
- `Tree`
|
|
97
|
+
- `HepAxes`
|
|
98
|
+
|
|
99
|
+
你可以直接:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from FUCKROOT import ROOT, Tree, HepAxes
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Notes And Caveats
|
|
106
|
+
|
|
107
|
+
- `Tree.__init__` 在数据量较大时只预取前 500 和后 500 条用于快速预览;实际分支数据在 `tree["branch"]` 时按需读取并缓存。
|
|
108
|
+
- `Tree.hist1d(...)` 当前实现会内部创建新的 `figure/axes` 并直接显示(`plt.show(block=False)`)。
|
|
109
|
+
- `HepAxes.twinx()` 会给出 warning:双轴直方图在当前版本下可能存在 bin 对齐问题,不建议用于严格对齐比较。
|
|
110
|
+
|
|
111
|
+
## Package Layout
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
FUCKROOT/
|
|
115
|
+
HEP_PLOT/
|
|
116
|
+
HepFigure.py # HepAxes 实现与投影注册
|
|
117
|
+
lhcbStyle.C # ROOT 风格参考文件
|
|
118
|
+
ROOT_BASE/
|
|
119
|
+
ROOT.py # ROOT 文件包装
|
|
120
|
+
Tree.py # 树数据与绘图辅助
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
本地开发安装:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pip install -e .
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
构建分发包:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
python -m build
|
|
135
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
FUCKROOT/__init__.py
|
|
4
|
+
FUCKROOT.egg-info/PKG-INFO
|
|
5
|
+
FUCKROOT.egg-info/SOURCES.txt
|
|
6
|
+
FUCKROOT.egg-info/dependency_links.txt
|
|
7
|
+
FUCKROOT.egg-info/requires.txt
|
|
8
|
+
FUCKROOT.egg-info/top_level.txt
|
|
9
|
+
FUCKROOT/HEP_PLOT/HepFigure.py
|
|
10
|
+
FUCKROOT/HEP_PLOT/__init__.py
|
|
11
|
+
FUCKROOT/HEP_PLOT/lhcbStyle.C
|
|
12
|
+
FUCKROOT/ROOT_BASE/ROOT.py
|
|
13
|
+
FUCKROOT/ROOT_BASE/Tree.py
|
|
14
|
+
FUCKROOT/ROOT_BASE/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
FUCKROOT
|
fuckroot-0.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: FUCKROOT
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: FUCKROOT toolkit for ROOT I/O and HEP-style plotting
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: matplotlib>=3.8
|
|
8
|
+
Requires-Dist: numpy>=1.24
|
|
9
|
+
Requires-Dist: pandas>=2.0
|
|
10
|
+
Requires-Dist: uproot>=5.0
|
|
11
|
+
Requires-Dist: tqdm>=4.65
|
|
12
|
+
|
|
13
|
+
# FUCKROOT
|
|
14
|
+
|
|
15
|
+
FUCKROOT 是一个面向高能物理(HEP)分析场景的小工具包,提供两类核心能力:
|
|
16
|
+
|
|
17
|
+
- ROOT 文件读取与树结构访问(基于 `uproot`)
|
|
18
|
+
- Matplotlib 的 HEP 风格绘图投影(`projection="hep"`)
|
|
19
|
+
|
|
20
|
+
当前版本:`0.1.1`
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- `ROOT` 类:打开 ROOT 文件并打印目录树结构
|
|
25
|
+
- `Tree` 类:
|
|
26
|
+
- 缓存分支数据,支持 `tree["branch"]` 直接取 NumPy 数组
|
|
27
|
+
- 快速查看分支名 `tree.keys()`
|
|
28
|
+
- `hist1d(...)` 一键绘制 1D 直方图
|
|
29
|
+
- `HepAxes` 自定义投影:
|
|
30
|
+
- LHCb 风格默认样式(刻度、线宽、配色、字体)
|
|
31
|
+
- `ax.hist(..., stat=True, errors=True)` 自动统计文本与误差棒
|
|
32
|
+
- 多次 `hist` 自动使用统一 bin/range,便于叠图比较
|
|
33
|
+
- 支持 `twinx()`(当前版本有已知对齐限制,见下方注意事项)
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
要求:Python `>= 3.10`
|
|
38
|
+
|
|
39
|
+
推荐在虚拟环境中安装:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
python -m venv .venv
|
|
43
|
+
source .venv/bin/activate
|
|
44
|
+
pip install -e .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
项目依赖(由 `pyproject.toml` 管理):
|
|
48
|
+
|
|
49
|
+
- `matplotlib>=3.8`
|
|
50
|
+
- `numpy>=1.24`
|
|
51
|
+
- `pandas>=2.0`
|
|
52
|
+
- `uproot>=5.0`
|
|
53
|
+
- `tqdm>=4.65`
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
### 1) 打开 ROOT 文件并查看目录
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from FUCKROOT import ROOT
|
|
61
|
+
|
|
62
|
+
rt = ROOT("your_file.root")
|
|
63
|
+
print(rt) # 打印 ROOT 目录树
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2) 访问树并读取分支
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
tree = rt["tree"]
|
|
70
|
+
print(tree.keys())
|
|
71
|
+
|
|
72
|
+
x = tree["var_0"] # 返回 numpy.ndarray
|
|
73
|
+
print(x.shape)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3) 画 HEP 风格直方图
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import matplotlib.pyplot as plt
|
|
80
|
+
|
|
81
|
+
fig, ax = plt.subplots(subplot_kw={"projection": "hep"})
|
|
82
|
+
ax.hist(x, bins=60, label="var_0", stat=True, errors=True)
|
|
83
|
+
ax.set_xlabel("var_0")
|
|
84
|
+
ax.set_ylabel("Entries")
|
|
85
|
+
ax.legend()
|
|
86
|
+
plt.show()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
说明:导入 `FUCKROOT.HEP_PLOT` 后,`plt.subplots()` 默认会启用 `projection="hep"`。
|
|
90
|
+
|
|
91
|
+
## Public API
|
|
92
|
+
|
|
93
|
+
`FUCKROOT/__init__.py` 暴露以下对象:
|
|
94
|
+
|
|
95
|
+
- `ROOT`
|
|
96
|
+
- `Tree`
|
|
97
|
+
- `HepAxes`
|
|
98
|
+
|
|
99
|
+
你可以直接:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from FUCKROOT import ROOT, Tree, HepAxes
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Notes And Caveats
|
|
106
|
+
|
|
107
|
+
- `Tree.__init__` 在数据量较大时只预取前 500 和后 500 条用于快速预览;实际分支数据在 `tree["branch"]` 时按需读取并缓存。
|
|
108
|
+
- `Tree.hist1d(...)` 当前实现会内部创建新的 `figure/axes` 并直接显示(`plt.show(block=False)`)。
|
|
109
|
+
- `HepAxes.twinx()` 会给出 warning:双轴直方图在当前版本下可能存在 bin 对齐问题,不建议用于严格对齐比较。
|
|
110
|
+
|
|
111
|
+
## Package Layout
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
FUCKROOT/
|
|
115
|
+
HEP_PLOT/
|
|
116
|
+
HepFigure.py # HepAxes 实现与投影注册
|
|
117
|
+
lhcbStyle.C # ROOT 风格参考文件
|
|
118
|
+
ROOT_BASE/
|
|
119
|
+
ROOT.py # ROOT 文件包装
|
|
120
|
+
Tree.py # 树数据与绘图辅助
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
本地开发安装:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pip install -e .
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
构建分发包:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
python -m build
|
|
135
|
+
```
|
fuckroot-0.0.0/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# FUCKROOT
|
|
2
|
+
|
|
3
|
+
FUCKROOT 是一个面向高能物理(HEP)分析场景的小工具包,提供两类核心能力:
|
|
4
|
+
|
|
5
|
+
- ROOT 文件读取与树结构访问(基于 `uproot`)
|
|
6
|
+
- Matplotlib 的 HEP 风格绘图投影(`projection="hep"`)
|
|
7
|
+
|
|
8
|
+
当前版本:`0.1.1`
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- `ROOT` 类:打开 ROOT 文件并打印目录树结构
|
|
13
|
+
- `Tree` 类:
|
|
14
|
+
- 缓存分支数据,支持 `tree["branch"]` 直接取 NumPy 数组
|
|
15
|
+
- 快速查看分支名 `tree.keys()`
|
|
16
|
+
- `hist1d(...)` 一键绘制 1D 直方图
|
|
17
|
+
- `HepAxes` 自定义投影:
|
|
18
|
+
- LHCb 风格默认样式(刻度、线宽、配色、字体)
|
|
19
|
+
- `ax.hist(..., stat=True, errors=True)` 自动统计文本与误差棒
|
|
20
|
+
- 多次 `hist` 自动使用统一 bin/range,便于叠图比较
|
|
21
|
+
- 支持 `twinx()`(当前版本有已知对齐限制,见下方注意事项)
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
要求:Python `>= 3.10`
|
|
26
|
+
|
|
27
|
+
推荐在虚拟环境中安装:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
python -m venv .venv
|
|
31
|
+
source .venv/bin/activate
|
|
32
|
+
pip install -e .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
项目依赖(由 `pyproject.toml` 管理):
|
|
36
|
+
|
|
37
|
+
- `matplotlib>=3.8`
|
|
38
|
+
- `numpy>=1.24`
|
|
39
|
+
- `pandas>=2.0`
|
|
40
|
+
- `uproot>=5.0`
|
|
41
|
+
- `tqdm>=4.65`
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
### 1) 打开 ROOT 文件并查看目录
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from FUCKROOT import ROOT
|
|
49
|
+
|
|
50
|
+
rt = ROOT("your_file.root")
|
|
51
|
+
print(rt) # 打印 ROOT 目录树
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 2) 访问树并读取分支
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
tree = rt["tree"]
|
|
58
|
+
print(tree.keys())
|
|
59
|
+
|
|
60
|
+
x = tree["var_0"] # 返回 numpy.ndarray
|
|
61
|
+
print(x.shape)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 3) 画 HEP 风格直方图
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import matplotlib.pyplot as plt
|
|
68
|
+
|
|
69
|
+
fig, ax = plt.subplots(subplot_kw={"projection": "hep"})
|
|
70
|
+
ax.hist(x, bins=60, label="var_0", stat=True, errors=True)
|
|
71
|
+
ax.set_xlabel("var_0")
|
|
72
|
+
ax.set_ylabel("Entries")
|
|
73
|
+
ax.legend()
|
|
74
|
+
plt.show()
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
说明:导入 `FUCKROOT.HEP_PLOT` 后,`plt.subplots()` 默认会启用 `projection="hep"`。
|
|
78
|
+
|
|
79
|
+
## Public API
|
|
80
|
+
|
|
81
|
+
`FUCKROOT/__init__.py` 暴露以下对象:
|
|
82
|
+
|
|
83
|
+
- `ROOT`
|
|
84
|
+
- `Tree`
|
|
85
|
+
- `HepAxes`
|
|
86
|
+
|
|
87
|
+
你可以直接:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from FUCKROOT import ROOT, Tree, HepAxes
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Notes And Caveats
|
|
94
|
+
|
|
95
|
+
- `Tree.__init__` 在数据量较大时只预取前 500 和后 500 条用于快速预览;实际分支数据在 `tree["branch"]` 时按需读取并缓存。
|
|
96
|
+
- `Tree.hist1d(...)` 当前实现会内部创建新的 `figure/axes` 并直接显示(`plt.show(block=False)`)。
|
|
97
|
+
- `HepAxes.twinx()` 会给出 warning:双轴直方图在当前版本下可能存在 bin 对齐问题,不建议用于严格对齐比较。
|
|
98
|
+
|
|
99
|
+
## Package Layout
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
FUCKROOT/
|
|
103
|
+
HEP_PLOT/
|
|
104
|
+
HepFigure.py # HepAxes 实现与投影注册
|
|
105
|
+
lhcbStyle.C # ROOT 风格参考文件
|
|
106
|
+
ROOT_BASE/
|
|
107
|
+
ROOT.py # ROOT 文件包装
|
|
108
|
+
Tree.py # 树数据与绘图辅助
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Development
|
|
112
|
+
|
|
113
|
+
本地开发安装:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pip install -e .
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
构建分发包:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
python -m build
|
|
123
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "FUCKROOT"
|
|
7
|
+
version = "0.0.0"
|
|
8
|
+
description = "FUCKROOT toolkit for ROOT I/O and HEP-style plotting"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"matplotlib>=3.8",
|
|
13
|
+
"numpy>=1.24",
|
|
14
|
+
"pandas>=2.0",
|
|
15
|
+
"uproot>=5.0",
|
|
16
|
+
"tqdm>=4.65",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[tool.setuptools]
|
|
20
|
+
include-package-data = true
|
|
21
|
+
|
|
22
|
+
[tool.setuptools.packages.find]
|
|
23
|
+
where = ["."]
|
|
24
|
+
include = ["FUCKROOT*"]
|
|
25
|
+
exclude = ["zdelete*"]
|
|
26
|
+
|
|
27
|
+
[tool.setuptools.package-data]
|
|
28
|
+
"FUCKROOT.HEP_PLOT" = ["*.C"]
|
fuckroot-0.0.0/setup.cfg
ADDED