30 """Get for the logging.Logger instance of this module
35 Logger instance of this module
37 return logging.getLogger(__name__)
41units_by_quantity_name = {
66def get_unit(quantity_name):
67 """Infers the unit of a quantity from its name.
69 Assumes the standard Belle II unit system.
71 Currently looks up the quantity string from units_by_quantity_name.
76 Name of a quantity (E.g. pt, x, ...)
83 unit = units_by_quantity_name.get(quantity_name, None)
87def compose_axis_label(quantity_name, unit=None):
88 """Formats a quantity name and a unit to a label for a plot axes.
90 If the unit is not given to
is tried to infer it
91 from the quantity name by the get_unit function.
96 Name of the quantity to be displayed at the axes
98 The unit of the quantity. Defaults to get_unit(quantity_name)
106 unit = get_unit(quantity_name)
109 axis_label = quantity_name
111 axis_label = f
'{quantity_name} ({unit})'
116def get1DBinningFromReference(name, refFileName):
117 """ returns nbins, lowerbound, upperbound for TH1 / TProfile with name "name" found in the file "refFileName"
119 @param name : name of the TH1 object to be looked
for in the file
120 @param refFileName : name of the reference file where the object
is searched
for
122 @return int nbin, float xmin, float xmax of the TH1
129 if refFileName
is None or refFileName ==
"":
130 return nbins, x_min, x_max
133 oldDirectory = ROOT.gROOT.CurrentDirectory().load()
135 tfile = ROOT.TFile(refFileName)
137 objptr = tfile.Get(name)
138 if objptr
and objptr.InheritsFrom(
"TH1"):
139 nbins = objptr.GetNbinsX()
140 x_min = objptr.GetXaxis().GetXmin()
141 x_max = objptr.GetXaxis().GetXmax()
143 basf2.B2WARNING(
'Requested object with name: ' + name +
' not found in file: ' + refFileName +
" (or not a TH1)")
145 basf2.B2WARNING(
'Requested file: ' + refFileName +
' could not be opened')
152 return nbins, x_min, x_max
156StatsEntry = ROOT.TParameter(float)
161 """Class for generating a validation plot for the Belle II validation page.
163 Typically it generates plots from values stored
in numpy arrays
and feeds them into
164 plot ROOT classes
for storing them.
166 It implements an automatic binning procedure based on the rice rule
and
167 robust z score outlier detection.
169 It also keeps track of additional statistics typically neglected by ROOT such
as a count
170 for the non finit values such
as NaN, +Inf, -Inf.
172 The special attributes
for the Belle II validation page like
177 are exposed
as properties of this
class.
181 very_sparse_dots_line_style_index = 28
185 """Constructor of the ValidationPlot
190 A unique name to be used as the name of the ROOT object to be generated
192 referenceFileName : str
193 name of a reference file. If set the code will
try to get the histogram
or profile
194 from that file
and determine the number of bins
and upper
and lower bound
195 (so far only implemented
for 1D (TH1, TProfile),
is ignored
for 2D plots)
199 self.name = root_save_name(name)
247 outlier_z_score=None,
248 include_exceptionals=True,
249 allow_discrete=False,
250 cumulation_direction=None,
252 """Fill the plot with a one dimensional histogram."""
257 if n
is not None and xmin
is not None and xmax
is not None:
262 th1_factory = ROOT.TH1D
270 lower_bound=lower_bound,
271 upper_bound=upper_bound,
272 outlier_z_score=outlier_z_score,
273 include_exceptionals=include_exceptionals,
274 allow_discrete=allow_discrete,
275 cumulation_direction=cumulation_direction)
293 outlier_z_score=None,
294 include_exceptionals=True,
295 allow_discrete=False,
296 cumulation_direction=None,
300 """Fill the plot with a one dimensional profile of one variable over another."""
308 if n
is not None and xmin
is not None and xmax
is not None:
313 th1_factory = ROOT.TProfile
315 if gaus_z_score
is None:
322 lower_bound=lower_bound,
323 upper_bound=upper_bound,
324 outlier_z_score=outlier_z_score,
325 include_exceptionals=include_exceptionals,
326 allow_discrete=allow_discrete,
327 cumulation_direction=cumulation_direction)
331 self.
hist2d(xs, ys=ys, weights=weights, stackby=stackby,
333 lower_bound=(lower_bound,
None),
334 upper_bound=(upper_bound,
None),
335 outlier_z_score=(outlier_z_score, outlier_z_score),
336 include_exceptionals=(include_exceptionals,
True),
337 allow_discrete=(allow_discrete,
False),
344 name=histogram.GetName()[1:],
345 z_score=gaus_z_score)
346 profiles.append(profile)
361 histogram.SetMinimum(min_y)
362 histogram.SetMaximum(1.05)
364 self.
plot.SetMinimum(min_y)
365 self.
plot.SetMaximum(1.05)
373 lower_bound=(
None,
None),
374 upper_bound=(
None,
None),
375 outlier_z_score=(
None,
None),
376 include_exceptionals=(
True,
True),
379 """Fill the plot with a (unbinned) two dimensional scatter plot"""
385 x_outlier_z_score, y_outlier_z_score = self.
unpack_2d_param(outlier_z_score)
386 x_include_exceptionals, y_include_exceptionals = self.
unpack_2d_param(include_exceptionals)
390 lower_bound=x_lower_bound,
391 upper_bound=x_upper_bound,
392 outlier_z_score=x_outlier_z_score,
393 include_exceptionals=x_include_exceptionals
398 lower_bound=y_lower_bound,
399 upper_bound=y_upper_bound,
400 outlier_z_score=y_outlier_z_score,
401 include_exceptionals=y_include_exceptionals
404 graph = ROOT.TGraph()
406 graph.SetName(self.
name)
407 graph.SetMarkerStyle(6)
408 graph.GetHistogram().SetOption(
"AP")
413 graph.SetLineColorAlpha(color_index, 0)
417 graph.GetXaxis().SetLimits(x_lower_bound, x_upper_bound)
418 graph.GetYaxis().SetLimits(y_lower_bound, y_upper_bound)
432 lower_bound=(
None,
None),
433 upper_bound=(
None,
None),
434 outlier_z_score=(
None,
None),
435 include_exceptionals=(
True,
True),
438 """Fill the plot with a (unbinned) two dimensional scatter plot
439 xs_and_err and ys_and_err are tuples containing the values
and the errors on these values
450 x_outlier_z_score, y_outlier_z_score = self.unpack_2d_param(outlier_z_score)
451 x_include_exceptionals, y_include_exceptionals = self.unpack_2d_param(include_exceptionals)
455 lower_bound=x_lower_bound,
456 upper_bound=x_upper_bound,
457 outlier_z_score=x_outlier_z_score,
458 include_exceptionals=x_include_exceptionals
463 lower_bound=y_lower_bound,
464 upper_bound=y_upper_bound,
465 outlier_z_score=y_outlier_z_score,
466 include_exceptionals=y_include_exceptionals
469 graph = ROOT.TGraphErrors()
471 graph.SetName(self.name)
472 graph.GetHistogram().SetOption("A")
474 graph.SetMarkerColor(4)
475 graph.SetMarkerStyle(21)
478 graph.GetXaxis().SetLimits(x_lower_bound, x_upper_bound)
479 graph.GetYaxis().SetLimits(y_lower_bound, y_upper_bound)
495 lower_bound=(
None,
None),
496 upper_bound=(
None,
None),
497 outlier_z_score=(
None,
None),
498 include_exceptionals=(
True,
True),
499 allow_discrete=(
False,
False),
502 """Fill the plot with a two dimensional histogram"""
506 if quantiles
is not None:
507 name =
"_" + self.
name
512 x_outlier_z_score, y_outlier_z_score = self.
unpack_2d_param(outlier_z_score)
513 x_include_exceptionals, y_include_exceptionals = self.
unpack_2d_param(include_exceptionals)
514 x_allow_discrete, y_allow_discrete = self.
unpack_2d_param(allow_discrete)
516 if quantiles
is not None:
517 y_include_exceptionals =
True
518 y_allow_discrete =
False
523 lower_bound=x_lower_bound,
524 upper_bound=x_upper_bound,
525 outlier_z_score=x_outlier_z_score,
526 include_exceptionals=x_include_exceptionals,
527 allow_discrete=x_allow_discrete)
532 lower_bound=y_lower_bound,
533 upper_bound=y_upper_bound,
534 outlier_z_score=y_outlier_z_score,
535 include_exceptionals=y_include_exceptionals,
536 allow_discrete=y_allow_discrete)
538 n_x_bins = len(x_bin_edges) - 1
539 n_y_bins = len(y_bin_edges) - 1
545 histogram = ROOT.TH2D(name,
553 get_logger().info(
"Scatter plot %s is discrete in x.", name)
554 x_taxis = histogram.GetXaxis()
555 for i_x_bin, x_bin_label
in enumerate(x_bin_labels):
556 x_taxis.SetBinLabel(i_x_bin + 1, x_bin_label)
560 x_bin_width = x_bin_edges[1] - x_bin_edges[0]
564 get_logger().info(
"Scatter plot %s is discrete in y.", name)
565 y_taxis = histogram.GetYaxis()
566 for i_y_bin, y_bin_label
in enumerate(y_bin_labels):
567 y_taxis.SetBinLabel(i_y_bin + 1, y_bin_label)
571 y_bin_width = y_bin_edges[1] - y_bin_edges[0]
574 self.
create(histogram, xs, ys=ys, weights=weights, stackby=stackby)
576 if quantiles
is not None:
580 for quantile
in quantiles:
581 profile = histogram.QuantilesX(quantile, histogram.GetName()[1:] +
'_' + str(quantile))
584 x_taxis = histogram.GetXaxis()
585 new_x_taxis = profile.GetXaxis()
586 for i_bin
in range(x_taxis.GetNbins() + 2):
587 label = x_taxis.GetBinLabel(i_bin)
589 new_x_taxis.SetBinLabel(i_bin, label)
592 epsilon = sys.float_info.epsilon
593 for i_bin
in range(0, profile.GetNbinsX() + 2):
594 profile.SetBinError(i_bin, epsilon)
596 profiles.append(profile)
599 self.
plot = self.
create_stack(profiles, name=self.
plot.GetName()[1:], reverse_stack=
False, force_graph=
True)
604 x_taxis = histogram.GetXaxis()
605 x_bin_edges = array.array(
"d", list(range(len(x_bin_labels) + 1)))
606 x_taxis.Set(n_x_bins, x_bin_edges)
611 x_taxis = histogram.GetXaxis()
612 y_bin_edges = array.array(
"d", list(range(len(y_bin_labels) + 1)))
613 y_taxis.Set(n_y_bins, y_bin_edges)
618 """Fit a Gaus bell curve to the central portion of a one dimensional histogram
620 The fit is applied to the central mean +- z_score * std interval of the histogram,
621 such that it
is less influence by non gaussian tails further away than the given z score.
623 @param float z_score number of sigmas to include
from the mean value of the histogram.
630 raise RuntimeError(
'Validation plot must be filled before it can be fitted.')
632 if not isinstance(plot, ROOT.TH1D):
633 raise RuntimeError(
'Fitting is currently implemented / tested for one dimensional, non stacked validation plots.')
637 fit_tf1 = ROOT.TF1(
"Fit", formula)
638 fit_tf1.SetTitle(title)
639 fit_tf1.SetParName(0,
"n")
640 fit_tf1.SetParName(1,
"mean")
641 fit_tf1.SetParName(2,
"std")
643 n = histogram.GetSumOfWeights()
644 mean = histogram.GetMean()
645 std = histogram.GetStdDev()
647 fit_tf1.SetParameter(0, n)
648 fit_tf1.SetParameter(1, mean)
649 fit_tf1.SetParameter(2, std)
652 return self.
fit(fit_tf1,
657 """Fit a general line to a one dimensional histogram"""
660 fit_tf1 = ROOT.TF1(
"Fit", formula)
661 fit_tf1.SetTitle(title)
662 fit_tf1.SetParName(0,
"slope")
663 fit_tf1.SetParName(1,
"intercept")
664 self.
fit(fit_tf1,
'M')
667 """Fit a constant function to a one dimensional histogram"""
670 fit_tf1 = ROOT.TF1(
"Fit", formula)
671 fit_tf1.SetTitle(title)
672 fit_tf1.SetParName(0,
"intercept")
673 self.
fit(fit_tf1,
'M')
676 """Fit a diagonal line through the origin to a one dimensional histogram"""
679 fit_tf1 = ROOT.TF1(
"Fit", formula)
680 fit_tf1.SetTitle(title)
681 fit_tf1.SetParName(0,
"slope")
682 self.
fit(fit_tf1,
'M')
684 def fit(self, formula, options, lower_bound=None, upper_bound=None, z_score=None):
685 """Fit a user defined function to a one dimensional histogram
690 Formula string
or TH1 to be fitted. See TF1 constructors
for that
is a valid formula
692 Options string to be used
in the fit. See TH1::Fit()
694 Lower bound of the range to be fitted
696 Upper bound of the range to be fitted
700 raise RuntimeError(
'Validation plot must be filled before it can be fitted.')
702 if not isinstance(plot, ROOT.TH1D):
703 raise RuntimeError(
'Fitting is currently implemented / tested for one dimensional, non stacked validation plots.')
707 xaxis = histogram.GetXaxis()
708 n_bins = xaxis.GetNbins()
709 hist_lower_bound = xaxis.GetBinLowEdge(1)
710 hist_upper_bound = xaxis.GetBinUpEdge(n_bins)
712 if z_score
is not None:
713 mean = histogram.GetMean()
714 std = histogram.GetStdDev()
716 if lower_bound
is None:
717 lower_bound = mean - z_score * std
719 if upper_bound
is None:
720 upper_bound = mean + z_score * std
723 if isinstance(formula, ROOT.TF1):
725 fit_tf1.SetRange(hist_lower_bound, hist_upper_bound)
727 fit_tf1 = ROOT.TF1(
"Fit",
731 get_logger().info(
'Fitting with %s', fit_tf1.GetExpFormula())
734 if lower_bound
is None or lower_bound < hist_lower_bound:
735 lower_bound = hist_lower_bound
736 if upper_bound
is None or upper_bound > hist_upper_bound:
737 upper_bound = hist_upper_bound
741 if 'N' not in options:
744 fit_res = histogram.Fit(fit_tf1, options +
"S",
"", lower_bound, upper_bound)
754 raise ValueError(
"Can not show a validation plot that has not been filled.")
757 """Write the plot to file
761 tdirectory : ROOT.TDirectory, optional
762 ROOT directory to which the plot should be written.
763 If omitted write to the current directory
766 raise ValueError(
"Can not write a validation plot that has not been filled.")
768 with root_cd(tdirectory):
769 ValidationPlot.set_tstyle()
774 meta_options = [
"nostats"]
778 meta_options.append(
"expert")
780 meta_options.append(
"shifter")
784 meta_options.append(f
"pvalue-error={self.pvalue_error}")
786 meta_options.append(f
"pvalue-warn={self.pvalue_warn}")
790 meta_options.append(
"logy")
792 meta_options_str =
",".join(meta_options)
795 histogram.GetListOfFunctions().Add(ROOT.TNamed(
'MetaOptions', meta_options_str))
800 """Getter method if an plot plot is marked as expert plot"""
805 """Getter for the plot title"""
810 """Setter for the plot title"""
813 self.
plot.SetTitle(title)
815 histogram.SetTitle(title)
819 """Getter for the axis label at the x axis"""
824 """Setter for the axis label at the x axis"""
827 histogram.GetXaxis().SetTitle(xlabel)
831 """Getter for the axis label at the y axis"""
836 """Setter for the axis label at the y axis"""
839 histogram.GetYaxis().SetTitle(ylabel)
843 """Getter for the contact email address to be displayed on the validation page"""
848 """Setter for the contact email address to be displayed on the validation page"""
851 found_obj = histogram.FindObject(
'Contact')
853 tnamed = ROOT.TNamed(
"Contact", contact)
854 histogram.GetListOfFunctions().Add(tnamed)
855 found_obj = histogram.FindObject(
'Contact')
856 found_obj.SetTitle(contact)
860 """Getter for the description to be displayed on the validation page"""
865 """Setter for the description to be displayed on the validation page"""
868 found_obj = histogram.FindObject(
'Description')
870 tnamed = ROOT.TNamed(
"Description", description)
871 histogram.GetListOfFunctions().Add(tnamed)
872 found_obj = histogram.FindObject(
'Description')
873 found_obj.SetTitle(description)
877 """Getter for the check to be displayed on the validation page"""
882 """Setter for the check to be displayed on the validation page"""
885 found_obj = histogram.FindObject(
'Check')
887 tnamed = ROOT.TNamed(
"Check", check)
888 histogram.GetListOfFunctions().Add(tnamed)
889 found_obj = histogram.FindObject(
'Check')
890 found_obj.SetTitle(check)
897 """Unpacks a function parameter for the two dimensional plots.
899 If it is a pair the first parameter shall apply to the x coordinate
900 the second to the y coordinate. In this case the pair
is returned
as two values
902 If something
else is given the it
is assumed that this parameter should equally apply
903 to both coordinates. In this case the same values
is return twice
as a pair.
907 param : pair
or single value
908 Function parameter
for a two dimensional plot
913 A pair of values being the parameter
for the x coordinate
and
914 the y coordinate respectively
918 x_param, y_param = param
922 return x_param, y_param
926 """Determine if the data consists of boolean values"""
927 return statistics.is_binary_series(xs)
931 """Determine if the data consists of discrete values"""
932 return statistics.is_discrete_series(xs, max_n_unique=max_n_unique)
936 """Find exceptionally frequent values
946 A list of the found exceptional values.
948 return statistics.rice_exceptional_values(xs)
952 """Does an estimation of mean and standard deviation robust against outliers.
962 Pair of mean and standard deviation
964 x_mean = statistics.truncated_mean(xs)
965 x_std = statistics.trimmed_std(xs)
970 """Formats a value to be placed at a tick on an axis."""
971 if np.isfinite(value)
and value == np.round(value):
972 return str(int(value))
974 formated_value = f
"{value:.5g}"
977 if len(formated_value) > 8:
978 formated_value = f
"{value:.3e}"
979 return formated_value
990 outlier_z_score=None,
991 include_exceptionals=True,
992 allow_discrete=False,
993 cumulation_direction=None):
994 """Combined factory method for creating a one dimensional histogram or a profile plot."""
998 xs = np.array(xs, copy=
False)
1001 ys = np.array(ys, copy=
False)
1003 if weights
is not None:
1004 weights = np.array(weights, copy=
False)
1009 lower_bound=lower_bound,
1010 upper_bound=upper_bound,
1011 outlier_z_score=outlier_z_score,
1012 include_exceptionals=include_exceptionals,
1013 allow_discrete=allow_discrete)
1015 n_bins = len(bin_edges) - 1
1018 histogram = th1_factory(name,
'', n_bins, bin_edges)
1021 get_logger().info(
"One dimensional plot %s is discrete in x.", name)
1022 x_taxis = histogram.GetXaxis()
1023 for i_bin, bin_label
in enumerate(bin_labels):
1024 x_taxis.SetBinLabel(i_bin + 1, bin_label)
1028 bin_width = bin_edges[1] - bin_edges[0]
1036 cumulation_direction=cumulation_direction,
1043 x_taxis = histogram.GetXaxis()
1044 bin_edges = array.array(
"d", list(range(len(bin_labels) + 1)))
1045 x_taxis.Set(n_bins, bin_edges)
1053 cumulation_direction=None,
1054 reverse_stack=None):
1055 """Create histograms from a template, possibly stacked"""
1060 histogram = histogram_template
1061 self.
fill_into(histogram, xs, ys, weights=weights)
1062 if cumulation_direction
is not None:
1063 histogram = self.
cumulate(histogram, cumulation_direction=cumulation_direction)
1065 histograms.append(histogram)
1069 stackby = np.array(stackby, copy=
False)
1070 name = histogram_template.GetName()
1077 groupby_label=
"stack")
1079 if cumulation_direction
is not None:
1080 histograms = [self.
cumulate(histogram, cumulation_direction=cumulation_direction)
1081 for histogram
in histograms]
1083 plot = self.
create_stack(histograms, name=name +
"_stacked", reverse_stack=reverse_stack)
1091 """Create a stack of histograms"""
1092 if len(histograms) == 1:
1093 plot = histograms[0]
1095 if isinstance(histograms[0], (ROOT.TProfile, ROOT.TGraph))
or force_graph:
1096 plot = ROOT.TMultiGraph()
1098 plot = ROOT.THStack()
1105 for histogram
in reversed(histograms):
1106 if isinstance(histogram, ROOT.TProfile)
or (isinstance(histogram, ROOT.TH1)
and force_graph):
1108 plot.Add(histogram,
"APZ")
1112 for histogram
in histograms:
1113 if isinstance(histogram, ROOT.TProfile)
or (isinstance(histogram, ROOT.TH1)
and force_graph):
1115 plot.Add(histogram,
"APZ")
1123 """Extract errors from a TProfile histogram and create a TGraph from these"""
1124 if isinstance(tprofile, ROOT.TGraph):
1127 x_taxis = tprofile.GetXaxis()
1128 n_bins = x_taxis.GetNbins()
1131 bin_ids_without_underflow = list(range(1, n_bins + 1))
1133 bin_centers = np.array([x_taxis.GetBinCenter(i_bin)
for i_bin
in bin_ids_without_underflow])
1135 bin_centers = np.abs(bin_centers)
1136 bin_widths = np.array([x_taxis.GetBinWidth(i_bin)
for i_bin
in bin_ids_without_underflow])
1137 bin_x_errors = bin_widths / 2.0
1140 bin_contents = np.array([tprofile.GetBinContent(i_bin)
for i_bin
in bin_ids_without_underflow])
1141 bin_y_errors = np.array([tprofile.GetBinError(i_bin)
for i_bin
in bin_ids_without_underflow])
1143 tgrapherrors = ROOT.TGraphErrors(n_bins, bin_centers, bin_contents, bin_x_errors, bin_y_errors)
1145 tgrapherrors.GetHistogram().SetOption(
"APZ")
1147 tgrapherrors.SetLineColor(tprofile.GetLineColor())
1148 tgrapherrors.SetLineColor(tprofile.GetLineColor())
1151 for tobject
in tprofile.GetListOfFunctions():
1152 tgrapherrors.GetListOfFunctions().Add(tobject.Clone())
1157 stats_values = np.array([np.nan] * 6)
1158 tprofile.GetStats(stats_values)
1160 sum_w = stats_values[0]
1162 sum_wx = stats_values[2]
1163 sum_wx2 = stats_values[3]
1164 sum_wy = stats_values[4]
1165 sum_wy2 = stats_values[5]
1173 np.sqrt(sum_wx2 * sum_w - sum_wx * sum_wx) / sum_w)
1181 np.sqrt(sum_wy2 * sum_w - sum_wy * sum_wy) / sum_w)
1185 tgrapherrors.GetCovariance())
1189 tgrapherrors.GetCorrelationFactor())
1199 groupby_label="group"):
1200 """Fill data into similar histograms in groups indicated by a groupby array"""
1203 unique_groupbys = np.unique(groupbys)
1204 name = histogram_template.GetName()
1206 for i_value, value
in enumerate(unique_groupbys):
1208 indices_for_value = np.isnan(groupbys)
1210 indices_for_value = groupbys == value
1213 histogram_for_value = histogram_template.Clone(name +
'_' + str(value))
1214 i_root_color = i_value + 1
1216 self.
set_color(histogram_for_value, i_root_color)
1225 filter=indices_for_value)
1227 histograms.append(histogram_for_value)
1232 """Set the color of the ROOT object.
1234 By default the line color of a TGraph should be invisible, so do not change it
1235 For other objects set the marker
and the line color
1239 tobject : Plotable object inheriting
from TAttLine
and TAttMarker such
as TGraph
or TH1
1240 Object of which the color should be set.
1242 Color index of the ROOT color table
1244 if isinstance(tobject, ROOT.TGraph):
1245 tobject.SetMarkerColor(root_i_color)
1247 tobject.SetLineColor(root_i_color)
1248 tobject.SetMarkerColor(root_i_color)
1250 def fill_into(self, plot, xs, ys=None, weights=None, filter=None):
1251 """Fill the data into the plot object"""
1252 if isinstance(plot, ROOT.TGraph):
1254 raise ValueError(
"ys are required for filling a graph")
1256 elif isinstance(plot, ROOT.TGraphErrors):
1258 raise ValueError(
"ys are required for filling a graph error")
1262 self.
fill_into_th1(plot, xs, ys, weights=weights, filter=filter)
1265 """fill point values and error of the x and y axis into the graph"""
1267 assert (len(xs[0]) == len(ys[0]))
1269 graph.Set(len(xs[0]))
1271 for i
in range(len(xs[0])):
1272 graph.SetPoint(i, xs[0][i], ys[0][i])
1273 graph.SetPointError(i, xs[1][i], ys[1][i])
1276 """Fill the data into a TGraph"""
1280 filter = slice(
None)
1290 if x_n_data > max_n_data
or y_n_data > max_n_data:
1291 get_logger().warning(f
"Number of points in scatter graph {self.name} exceed limit {max_n_data}")
1293 get_logger().warning(f
"Cropping {max_n_data}")
1295 xs = xs[0:max_n_data]
1296 ys = ys[0:max_n_data]
1298 x_axis = graph.GetXaxis()
1299 y_axis = graph.GetYaxis()
1301 x_lower_bound = x_axis.GetXmin()
1302 x_upper_bound = x_axis.GetXmax()
1304 y_lower_bound = y_axis.GetXmin()
1305 y_upper_bound = y_axis.GetXmax()
1307 x_underflow_indices = xs < x_lower_bound
1308 x_overflow_indices = xs > x_upper_bound
1310 y_underflow_indices = ys < y_lower_bound
1311 y_overflow_indices = ys > y_upper_bound
1313 plot_indices = ~(np.isnan(xs) |
1314 x_underflow_indices |
1315 x_overflow_indices |
1317 y_underflow_indices |
1320 n_plot_data = np.sum(plot_indices)
1321 plot_xs = xs[plot_indices]
1322 plot_ys = ys[plot_indices]
1324 graph.Set(int(n_plot_data))
1325 for i, (x, y)
in enumerate(zip(plot_xs, plot_ys)):
1326 graph.SetPoint(i, x, y)
1333 x_n_underflow = np.sum(x_underflow_indices)
1337 x_n_overflow = np.sum(x_overflow_indices)
1341 y_n_underflow = np.sum(y_underflow_indices)
1345 y_n_overflow = np.sum(y_overflow_indices)
1359 """Fill the histogram blocking non finite values
1363 histogram : ROOT.TH1
1364 The histogram to be filled
1365 xs : numpy.ndarray (1d)
1366 Data for the first axes
1367 ys : numpy.ndarray (1d), optional
1368 Data
for the second axes
1369 weights : numpy.ndarray (1d), optional
1370 Weight of the individual points. Defaults to one
for each
1371 filter : numpy.ndarray, optional
1372 Boolean index array indicating which entries shall be taken.
1376 filter = slice(
None)
1381 finite_filter = np.isfinite(xs)
1387 finite_filter &= np.isfinite(ys)
1390 xs = xs[finite_filter]
1391 weights = np.ones_like(xs)
1393 weights = weights[filter]
1395 finite_filter &= np.isfinite(weights)
1396 xs = xs[finite_filter]
1397 weights[finite_filter]
1400 ys = ys[finite_filter]
1405 except AttributeError:
1406 Fill = histogram.Fill
1408 fill = np.frompyfunc(Fill, 2, 1)
1409 fill(xs.astype(np.float64, copy=
False),
1410 weights.astype(np.float64, copy=
False))
1412 fill = np.frompyfunc(Fill, 3, 1)
1413 fill(xs.astype(np.float64, copy=
False),
1414 ys.astype(np.float64, copy=
False),
1415 weights.astype(np.float64, copy=
False))
1419 xs = xs.astype(np.float64, copy=
False)
1420 weights = weights.astype(np.float64, copy=
False)
1423 histogram.FillN(n, xs, weights)
1425 basf2.B2WARNING(
"No values to be filled into histogram: " + self.
name)
1429 xs = xs.astype(np.float64, copy=
False)
1430 ys = ys.astype(np.float64, copy=
False)
1431 weights = weights.astype(np.float64, copy=
False)
1434 histogram.FillN(n, xs, ys, weights)
1436 basf2.B2WARNING(
"No values to be filled into histogram: " + self.
name)
1442 """ Extracts the counts of non finite floats from a series
1443 and adds them
as additional statistics to the histogram.
1447 histogram : derived
from ROOT.TH1
or ROOT.TGraph
1448 Something having a GetListOfFunctions method that
1450 A label
for the data series to be prefixed to the entries.
1451 xs : numpy.ndarray (1d)
1452 Data
from which the non finit floats should be counted.
1454 n_nans = np.isnan(xs).sum()
1458 n_positive_inf = np.sum(xs == np.inf)
1459 if n_positive_inf > 0:
1462 n_negative_inf = np.sum(xs == -np.inf)
1463 if n_negative_inf > 0:
1468 """Add a new additional statistics to the histogram.
1472 histogram : derived from ROOT.TH1
or ROOT.TGraph
1473 Something having a GetListOfFunctions method that holds the additional statistics
1475 Label of the statistic
1477 Value of the statistic
1479 stats_entry = StatsEntry(str(label), float(value))
1480 histogram.GetListOfFunctions().Add(stats_entry)
1485 """Get the additional statistics from the histogram and return them a dict.
1489 histogram : derived from ROOT.TH1
or ROOT.TGraph
1490 Something having a GetListOfFunctions method that holds the additional statistics
1494 collection.OrderedDict
1495 A map of labels to values
for the additional statistics
1497 additional_stats = collections.OrderedDict()
1498 for tobject
in histogram.GetListOfFunctions():
1499 if isinstance(tobject, StatsEntry):
1500 stats_entry = tobject
1501 label = stats_entry.GetName()
1502 value = stats_entry.GetVal()
1503 additional_stats[label] = value
1504 return additional_stats
1508 """Extract a slice of a scatterplot and apply a Gaussian fit to it"""
1511 y_taxis = th2.GetYaxis()
1512 th2_lower_bound = y_taxis.GetXmin()
1513 th2_upper_bound = y_taxis.GetXmax()
1514 th2_height = y_taxis.GetXmax() - y_taxis.GetXmin()
1515 n_y_bins = y_taxis.GetNbins()
1517 y_mean = th2.GetMean(2)
1518 y_std = th2.GetStdDev(2)
1519 fit_lower_bound = max(th2_lower_bound, y_mean - z_score * y_std)
1520 fit_upper_bound = min(th2_upper_bound, y_mean + z_score * y_std)
1521 fit_height = fit_upper_bound - fit_lower_bound
1523 required_n_bins_inslice_filled = n_y_bins * fit_height / th2_height
1525 fit_lower_bound = th2_lower_bound
1526 fit_upper_bound = th2_upper_bound
1527 fit_height = fit_upper_bound - fit_lower_bound
1528 required_n_bins_inslice_filled = n_y_bins / 1.61
1531 required_n_bins_inslice_filled = min(required_n_bins_inslice_filled, n_y_bins / 1.61)
1533 fit_tf1 = ROOT.TF1(
"Fit",
"gaus", fit_lower_bound, fit_upper_bound)
1534 fit_tf1.SetParName(0,
"n")
1535 fit_tf1.SetParName(1,
"mean")
1536 fit_tf1.SetParName(2,
"std")
1540 param_fit_th1s = ROOT.TObjArray()
1541 th2.FitSlicesY(fit_tf1, i_first_bin, i_last_bin,
1542 int(required_n_bins_inslice_filled),
1543 fit_options, param_fit_th1s)
1545 th1_means = param_fit_th1s.At(1)
1546 th1_means.SetName(name)
1547 th1_means.SetTitle(th2.GetTitle())
1552 x_taxis = th2.GetXaxis()
1553 new_x_taxis = th1_means.GetXaxis()
1554 for i_bin
in range(x_taxis.GetNbins() + 2):
1555 label = x_taxis.GetBinLabel(i_bin)
1557 new_x_taxis.SetBinLabel(i_bin, label)
1560 data_lower_bound = th1_means.GetMinimum(fit_lower_bound)
1561 data_upper_bound = th1_means.GetMaximum(fit_upper_bound)
1562 data_height = data_upper_bound - data_lower_bound
1564 plot_lower_bound = max(fit_lower_bound, data_lower_bound - 0.05 * data_height)
1565 plot_upper_bound = min(fit_upper_bound, data_upper_bound + 0.05 * data_height)
1567 th1_means.SetMinimum(plot_lower_bound)
1568 th1_means.SetMaximum(plot_upper_bound)
1573 def cumulate(cls, histogram, cumulation_direction=None):
1574 """Cumulates the histogram inplace.
1578 histogram : ROOT.TH1 or ROOT.TProfile
1579 Filled histogram to be cumulated
1580 cumulation_direction : int, optional
1581 Direction
is indicated by the sign.
1582 Positive means
from left to right, negative means
from right to left.
1583 If now cumulation direction
is given
return the histogram
as is.
1588 Cumulated histogram potentially altered inplace.
1590 if not cumulation_direction:
1593 cumulate_backward = cumulation_direction < 0
1594 cumulate_forward =
not cumulate_backward
1596 if isinstance(histogram, ROOT.TH2):
1597 raise ValueError(
"Cannot cumulate a two dimensional histogram.")
1599 if isinstance(histogram, ROOT.TH3):
1600 raise ValueError(
"Cannot cumulate a three dimensional histogram.")
1602 if not isinstance(histogram, ROOT.TH1):
1603 raise ValueError(
"Can only cumulate a one dimensional histogram.")
1605 if isinstance(histogram, ROOT.TProfile):
1606 tprofile = histogram
1609 tgraph.SetName(tprofile.GetName())
1611 n_bins = histogram.GetNbinsX()
1613 cumulated_content = 0.0
1614 cumulated_entries = 0
1618 i_bins = list(range(0, n_bins + 2))
1619 if not cumulate_forward:
1620 i_bins = reversed(i_bins)
1622 for i_bin
in i_bins:
1624 bin_content = tprofile.GetBinContent(i_bin)
1625 bin_entries = tprofile.GetBinEffectiveEntries(i_bin)
1626 bin_std = tprofile.GetBinError(i_bin)
1628 if bin_entries != 0:
1629 cumulated_content = (
1630 1.0 * (cumulated_entries * cumulated_content + bin_entries * bin_content) /
1631 (cumulated_entries + bin_entries)
1635 math.hypot(cumulated_entries * cumulated_std, bin_entries * bin_std) /
1636 (cumulated_entries + bin_entries)
1639 cumulated_entries = cumulated_entries + bin_entries
1644 if i_point >= 0
and i_point < n_points:
1645 x = tgraph.GetX()[i_point]
1649 tgraph.SetPoint(i_point, x, cumulated_content)
1651 x_error = tgraph.GetErrorX(i_point)
1652 tgraph.SetPointError(i_point, x_error, cumulated_std)
1657 n_bins = histogram.GetNbinsX()
1658 cumulated_content = 0.0
1660 i_bins = list(range(0, n_bins + 2))
1661 if not cumulate_forward:
1662 i_bins = reversed(i_bins)
1664 for i_bin
in i_bins:
1665 bin_content = histogram.GetBinContent(i_bin)
1666 cumulated_content += bin_content
1667 histogram.SetBinContent(i_bin, cumulated_content)
1677 outlier_z_score=None,
1678 include_exceptionals=True,
1679 allow_discrete=False):
1680 """Deducing bin edges from a data series.
1684 xs : numpy.ndarray (1d)
1685 Data point for which a binning should be found.
1686 stackbys : numpy.ndarray (1d)
1687 Categories of the data points to be distinguishable
1688 bins : list(float)
or int
or None, optional
1689 Preset bin edges
or preset number of desired bins.
1690 The default,
None, means the bound should be extracted
from data.
1691 The rice rule
is used the determine the number of bins.
1692 If a list of floats
is given
return them immediately.
1693 lower_bound : float
or None, optional
1694 Preset lower bound of the binning range.
1695 The default,
None, means the bound should be extracted
from data.
1696 upper_bound : float
or None, optional
1697 Preset upper bound of the binning range.
1698 The default,
None, means the bound should be extracted
from data.
1699 outlier_z_score : float
or None, optional
1700 Threshold z-score of outlier detection.
1701 The default,
None, means no outlier detection.
1702 include_exceptionals : bool, optional
1703 If the outlier detection
is active this switch indicates,
1704 if values detected
as exceptionally frequent shall be included
1705 nevertheless into the binning range. Default
is True,
1706 which means exceptionally frequent values
as included
1707 even
if they are detected
as outliers.
1711 np.array (1d), list(str)
1712 Pair of bin edges
and labels deduced
from the series.
1713 Second element
is None if the series
is not detected
as discrete.
1715 debug = get_logger().debug
1716 debug('Determine binning for plot named %s', self.
name)
1722 elif isinstance(bins, collections.abc.Iterable):
1726 bin_edges = array.array(
'd', bin_edges)
1728 return bin_edges, bin_labels
1739 message = f
'Cannot accept n_bins={bins} as number of bins, because it is not a number greater than 0.'
1740 raise ValueError(message)
1743 xs = np.array(xs, copy=
False)
1747 debug(
'Discrete binning values encountered')
1748 finite_xs = xs[np.isfinite(xs)]
1749 unique_xs = np.unique(finite_xs)
1752 if lower_bound
is None:
1753 if len(unique_xs) == 0:
1754 if upper_bound
is None:
1757 lower_bound = upper_bound - 1
1759 lower_bound = np.min(unique_xs)
1761 unique_xs = unique_xs[unique_xs >= lower_bound]
1763 if upper_bound
is None:
1764 if len(unique_xs) == 0:
1765 upper_bound = lower_bound + 1
1767 upper_bound = np.min(unique_xs)
1769 unique_xs = unique_xs[unique_xs <= upper_bound]
1772 n_bins = len(unique_xs)
or 1
1774 if len(unique_xs) > 0
and n_bins >= len(unique_xs):
1776 bin_edges = array.array(
'd', unique_xs)
1779 bin_edges.append(bin_edges[-1] + 1)
1780 return bin_edges, bin_labels
1789 debug(
'Lower bound %s', lower_bound)
1790 debug(
'Upper bound %s', upper_bound)
1791 debug(
'N bins %s', n_bins)
1797 lower_bound=lower_bound,
1798 upper_bound=upper_bound,
1799 outlier_z_score=outlier_z_score,
1800 include_exceptionals=include_exceptionals)
1802 n_bins, lower_bound, upper_bound = bin_range
1804 n_bin_edges = n_bins + 1
1805 if lower_bound != upper_bound:
1807 debug(
"Creating flat distribution binning")
1808 percentiles = np.linspace(0.0, 100.0, n_bin_edges)
1809 bin_edges = np.unique(np.nanpercentile(xs[(lower_bound <= xs) & (xs <= upper_bound)], percentiles))
1813 bin_edges = np.linspace(lower_bound, upper_bound, n_bin_edges)
1818 bin_edges[0] = lower_bound
1819 bin_edges[-1] = np.nextafter(upper_bound, np.inf)
1820 debug(
'Bins %s', bin_edges)
1824 bin_edges = [lower_bound, upper_bound + 1]
1827 bin_edges = array.array(
'd', bin_edges)
1828 debug(
'Bins %s for %s', bin_edges, self.
name)
1829 return bin_edges,
None
1837 outlier_z_score=None,
1838 include_exceptionals=True):
1839 """Calculates the number of bins, the lower bound and the upper bound from a given data series
1840 estimating the values that are not given.
1842 If the outlier_z_score
is given the method tries to exclude outliers that exceed a certain z-score.
1843 The z-score
is calculated (x - x_mean) / x_std. The be robust against outliers the necessary
1844 mean
and std deviation are based on truncated mean
and a trimmed std calculated
from the inter
1845 quantile range (IQR).
1847 If additional include_exceptionals
is true the method tries to find exceptional values
in the series
1848 and always include them
in the range
if it finds any.
1849 Exceptional values means exact values that appear often
in the series
for whatever reason.
1850 Possible reasons include
1851 * Integral / default values
1852 * Failed evaluation conditions
1854 which should be
not cropped away automatically
if you are locking on the quality of your data.
1858 xs : numpy.ndarray (1d)
1859 Data point
for which a binning should be found.
1860 stackbys : numpy.ndarray (1d)
1861 Categories of the data points to be distinguishable
1862 n_bins : int
or None, optional
1863 Preset number of desired bins. The default,
None, means the bound should be extracted
from data.
1864 The rice rule
is used the determine the number of bins.
1865 lower_bound : float
or None, optional
1866 Preset lower bound of the binning range.
1867 The default,
None, means the bound should be extracted
from data.
1868 upper_bound : float
or None, optional
1869 Preset upper bound of the binning range.
1870 The default,
None, means the bound should be extracted
from data.
1871 outlier_z_score : float
or None, optional
1872 Threshold z-score of outlier detection.
1873 The default,
None, means no outlier detection.
1874 include_exceptionals : bool, optional
1875 If the outlier detection
is active this switch indicates,
1876 if values detected
as exceptionally frequent shall be included
1877 nevertheless into the binning range. Default
is True,
1878 which means exceptionally frequent values
as included
1879 even
if they are detected
as outliers.
1883 n_bins, lower_bound, upper_bound : int, float, float
1884 A triple of found number of bins, lower bound
and upper bound of the binning range.
1887 if stackbys
is not None:
1888 unique_stackbys = np.unique(stackbys)
1890 for value
in unique_stackbys:
1892 indices_for_value = np.isnan(stackbys)
1894 indices_for_value = stackbys == value
1896 stack_lower_bound, stack_upper_bound = \
1898 lower_bound=lower_bound,
1899 upper_bound=upper_bound,
1900 outlier_z_score=outlier_z_score,
1901 include_exceptionals=include_exceptionals)
1903 stack_ranges.append([stack_lower_bound, stack_upper_bound])
1905 lower_bound = np.nanmin([lwb
for lwb, upb
in stack_ranges])
1906 upper_bound = np.nanmax([upb
for lwb, upb
in stack_ranges])
1910 lower_bound=lower_bound,
1911 upper_bound=upper_bound,
1912 outlier_z_score=outlier_z_score,
1913 include_exceptionals=include_exceptionals)
1918 n_data = np.sum((lower_bound <= xs) & (xs <= upper_bound))
1919 rice_n_bins = int(statistics.rice_n_bin(n_data))
1920 n_bins = rice_n_bins
1923 n_bins = int(n_bins)
1926 message = f
'Cannot accept n_bins={n_bins} as number of bins, because it is not a number greater than 0.'
1927 raise ValueError(message)
1929 return n_bins, lower_bound, upper_bound
1935 outlier_z_score=None,
1936 include_exceptionals=True):
1940 xs : numpy.ndarray (1d)
1941 Data point for which a binning should be found.
1942 lower_bound : float
or None, optional
1943 Preset lower bound of the binning range.
1944 The default,
None, means the bound should be extracted
from data.
1945 upper_bound : float
or None, optional
1946 Preset upper bound of the binning range.
1947 The default,
None, means the bound should be extracted
from data.
1948 outlier_z_score : float
or None, optional
1949 Threshold z-score of outlier detection.
1950 The default,
None, means no outlier detection.
1951 include_exceptionals : bool, optional
1952 If the outlier detection
is active this switch indicates,
1953 if values detected
as exceptionally frequent shall be included
1954 nevertheless into the binning range. Default
is True,
1955 which means exceptionally frequent values
as included
1956 even
if they are detected
as outliers.
1960 lower_bound, upper_bound : float, float
1961 A pair of found lower bound
and upper bound of series.
1963 debug = get_logger().debug
1965 finite_xs_indices = np.isfinite(xs)
1966 if np.any(finite_xs_indices):
1967 finite_xs = xs[finite_xs_indices]
1971 make_symmetric =
False
1972 exclude_outliers = outlier_z_score
is not None and (lower_bound
is None or upper_bound
is None)
1975 if include_exceptionals
or exclude_outliers:
1977 exceptional_indices = np.in1d(finite_xs, exceptional_xs)
1980 if exclude_outliers:
1981 if not np.all(exceptional_indices):
1986 x_mean, x_std = np.nan, np.nan
1988 make_symmetric = abs(x_mean) < x_std / 5.0
and lower_bound
is None and upper_bound
is None
1990 if include_exceptionals
and len(exceptional_xs) != 0:
1991 lower_exceptional_x = np.min(exceptional_xs)
1992 upper_exceptional_x = np.max(exceptional_xs)
1993 make_symmetric =
False
1995 lower_exceptional_x = np.nan
1996 upper_exceptional_x = np.nan
1999 if lower_bound
is None:
2001 lower_bound = np.min(finite_xs)
2005 if outlier_z_score
is not None:
2007 lower_outlier_bound = x_mean - outlier_z_score * x_std
2011 indices_above_lower_outlier_bound = finite_xs >= lower_outlier_bound
2013 if np.any(indices_above_lower_outlier_bound):
2014 lower_bound = np.min(finite_xs[indices_above_lower_outlier_bound])
2017 lower_bound = np.nanmin([lower_bound, lower_exceptional_x])
2019 debug(
'Lower bound after outlier detection')
2020 debug(
'Lower bound %s', lower_bound)
2021 debug(
'Lower outlier bound %s', lower_outlier_bound)
2024 if upper_bound
is None:
2026 upper_bound = np.max(finite_xs)
2029 if outlier_z_score
is not None:
2031 upper_outlier_bound = x_mean + outlier_z_score * x_std
2035 indices_below_upper_outlier_bound = finite_xs <= upper_outlier_bound
2037 if np.any(indices_below_upper_outlier_bound):
2038 upper_bound = np.max(finite_xs[indices_below_upper_outlier_bound])
2041 upper_bound = np.nanmax([upper_bound, upper_exceptional_x])
2043 debug(
'Upper bound after outlier detection')
2044 debug(
'Upper bound %s', upper_bound)
2045 debug(
'Upper outlier bound %s', upper_outlier_bound)
2047 if make_symmetric
and lower_bound < 0
and upper_bound > 0:
2048 if abs(abs(lower_bound) - abs(upper_bound)) < x_std / 5.0:
2049 abs_bound = max(abs(lower_bound), abs(upper_bound))
2050 lower_bound = -abs_bound
2051 upper_bound = abs_bound
2053 return lower_bound, upper_bound
2057 """Combining fit TF1 with the additional statistics and attach them to the histogram.
2061 histogram : ROOT.TH1 or ROOT.TGraph
or ROOT.TMultiGraph
2062 Something having a GetListOfFunctions method that should hold
2063 the combined fit
and additional statistics function.
2066 cls.set_tf1(histogram, additional_stats_tf1)
2070 """Combining fit TF1 with the additional statistics and attach them to the histogram.
2074 histogram : ROOT.TH1 or ROOT.TGraph
or ROOT.TMultiGraph
2075 Something having a GetListOfFunctions method that should hold
2076 the combined fit
and additional statistics function.
2080 cls.set_tf1(histogram, combined_tf1)
2084 """Set the attached TF1 of the histogram.
2088 histogram : ROOT.TH1 or ROOT.TGraph
or ROOT.TMultiGraph
2089 Something having a GetListOfFunctions method that should hold
2090 the combined fit
and additional statistics function.
2095 tf1.SetName(
"FitAndStats")
2096 histogram.GetListOfFunctions().Add(tf1)
2100 """Delete the attached TF1 from the histogram
2104 histogram : ROOT.TH1 or ROOT.TGraph
2105 Something having a GetListOfFunctions method that holds the fit function
2107 tf1 = histogram.FindObject("FitAndStats")
2109 function_list = histogram.GetListOfFunctions()
2110 function_list.Remove(tf1)
2114 """Create a TF1 with the additional statistics from the histogram as parameters.
2118 histogram : ROOT.TH1 or ROOT.TGraph
2119 Something having a GetListOfFunctions method that holds the additional statistics.
2124 Function
with the additional statistics
as parameters.
2128 if not additional_stats:
2138 formula_string =
'+'.join(
'0*[' + str(i) +
']' for i
in range(len(additional_stats)))
2141 additional_stats_tf1 = ROOT.TF1(
"Stats", formula_string, lower_bound, upper_bound)
2143 for (i, (label, value))
in enumerate(additional_stats.items()):
2147 label = label.replace(
" ",
"-")
2148 additional_stats_tf1.SetParName(i, label)
2149 additional_stats_tf1.FixParameter(i, value)
2151 return additional_stats_tf1
2155 """Combine the fit function and the function carrying the additional statistics to one function.
2161 additional_stats_tf1 : ROOT.TF1
2162 The function carrying the additional statistics as parameters
2168 if additional_stats_tf1
is None:
2174 lower_bound = ctypes.c_double()
2175 upper_bound = ctypes.c_double()
2176 fit_tf1.GetRange(lower_bound, upper_bound)
2177 title = fit_tf1.GetTitle()
2179 combined_formula = additional_stats_tf1.GetExpFormula().Data() +
'+' + fit_tf1.GetExpFormula().Data()
2180 combined_tf1 = ROOT.TF1(
"Combined", combined_formula, lower_bound.value, upper_bound.value)
2181 combined_tf1.SetTitle(title)
2184 chi2 = fit_tf1.GetChisquare()
2185 combined_tf1.SetChisquare(chi2)
2187 ndf = fit_tf1.GetNDF()
2188 combined_tf1.SetNDF(ndf)
2190 n_stats_parameters = additional_stats_tf1.GetNpar()
2199 """Copy the parameters of one TF1 to another.
2203 tf1_source : ROOT.TF1
2204 Take parameters from here
2205 tf1_target : ROOT.TF1
2207 offset : int, optional
2208 Index of the first target parameter to which to copy.
2210 n_parameters = tf1_source.GetNpar()
2213 lower_bound = ctypes.c_double()
2214 upper_bound = ctypes.c_double()
2216 for i_source
in range(n_parameters):
2217 parameter_name = tf1_source.GetParName(i_source)
2218 i_target = tf1_target.GetParNumber(parameter_name)
2222 for i_target
in range(tf1_target.GetNpar()):
2223 if parameter_name == tf1_target.GetParName(i_target):
2229 tf1_target.SetParameter(i_target,
2230 tf1_source.GetParameter(i_source))
2231 tf1_target.SetParError(i_target,
2232 tf1_source.GetParError(i_source))
2234 tf1_source.GetParLimits(i_source, lower_bound, upper_bound)
2235 tf1_target.SetParLimits(i_target, lower_bound.value, upper_bound.value)
2238 """Reassign the special attributes of the plot forwarding them to the ROOT plot."""
2255 """Sets the maximum of the vertical plotable range"""
2257 if isinstance(histogram, ROOT.TH1):
2258 histogram.SetMaximum(histogram.GetMaximum(maximum))
2260 histogram.SetMaximum(maximum)
2263 """Sets the minimum of the vertical plotable range"""
2265 if isinstance(histogram, ROOT.TH1):
2266 histogram.SetMinimum(histogram.GetMinimum(minimum))
2268 histogram.SetMinimum(minimum)
2272 """Set the style such that the additional stats entries are shown by the TBrowser"""
2273 belle2_validation_style_name = "belle2_validation_style"
2274 belle2_validation_tstyle = ROOT.gROOT.GetStyle(belle2_validation_style_name)
2275 if not belle2_validation_tstyle:
2276 belle2_validation_tstyle = ROOT.TStyle(belle2_validation_style_name, belle2_validation_style_name)
2279 belle2_validation_tstyle.SetOptFit(opt_fit)
2282 belle2_validation_tstyle.SetOptStat(opt_stat)
2283 ROOT.gROOT.SetStyle(belle2_validation_style_name)
2288 belle2_validation_tstyle.cd()
2292 """Simple test method"""
2293 ValidationPlot.set_tstyle()
2296 normal_distributed_values = np.random.randn(1000)
2299 normal_distributed_values[i] = np.nan
2301 for i
in range(10, 20):
2302 normal_distributed_values[i] = np.inf
2304 for i
in range(20, 30):
2305 normal_distributed_values[i] = -np.inf
2308 validation_histogram.hist(normal_distributed_values)
2309 validation_histogram.title =
'A normal distribution'
2310 validation_histogram.xlabel =
'normal'
2311 validation_histogram.ylabel =
'frequency'
2312 validation_histogram.fit_gaus()
2316 cumulated_histogram.hist(normal_distributed_values, cumulation_direction=1)
2317 cumulated_histogram.title =
'A cumulated normal distribution'
2318 cumulated_histogram.xlabel =
'normal'
2319 cumulated_histogram.ylabel =
'cdf'
2320 cumulated_histogram.show()
2324 stackby = np.random.binomial(1.0, 0.40, 1000)
2325 stacked_validation_histogram =
ValidationPlot(
'test_stacked_hist')
2326 stacked_validation_histogram.hist(normal_distributed_values, stackby=stackby)
2329 x = np.random.randn(1000)
2330 y = 3 * np.random.randn(1000)
2331 ones = np.ones_like(x)
2334 x1 = np.where(stackby != 0, np.cos(angle) * ones, ones) * x + np.where(stackby != 0, np.sin(angle) * ones, ones) * y
2335 y1 = np.where(stackby != 0, np.sin(angle) * ones, ones) * x - np.where(stackby != 0, np.cos(angle) * ones, ones) * y
2337 stacked_validation_scatter =
ValidationPlot(
'test_stacked_scatter')
2338 stacked_validation_scatter.scatter(x1, y1, stackby=stackby)
2341 stacked_validation_profile =
ValidationPlot(
'test_stacked_profile')
2342 stacked_validation_profile.profile(x1, y1, stackby=stackby)
2345 stacked_validation_hist2d =
ValidationPlot(
'test_stacked_hist2d')
2346 stacked_validation_hist2d.hist2d(x1, y1, stackby=stackby)
2349 x = np.linspace(-1, 1, 1000)
2353 diagonal_plot.profile(x, y, bins=50)
2354 diagonal_plot.fit_line()
2358 cumulated_profile.profile(x, y, bins=50, cumulation_direction=1)
2360 tfile = ROOT.TFile(
'test.root',
'RECREATE')
2362 validation_histogram.write(tfile)
2364 with root_cd(
"expert")
as tdirectory1:
2365 diagonal_plot.write(tdirectory1)
2366 cumulated_profile.write(tdirectory1)
2367 cumulated_histogram.write(tdirectory1)
2369 with root_cd(
"stacked")
as tdirectory2:
2370 stacked_validation_histogram.write(tdirectory2)
2371 stacked_validation_scatter.write()
2372 stacked_validation_profile.write()
2373 stacked_validation_hist2d.write()
2377 tfile = ROOT.TFile(
'test.root')
2378 tBrowser = ROOT.TBrowser()
2379 tBrowser.BrowseObject(tfile)
2384if __name__ ==
'__main__':
title
cached value of the title for this plot
def format_bin_label(value)
pvalue_warn
custom levels for pvalue warnings
def hist(self, xs, weights=None, stackby=None, bins=None, lower_bound=None, upper_bound=None, outlier_z_score=None, include_exceptionals=True, allow_discrete=False, cumulation_direction=None, is_expert=True)
y_log
Indicator whether the y axes should be displayed as a log scale.
def create_stack(cls, histograms, name, reverse_stack, force_graph=False)
def create_additional_stats_tf1(cls, histogram)
description
description of the plot
contact
contact information for this plot
upper_bound
upper right corner of the hisogram
def write(self, tdirectory=None)
def fit_gaus(self, z_score=None)
ylabel
default label for the histogram's Y axis
int very_sparse_dots_line_style_index
A an index that reference to a dot spacing such that the line is almost invisible for scatter.
def profile(self, xs, ys, weights=None, stackby=None, bins=None, lower_bound=None, upper_bound=None, y_binary=None, y_log=None, outlier_z_score=None, include_exceptionals=True, allow_discrete=False, cumulation_direction=None, gaus_z_score=None, is_expert=True, is_asymmetry=False)
def unpack_2d_param(param)
def is_discrete(xs, max_n_unique=None)
_contact
Contact email address for display on the validation page.
pvalue_error
custom levels for pvalue errors
def grapherrors(self, xs_and_err, ys_and_err, stackby=None, lower_bound=(None, None), upper_bound=(None, None), outlier_z_score=(None, None), include_exceptionals=(True, True), max_n_data=100000, is_expert=True)
def delete_tf1(cls, histogram)
_title
Title of the validation plot.
def create_1d(self, th1_factory, xs, ys=None, weights=None, bins=None, stackby=None, lower_bound=None, upper_bound=None, outlier_z_score=None, include_exceptionals=True, allow_discrete=False, cumulation_direction=None)
def description(self, description)
def __init__(self, name, referenceFileName=None)
def add_stats_entry(cls, histogram, label, value)
def contact(self, contact)
_xlabel
X axes label of the validation plot.
xlabel
cached value of the x-axis label for this plot
def set_tf1(cls, histogram, tf1)
def fill_into(self, plot, xs, ys=None, weights=None, filter=None)
def combine_fit_and_additional_stats(cls, fit_tf1, additional_stats_tf1)
def create(self, histogram_template, xs, ys=None, weights=None, stackby=None, cumulation_direction=None, reverse_stack=None)
def set_maximum(self, maximum)
def attach_attributes(self)
def add_nan_inf_stats(cls, histogram, name, xs)
def set_color(self, tobject, root_i_color)
def get_robust_mean_and_std(xs)
def cumulate(cls, histogram, cumulation_direction=None)
def copy_tf1_parameters(cls, tf1_source, tf1_target, offset=0)
_is_expert
per default all plots are expert and must be set to non-expert explicitly
def fit(self, formula, options, lower_bound=None, upper_bound=None, z_score=None)
_description
Description of the plot purpose for display on the validation page.
histograms
A list of the histograms that make up the plot.
def get_additional_stats(cls, histogram)
_check
Detailed check instructions for display on the validation page.
name
A unique name to be used as the name of the ROOT object to be generated.
def set_fit_tf1(cls, histogram, fit_tf1)
def set_minimum(self, minimum)
def fill_into_grouped(self, histogram_template, xs, ys=None, weights=None, groupbys=None, groupby_label="group")
plot
The main plot object, may contain one or more (in case of stacked pltos) histograms.
def determine_range(self, xs, lower_bound=None, upper_bound=None, outlier_z_score=None, include_exceptionals=True)
def set_additional_stats_tf1(cls, histogram)
_ylabel
Y axes label of the validation plot.
referenceFileName
name of the reference file, if not None the binning will be read from there
def fill_into_tgrapherror(self, graph, xs, ys, filter=None)
def hist2d(self, xs, ys, weights=None, stackby=None, bins=(None, None), lower_bound=(None, None), upper_bound=(None, None), outlier_z_score=(None, None), include_exceptionals=(True, True), allow_discrete=(False, False), quantiles=None, is_expert=True)
lower_bound
lower left corner of the histogram
def determine_bin_range(self, xs, stackbys=None, n_bins=None, lower_bound=None, upper_bound=None, outlier_z_score=None, include_exceptionals=True)
def determine_bin_edges(self, xs, stackbys=None, bins=None, lower_bound=None, upper_bound=None, outlier_z_score=None, include_exceptionals=True, allow_discrete=False)
def gaus_slice_fit(cls, th2, name, z_score=None)
check
cached value of the user-check action for this plot
def fill_into_th1(self, histogram, xs, ys=None, weights=None, filter=None)
def get_exceptional_values(xs)
def convert_tprofile_to_tgrapherrors(cls, tprofile, abs_x=False)
def fill_into_tgraph(self, graph, xs, ys, filter=None)