/// <summary> /// Returna plot that has as its content its value divided by its error, and zero errors. /// </summary> /// <param name="ctx"></param> /// <param name="source"></param> /// <returns></returns> public static NTH1 asSigma(IScopeContext ctx, NTH1 plot) { var result = plot.Clone() as NTH1; Tags.CopyTags(ctx, plot, result); result.Reset(); // Besure to do the overflow bins as well. for (int i_bin_x = 0; i_bin_x < result.NbinsX + 2; i_bin_x++) { for (int i_bin_y = 0; i_bin_y < result.NbinsY + 2; i_bin_y++) { for (int i_bin_z = 0; i_bin_z < result.NbinsZ + 2; i_bin_z++) { var v = plot.GetBinContent(i_bin_x, i_bin_y, i_bin_z); var e = plot.GetBinError(i_bin_x, i_bin_y, i_bin_z); var sig = e == 0 ? 0.0 : v / e; result.SetBinContent(i_bin_x, i_bin_y, i_bin_z, sig); } } } return(result); }
private static void GetFilePathFromObjects(NTObject[] obj, out NTH1 hPath, out NTH1 hSize) { hPath = null; hSize = null; foreach (var h in obj.Where(o => o != null)) { if (h.Name.EndsWith("_size")) { hSize = h as NTH1; } else { hPath = h as NTH1; } } }
/// <summary> /// Create a plot out of a single ratio point. /// </summary> /// <param name="template"></param> /// <param name="xpos"></param> /// <param name="numerator"></param> /// <param name="denominator"></param> /// <returns></returns> public static NTH1 ratioPointAsPlot(NTH1 template, double xpos, double numerator, double denominator) { // Make a clone, and zero it out. var hNum = template.Clone() as NTH1; hNum.Reset(); var hDen = hNum.Clone() as NTH1; // Now, do the division var index = hNum.FindBin(xpos); hNum.SetBinContent(index, numerator); hNum.SetBinError(index, Math.Sqrt(numerator)); hDen.SetBinContent(index, denominator); hDen.SetBinError(index, Math.Sqrt(denominator)); hNum.Divide(hDen); return(hNum); }
/// <summary> /// Extract the file name from the NTObjects we are getting fed. /// </summary> /// <param name="obj"></param> /// <param name="hPath"></param> /// <param name="hSize"></param> private static void GetFilePathFromObjects(NTObject[] obj, out NTH1 hPath, out NTH1 hSize) { hPath = null; hSize = null; foreach (var h in obj) { if (h.Name.EndsWith("_size")) { hSize = h as NTH1; } else { hPath = h as NTH1; } } if (hPath == null || hSize == null) { throw new InvalidOperationException("Internal error - cache is missing either the path for a CSV file or its size"); } }
/// <summary> /// Generate an integral plot out of the current plot. /// </summary> /// <param name="ctx"></param> /// <param name="plot"></param> /// <returns></returns> public static ROOTNET.Interface.NTH1 asIntegral(IScopeContext ctx, NTH1 plot, bool sumForward = true) { // Clone it. var result = plot.Clone() as ROOTNET.Interface.NTH1;; Tags.CopyTags(ctx, plot, result); // get the numbers out var numbers = Enumerable.Range(0, result.NbinsX + 1) .Select(ibin => result.GetBinContent(ibin)); // Now, sum them up. var total = numbers.Sum(); // And transform them. double runningTotal = sumForward ? 0 : total; Func <double, double> calRunningTotal; if (sumForward) { calRunningTotal = p => runningTotal += p; } else { calRunningTotal = p => runningTotal -= p; } numbers = numbers .Select(n => calRunningTotal(n)) .ToArray(); // The runningTotal has side effects, so we better put a stop. // Stuff them back in result. foreach (var n in Enumerable.Range(0, result.NbinsX + 1).Zip(numbers, (ibin, v) => Tuple.Create(ibin, v))) { result.SetBinContent(n.Item1, n.Item2); } return(result); }
public DrawingObject(ROOTNET.Interface.NTH1 p) { _p = p; }
/// <summary> /// Calculate on the fly the signal and rejection curves for the two plots. /// </summary> /// <param name="xAxisHist"></param> /// <param name="yAxisHist"></param> /// <returns></returns> private static NTGraph CalculateROC(NTH1 xAxisHist, NTH1 yAxisHist, string name, string title) { var tg = new ROOTNET.NTGraph(xAxisHist.NbinsX, xAxisHist.Data(), yAxisHist.Data()); tg.Xaxis.Title = "Fractional Signal Efficiency"; tg.Yaxis.Title = "Fractional Background Rejection"; tg.Title = title; tg.Name = name; return tg; }
/// <summary> /// Return the x-axis value for a particular efficiency. /// </summary> /// <param name="r"></param> /// <param name="bv"></param> /// <returns></returns> private static double CalcEffValue(NTH1 r, double bv, bool greater = true) { var firstValue = Range(0, r.NbinsX) .Where(bin => greater ? r.GetBinContent(bin) > bv : r.GetBinContent(bin) < bv) .FirstOrDefault(); return r.Xaxis.GetBinCenter(firstValue); }
/// <summary> /// Given a cut value (x axis value), return the value of the histo at that point. /// </summary> /// <param name="hist"></param> /// <param name="xAxisValue"></param> /// <returns></returns> private static double LookupEffAtCut(NTH1 hist, double xAxisValue) { var bin = hist.Xaxis.FindBin(xAxisValue); return hist.GetBinContent(bin); }
/// <summary> /// Generate 2D turn on graphs from input signal and background plots. /// </summary> /// <param name="ctx"></param> /// <param name="plot"></param> /// <param name="xCutGreaterThan"></param> /// <param name="yCutGreaterThan"></param> /// <returns>A graph with the signal eff along the x axis, and the background eff along the y axis</returns> public static ROOTNET.Interface.NTGraph asROC(IScopeContext ctx, NTH1 signal, NTH1 background, bool xCutGreaterThan = true, bool yCutGreaterThan = true) { // The two plots must be identical. if (signal.NbinsX != background.NbinsX) { throw new ArgumentException($"AsROC requires the same binning on the input plots (signal has {signal.NbinsX} and background has {background.NbinsX})."); } // Now, develop pairs of values so we can track the background and signal efficiency. var numberPairs = Enumerable.Range(0, signal.NbinsX + 1) .Select(ibin => Tuple.Create(signal.GetBinContent(ibin), background.GetBinContent(ibin))); // Now, turn them into efficiencies if we need to. var signalTotal = numberPairs.Select(p => p.Item1).Sum(); var backgroundTotal = numberPairs.Select(p => p.Item2).Sum(); double runningTotalSignal = xCutGreaterThan ? 0 : signalTotal; double runningTotalBackground = yCutGreaterThan ? 0 : backgroundTotal; Func <double, double> calcRunningSignal, calcRunningBackground; if (xCutGreaterThan) { calcRunningSignal = p => runningTotalSignal += p; } else { calcRunningSignal = p => runningTotalSignal -= p; } if (yCutGreaterThan) { calcRunningBackground = p => runningTotalBackground += p; } else { calcRunningBackground = p => runningTotalBackground -= p; } numberPairs = numberPairs .Select(p => Tuple.Create(calcRunningSignal(p.Item1), calcRunningBackground(p.Item2))) .Select(p => Tuple.Create(p.Item1 / signalTotal, p.Item2 / backgroundTotal)) .ToArray(); // Side effects, make sure this gets run only once! // Remove the non-unique pairs, since this is going to be a scatter plot. numberPairs = numberPairs .Distinct(new TupleCompare()); // Next, draw them in a graph. var pts = numberPairs.ToArray(); var graf = new ROOTNET.NTGraph(pts.Length, pts.Select(p => p.Item1).ToArray(), pts.Select(p => p.Item2).ToArray()); graf.FillColor = 0; // Make sure the background is white // Track tags for the signal (assuming the background is "common"), and track everything else. Tags.CopyTags(ctx, signal, graf); graf.SetTitle($"{signal.Title} ROC"); graf.Xaxis.Title = $"Efficiency (signal)"; graf.Yaxis.Title = $"Efficiency (background)"; graf.Histogram.Maximum = 1.0; graf.Histogram.Minimum = 0.0; return(graf); }
/// <summary> /// Internally do the divide, hidden in the monad. /// </summary> /// <param name="hn"></param> /// <param name="hd"></param> /// <returns></returns> private static NTH1 InternalDivide(NTH1 hn, NTH1 hd) { var result = hn.Clone(string.Format("{0}_div_{1}", hn.Name, hd.Name)) as NTH1; if (!result.Divide(hd)) { throw new HistogramOperationErrorException($"Failure to divide {hn.Name} by {hd.Name}"); } return result; }