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.
@@ -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,4 @@
1
+ from .ROOT import ROOT
2
+ from .Tree import Tree
3
+
4
+ __all__ = ["ROOT", "Tree"]
@@ -0,0 +1,4 @@
1
+ from .HEP_PLOT import HepAxes
2
+ from .ROOT_BASE import ROOT, Tree
3
+
4
+ __all__ = ["ROOT", "Tree", "HepAxes"]
@@ -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,5 @@
1
+ matplotlib>=3.8
2
+ numpy>=1.24
3
+ pandas>=2.0
4
+ uproot>=5.0
5
+ tqdm>=4.65
@@ -0,0 +1 @@
1
+ FUCKROOT
@@ -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,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"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+