Skip to content

Commit

Permalink
Build with Poisson error support
Browse files Browse the repository at this point in the history
  • Loading branch information
linev committed Nov 29, 2024
1 parent 92a393b commit 7fd72dc
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 42 deletions.
133 changes: 95 additions & 38 deletions build/jsroot.js
Original file line number Diff line number Diff line change
Expand Up @@ -73985,8 +73985,8 @@ __proto__: null,
TPavePainter: TPavePainter
});

const kCARTESIAN = 1, kPOLAR = 2, kCYLINDRICAL = 3, kSPHERICAL = 4, kRAPIDITY = 5;

const kCARTESIAN = 1, kPOLAR = 2, kCYLINDRICAL = 3, kSPHERICAL = 4, kRAPIDITY = 5,
kNormal$1 = 0, kPoisson = 1, kPoisson2 = 2;
/**
* @summary Class to decode histograms draw options
* @desc All options started from capital letter are major drawing options
Expand All @@ -74010,7 +74010,7 @@ class THistDrawOptions {
Text: false, TextAngle: 0, TextKind: '', Char: 0, Color: false, Contour: 0, Cjust: false,
Lego: 0, Surf: 0, Off: 0, Tri: 0, Proj: 0, AxisPos: 0, Ortho: gStyle.fOrthoCamera,
Spec: false, Pie: false, List: false, Zscale: false, Zvert: true, PadPalette: false,
Candle: '', Violin: '', Scaled: null, Circular: 0,
Candle: '', Violin: '', Scaled: null, Circular: 0, Poisson: kNormal$1,
GLBox: 0, GLColor: false, Project: '', ProfileProj: '', Profile2DProj: '', System: kCARTESIAN,
AutoColor: false, NoStat: false, ForceStat: false, PadStats: false, PadTitle: false, AutoZoom: false,
HighRes: 0, Zero: 1, Palette: 0, BaseLine: false, ShowEmpty: false,
Expand Down Expand Up @@ -74129,6 +74129,8 @@ class THistDrawOptions {
if (d.check('XTITLE:', true)) histo.fXaxis.fTitle = decodeURIComponent(d.part.toLowerCase());
if (d.check('YTITLE:', true)) histo.fYaxis.fTitle = decodeURIComponent(d.part.toLowerCase());
if (d.check('ZTITLE:', true)) histo.fZaxis.fTitle = decodeURIComponent(d.part.toLowerCase());
if (d.check('POISSON2')) this.Poisson = kPoisson2;
if (d.check('POISSON')) this.Poisson = kPoisson;

if (d.check('SHOWEMPTY')) this.ShowEmpty = true;

Expand Down Expand Up @@ -76461,6 +76463,33 @@ class THistPainter extends ObjectPainter {
return `[${funcs.axisAsText(name, x1)}, ${funcs.axisAsText(name, x2)})`;
}

/** @summary Internal method to extract up/down errors for the bin
* @private */
getBinErrors(histo, bin, content) {
const err = histo.getBinError(bin),
res = { low: err, up: err },
kind = this.options.Poisson || histo.fBinStatErrOpt;

if (!kind || (histo.fSumw2.fN && histo.fTsumw !== histo.fTsumw2))
return res;

const alpha = (kind === kPoisson2) ? 0.05 : 1 - 0.682689492,
n = Math.round(content);

if (content < 0)
return res;

res.poisson = true; // indicate poisson error

if (n === 0)
res.low = 0;
else
res.low = content - gamma_quantile(alpha/2, n, 1);

res.up = gamma_quantile_c(alpha/2, n + 1, 1) - content;
return res;
}

/** @summary generic draw function for histograms
* @private */
static async _drawHist(painter, opt) {
Expand Down Expand Up @@ -78254,12 +78283,20 @@ let TH2Painter$2 = class TH2Painter extends THistPainter {
let text = (binz === Math.round(binz)) ? binz.toString() : floatToString(binz, gStyle.fPaintTextFormat);

if (show_err) {
const errz = histo.getBinError(histo.getBin(i+1, j+1)),
lble = (errz === Math.round(errz)) ? errz.toString() : floatToString(errz, gStyle.fPaintTextFormat);
if (this.options.TextLine)
text += '\xB1' + lble;
else
text = `#splitmline{${text}}{#pm${lble}}`;
const errs = this.getBinErrors(histo, histo.getBin(i + 1, j + 1), binz);
if (errs.poisson) {
const lble = `-${floatToString(errs.low, gStyle.fPaintTextFormat)} +${floatToString(errs.up, gStyle.fPaintTextFormat)}`;
if (this.options.TextLine)
text += ' ' + lble;
else
text = `#splitmline{${text}}{${lble}}`;
} else {
const lble = (errs.up === Math.round(errs.up)) ? errs.up.toString() : floatToString(errs.up, gStyle.fPaintTextFormat);
if (this.options.TextLine)
text += '\xB1' + lble;
else
text = `#splitmline{${text}}{#pm${lble}}`;
}
}

let x, y, width, height;
Expand Down Expand Up @@ -79356,8 +79393,9 @@ let TH2Painter$2 = class TH2Painter extends THistPainter {
/** @summary Provide text information (tooltips) for histogram bin */
getBinTooltips(i, j) {
const histo = this.getHisto(),
profile2d = this.matchObjectType(clTProfile2D) && isFunc(histo.getBinEntries);
let binz = histo.getBinContent(i+1, j+1);
profile2d = this.matchObjectType(clTProfile2D) && isFunc(histo.getBinEntries),
bincontent = histo.getBinContent(i+1, j+1);
let binz = bincontent;

if (histo.$baseh)
binz -= histo.$baseh.getBinContent(i+1, j+1);
Expand All @@ -79369,8 +79407,11 @@ let TH2Painter$2 = class TH2Painter extends THistPainter {
'content = ' + ((binz === Math.round(binz)) ? binz : floatToString(binz, gStyle.fStatFormat))];

if ((this.options.TextKind === 'E') || profile2d) {
const errz = histo.getBinError(histo.getBin(i+1, j+1));
lines.push('error = ' + ((errz === Math.round(errz)) ? errz.toString() : floatToString(errz, gStyle.fPaintTextFormat)));
const errs = this.getBinErrors(histo, histo.getBin(i + 1, j + 1), bincontent);
if (errs.poisson)
lines.push('error low = ' + floatToString(errs.low, gStyle.fPaintTextFormat), 'error up = ' + floatToString(errs.up, gStyle.fPaintTextFormat));
else
lines.push('error = ' + floatToString(errs.up, gStyle.fPaintTextFormat));
}

if (profile2d) {
Expand Down Expand Up @@ -81747,7 +81788,7 @@ function drawBinsError3D(painter, is_v7 = false) {
zmin = main.z_handle.getScaleMin(),
zmax = main.z_handle.getScaleMax(),
test_cutg = painter.options.cutg;
let i, j, bin, binz, binerr, x1, y1, x2, y2, z1, z2,
let i, j, bin, binz, errs, x1, y1, x2, y2, z1, z2,
nsegments = 0, lpos = null, binindx = null, lindx = 0;

const check_skip_min = () => {
Expand All @@ -81756,7 +81797,7 @@ function drawBinsError3D(painter, is_v7 = false) {
return !painter.options.ShowEmpty;
};

// loop over the points - first loop counts points, second fill arrays
// loop over the points - first loop counts points, second fill arrays
for (let loop = 0; loop < 2; ++loop) {
for (i = handle.i1; i < handle.i2; ++i) {
x1 = handle.grx[i];
Expand All @@ -81773,14 +81814,14 @@ function drawBinsError3D(painter, is_v7 = false) {
if (loop === 0) { nsegments += 3; continue; }

bin = histo.getBin(i + 1, j + 1);
binerr = histo.getBinError(bin);
errs = painter.getBinErrors(histo, bin, binz);
binindx[lindx / 18] = bin;

y1 = handle.gry[j];
y2 = handle.gry[j + 1];

z1 = main.grz((binz - binerr < zmin) ? zmin : binz - binerr);
z2 = main.grz((binz + binerr > zmax) ? zmax : binz + binerr);
z1 = main.grz((binz - errs.low < zmin) ? zmin : binz - errs.low);
z2 = main.grz((binz + errs.up > zmax) ? zmax : binz + errs.up);

lpos[lindx] = x1; lpos[lindx + 3] = x2;
lpos[lindx + 1] = lpos[lindx + 4] = (y1 + y2) / 2;
Expand Down Expand Up @@ -82317,7 +82358,7 @@ let TH1Painter$2 = class TH1Painter extends THistPainter {
this.scan_xright = right;

const profile = this.isTProfile();
let hmin = 0, hmin_nz = 0, hmax = 0, hsum = 0, first = true, value, err;
let hmin = 0, hmin_nz = 0, hmax = 0, hsum = 0, first = true, value, errs = { low: 0, up: 0 };

for (let i = 0; i < this.nbinsx; ++i) {
value = histo.getBinContent(i + 1);
Expand All @@ -82334,10 +82375,11 @@ let TH1Painter$2 = class TH1Painter extends THistPainter {
first = false;
}

err = this.options.Error ? histo.getBinError(i + 1) : 0;
if (this.options.Error)
errs = this.getBinErrors(histo, i + 1, value);

hmin = Math.min(hmin, value - err);
hmax = Math.max(hmax, value + err);
hmin = Math.min(hmin, value - errs.low);
hmax = Math.max(hmax, value + errs.up);

if (f1) {
// similar code as in THistPainter, line 7196
Expand Down Expand Up @@ -82748,11 +82790,12 @@ let TH1Painter$2 = class TH1Painter extends THistPainter {
if (funcs.logx && (x <= 0)) continue;
const grx = Math.round(funcs.grx(x)),
y = histo.getBinContent(i+1),
yerr = histo.getBinError(i+1);
if (funcs.logy && (y-yerr < funcs.scale_ymin)) continue;
yerrs = this.getBinErrors(histo, i + 1, y);
if (funcs.logy && (y - yerrs.low < funcs.scale_ymin))
continue;

bins1.push({ grx, gry: Math.round(funcs.gry(y + yerr)) });
bins2.unshift({ grx, gry: Math.round(funcs.gry(y - yerr)) });
bins1.push({ grx, gry: Math.round(funcs.gry(y + yerrs.up)) });
bins2.unshift({ grx, gry: Math.round(funcs.gry(y - yerrs.low)) });
}

const line = this.options.ErrorKind !== 4,
Expand Down Expand Up @@ -82864,9 +82907,9 @@ let TH1Painter$2 = class TH1Painter extends THistPainter {
if (startmidx === undefined) startmidx = midx;
my = Math.round(funcs.gry(bincont));
if (show_errors) {
binerr = histo.getBinError(bin+1);
yerr1 = Math.round(my - funcs.gry(bincont + binerr)); // up
yerr2 = Math.round(funcs.gry(bincont - binerr) - my); // down
binerr = this.getBinErrors(histo, bin + 1, bincont);
yerr1 = Math.round(my - funcs.gry(bincont + binerr.up)); // up
yerr2 = Math.round(funcs.gry(bincont - binerr.low) - my); // low
} else
yerr1 = yerr2 = 20;

Expand Down Expand Up @@ -83175,7 +83218,11 @@ let TH1Painter$2 = class TH1Painter extends THistPainter {
tips.push(`x = ${xlbl}`, `y = ${funcs.axisAsText('y', cont)}`);
if (this.options.Error) {
if (xlbl[0] === '[') tips.push(`error x = ${((x2 - x1) / 2).toPrecision(4)}`);
tips.push(`error y = ${histo.getBinError(bin + 1).toPrecision(4)}`);
const errs = this.getBinErrors(histo, bin + 1, cont);
if (errs.poisson)
tips.push(`error low = ${errs.low.toPrecision(4)}`, `error up = ${errs.up.toPrecision(4)}`);
else
tips.push(`error y = ${errs.up.toPrecision(4)}`);
}
} else {
tips.push(`bin = ${bin+1}`, `x = ${xlbl}`);
Expand Down Expand Up @@ -83292,15 +83339,16 @@ let TH1Painter$2 = class TH1Painter extends THistPainter {
if (this.markeratt) msize = Math.max(msize, this.markeratt.getFullSize());

if (this.options.Error) {
const cont = histo.getBinContent(findbin+1),
binerr = histo.getBinError(findbin+1);
const cont = histo.getBinContent(findbin + 1),
binerrs = this.getBinErrors(histo, findbin + 1, cont);

gry1 = Math.round(funcs.gry(cont + binerr)); // up
gry2 = Math.round(funcs.gry(cont - binerr)); // down
gry1 = Math.round(funcs.gry(cont + binerrs.up)); // up
gry2 = Math.round(funcs.gry(cont - binerrs.low)); // low

if ((cont === 0) && this.isTProfile()) findbin = null;
if ((cont === 0) && this.isTProfile())
findbin = null;

const dx = (grx2-grx1)*this.options.errorX;
const dx = (grx2 - grx1)*this.options.errorX;
grx1 = Math.round(midx - dx);
grx2 = Math.round(midx + dx);
}
Expand Down Expand Up @@ -150234,10 +150282,12 @@ let THStackPainter$2 = class THStackPainter extends ObjectPainter {
j2 = hist.fYaxis.fLast;
}
}
let err = 0;
for (let j = j1; j <= j2; ++j) {
for (let i = i1; i <= i2; ++i) {
const val = hist.getBinContent(i, j),
err = witherr ? hist.getBinError(hist.getBin(i, j)) : 0;
const val = hist.getBinContent(i, j);
if (witherr)
err = hist.getBinError(hist.getBin(i, j));
if (logscale && (val - err <= 0))
continue;
if (domin && (first || (val - err < res.min)))
Expand Down Expand Up @@ -163459,6 +163509,13 @@ class RHistPainter extends RObjectPainter {
if (!not_shown) pp.showPadButtons();
}

/** @summary Return histo bin errors
* @private */
getBinErrors(histo, bin /* , binz */) {
const err = histo.getBinError(bin);
return { low: err, up: err };
}

/** @summary get tool tips used in 3d mode */
get3DToolTip(indx) {
const histo = this.getHisto(),
Expand Down
1 change: 1 addition & 0 deletions changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
1. Support "same" option for first histogram, draw direcly on pad
1. Support different angle coordiantes in `TGraphPolargram`, handle 'N' and 'O' draw options
1. Implement 'arc' draw option for `TPave`
1. Support Poisson errors for TH1/TH2, https://root-forum.cern.ch/t/62335/
1. Fix - handle `TPave` NDC position also when fInit is not set
1. Fix - correctly position title according to gStyle->GetTitleAlign()
1. Fix - correctly handle tooltip events for `TGraphPolar`
Expand Down
7 changes: 3 additions & 4 deletions modules/hist2d/THistPainter.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import { ensureTCanvas } from '../gpad/TCanvasPainter.mjs';
import { gamma_quantile, gamma_quantile_c } from '../base/math.mjs';


const kCARTESIAN = 1, kPOLAR = 2, kCYLINDRICAL = 3, kSPHERICAL = 4, kRAPIDITY = 5;

const kNormal = 0, kPoisson = 1, kPoisson2 = 2;
const kCARTESIAN = 1, kPOLAR = 2, kCYLINDRICAL = 3, kSPHERICAL = 4, kRAPIDITY = 5,
kNormal = 0, kPoisson = 1, kPoisson2 = 2;

Check warning on line 13 in modules/hist2d/THistPainter.mjs

View workflow job for this annotation

GitHub Actions / build-macos (18.x)

Multiple spaces found before 'kPoisson'

Check warning on line 13 in modules/hist2d/THistPainter.mjs

View workflow job for this annotation

GitHub Actions / build-macos (20.x)

Multiple spaces found before 'kPoisson'

Check warning on line 13 in modules/hist2d/THistPainter.mjs

View workflow job for this annotation

GitHub Actions / build-ubuntu (18.x, g++-11)

Multiple spaces found before 'kPoisson'

Check warning on line 13 in modules/hist2d/THistPainter.mjs

View workflow job for this annotation

GitHub Actions / build-windows (18.x)

Multiple spaces found before 'kPoisson'

Check warning on line 13 in modules/hist2d/THistPainter.mjs

View workflow job for this annotation

GitHub Actions / build-ubuntu (18.x, g++-12)

Multiple spaces found before 'kPoisson'

Check warning on line 13 in modules/hist2d/THistPainter.mjs

View workflow job for this annotation

GitHub Actions / build-windows (20.x)

Multiple spaces found before 'kPoisson'

Check warning on line 13 in modules/hist2d/THistPainter.mjs

View workflow job for this annotation

GitHub Actions / build-ubuntu (18.x, g++-13)

Multiple spaces found before 'kPoisson'

Check warning on line 13 in modules/hist2d/THistPainter.mjs

View workflow job for this annotation

GitHub Actions / build-ubuntu (20.x, g++-11)

Multiple spaces found before 'kPoisson'

Check warning on line 13 in modules/hist2d/THistPainter.mjs

View workflow job for this annotation

GitHub Actions / build-ubuntu (20.x, g++-12)

Multiple spaces found before 'kPoisson'

Check warning on line 13 in modules/hist2d/THistPainter.mjs

View workflow job for this annotation

GitHub Actions / build-ubuntu (20.x, g++-13)

Multiple spaces found before 'kPoisson'
/**
* @summary Class to decode histograms draw options
* @desc All options started from capital letter are major drawing options
Expand Down Expand Up @@ -2495,7 +2494,7 @@ class THistPainter extends ObjectPainter {
res = { low: err, up: err },
kind = this.options.Poisson || histo.fBinStatErrOpt;

if (!kind || (histo.fSumw2.fN && histo.fTsumw != histo.fTsumw2))
if (!kind || (histo.fSumw2.fN && histo.fTsumw !== histo.fTsumw2))
return res;

const alpha = (kind === kPoisson2) ? 0.05 : 1 - 0.682689492,
Expand Down

0 comments on commit 7fd72dc

Please sign in to comment.