{
"cells": [
{
"cell_type": "markdown",
"id": "5dcf5e4d",
"metadata": {},
"source": [
"# fit2dHist\n",
"Example to fit two histograms at the same time via the Fitter class.\n",
"\n",
"To execute this tutorial, you can do:\n",
"\n",
"```cpp\n",
"root > .x fit2dHist.C (executing via cling, slow)\n",
"```\n",
"\n",
" or\n",
"```cpp\n",
"root > .x fit2dHist.C+ (executing via ACLIC , fast, with Minuit)\n",
"root > .x fit2dHist.C+(2) (executing via ACLIC , fast, with Minuit2)\n",
"```\n",
"\n",
" or using the option to fit independently the 2 histos\n",
"```cpp\n",
"root > .x fit2dHist.C+(10) (via ACLIC, fast, independent fits with Minuit)\n",
"root > .x fit2dHist.C+(12) (via ACLIC, fast, independent fits with Minuit2)\n",
"```\n",
"\n",
"Note that you can also execute this script in batch with eg,\n",
"```cpp\n",
" root -b -q \"fit2dHist.C+(12)\"\n",
"```\n",
"\n",
"or execute interactively from the shell\n",
"```cpp\n",
" root fit2dHist.C+\n",
" root \"fit2dHist.C+(12)\"\n",
"```\n",
"\n",
"\n",
"\n",
"\n",
"**Author:** : Lorenzo Moneta, Rene Brun 18/01/2006 \n",
"This notebook tutorial was automatically generated with ROOTBOOK-izer from the macro found in the ROOT repository on Tuesday, May 19, 2026 at 08:24 PM."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "0d54ccc0",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:44.106385Z",
"iopub.status.busy": "2026-05-19T20:24:44.106275Z",
"iopub.status.idle": "2026-05-19T20:24:44.111962Z",
"shell.execute_reply": "2026-05-19T20:24:44.111679Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_44:31:44: error: use of undeclared identifier 'my2Dfunc'\n",
" tmp = (h1->GetBinContent(ix,iy) - my2Dfunc(x,(double*) p))/h1->GetBinError(ix,iy);\n",
" ^\n",
"input_line_44:42:44: error: use of undeclared identifier 'my2Dfunc'\n",
" tmp = (h2->GetBinContent(ix,iy) - my2Dfunc(x,(double *) p))/h2->GetBinError(ix,iy);\n",
" ^\n"
]
}
],
"source": [
"%%cpp -d\n",
"class MyFcn {\n",
" public:\n",
" TH2D *h1 = nullptr;\n",
" TH2D *h2 = nullptr;\n",
" int npfits = 0;\n",
"\n",
" MyFcn(TH2D * _h1, TH2D * _h2) :\n",
" h1(_h1), h2(_h2) {}\n",
"\n",
" double operator()(const double *p) {\n",
"\n",
" TAxis *xaxis1 = h1->GetXaxis();\n",
" TAxis *yaxis1 = h1->GetYaxis();\n",
" TAxis *xaxis2 = h2->GetXaxis();\n",
" TAxis *yaxis2 = h2->GetYaxis();\n",
"\n",
" int nbinX1 = h1->GetNbinsX();\n",
" int nbinY1 = h1->GetNbinsY();\n",
" int nbinX2 = h2->GetNbinsX();\n",
" int nbinY2 = h2->GetNbinsY();\n",
"\n",
" double chi2 = 0;\n",
" double x[2];\n",
" double tmp;\n",
" npfits = 0;\n",
" for (int ix = 1; ix <= nbinX1; ++ix) {\n",
" x[0] = xaxis1->GetBinCenter(ix);\n",
" for (int iy = 1; iy <= nbinY1; ++iy) {\n",
" if ( h1->GetBinError(ix,iy) > 0 ) {\n",
" x[1] = yaxis1->GetBinCenter(iy);\n",
" tmp = (h1->GetBinContent(ix,iy) - my2Dfunc(x,(double*) p))/h1->GetBinError(ix,iy);\n",
" chi2 += tmp*tmp;\n",
" npfits++;\n",
" }\n",
" }\n",
" }\n",
" for (int ix = 1; ix <= nbinX2; ++ix) {\n",
" x[0] = xaxis2->GetBinCenter(ix);\n",
" for (int iy = 1; iy <= nbinY2; ++iy) {\n",
" if ( h2->GetBinError(ix,iy) > 0 ) {\n",
" x[1] = yaxis2->GetBinCenter(iy);\n",
" tmp = (h2->GetBinContent(ix,iy) - my2Dfunc(x,(double *) p))/h2->GetBinError(ix,iy);\n",
" chi2 += tmp*tmp;\n",
" npfits++;\n",
" }\n",
" }\n",
" }\n",
" return chi2;\n",
"}\n",
"};"
]
},
{
"cell_type": "markdown",
"id": "a7d2b139",
"metadata": {},
"source": [
" Definition of a helper function: "
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "42747a61",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:44.131364Z",
"iopub.status.busy": "2026-05-19T20:24:44.131230Z",
"iopub.status.idle": "2026-05-19T20:24:44.134622Z",
"shell.execute_reply": "2026-05-19T20:24:44.134331Z"
}
},
"outputs": [],
"source": [
"%%cpp -d\n",
"\n",
"#include \"TH2D.h\"\n",
"#include \"TF2.h\"\n",
"#include \"TCanvas.h\"\n",
"#include \"TStyle.h\"\n",
"#include \"TRandom3.h\"\n",
"#include \"Fit/Fitter.h\"\n",
"#include \"TList.h\"\n",
"\n",
"#include \n",
"\n",
"double gauss2D(double *x, double *par) {\n",
" double z1 = double((x[0]-par[1])/par[2]);\n",
" double z2 = double((x[1]-par[3])/par[4]);\n",
" return par[0]*exp(-0.5*(z1*z1+z2*z2));\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "7890e258",
"metadata": {},
"source": [
" Definition of a helper function: "
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "fdbca3a9",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:44.144065Z",
"iopub.status.busy": "2026-05-19T20:24:44.143932Z",
"iopub.status.idle": "2026-05-19T20:24:44.146050Z",
"shell.execute_reply": "2026-05-19T20:24:44.145763Z"
}
},
"outputs": [],
"source": [
"%%cpp -d\n",
"double my2Dfunc(double *x, double *par) {\n",
" return gauss2D(x,&par[0]) + gauss2D(x,&par[5]);\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "a4b8fcff",
"metadata": {},
"source": [
" Definition of a helper function: "
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "8d8d0c7b",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:44.155315Z",
"iopub.status.busy": "2026-05-19T20:24:44.155199Z",
"iopub.status.idle": "2026-05-19T20:24:44.161740Z",
"shell.execute_reply": "2026-05-19T20:24:44.161342Z"
}
},
"outputs": [],
"source": [
"%%cpp -d\n",
"\n",
"void FillHisto(TH2D * h, int n, double * p) {\n",
"\n",
"\n",
" const double mx1 = p[1];\n",
" const double my1 = p[3];\n",
" const double sx1 = p[2];\n",
" const double sy1 = p[4];\n",
" const double mx2 = p[6];\n",
" const double my2 = p[8];\n",
" const double sx2 = p[7];\n",
" const double sy2 = p[9];\n",
" //const double w1 = p[0]*sx1*sy1/(p[5]*sx2*sy2);\n",
" const double w1 = 0.5;\n",
"\n",
" double x, y;\n",
" for (int i = 0; i < n; ++i) {\n",
" // generate randoms with larger Gaussians\n",
" gRandom->Rannor(x,y);\n",
"\n",
" double r = gRandom->Rndm(1);\n",
" if (r < w1) {\n",
" x = x*sx1 + mx1;\n",
" y = y*sy1 + my1;\n",
" }\n",
" else {\n",
" x = x*sx2 + mx2;\n",
" y = y*sy2 + my2;\n",
" }\n",
" h->Fill(x,y);\n",
"\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "09dd8317",
"metadata": {},
"source": [
" Arguments are defined. "
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "2ba03634",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:44.162859Z",
"iopub.status.busy": "2026-05-19T20:24:44.162748Z",
"iopub.status.idle": "2026-05-19T20:24:44.489938Z",
"shell.execute_reply": "2026-05-19T20:24:44.489446Z"
}
},
"outputs": [],
"source": [
"int option=1;"
]
},
{
"cell_type": "markdown",
"id": "127dc955",
"metadata": {},
"source": [
"create two histograms"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "8596024b",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:44.491547Z",
"iopub.status.busy": "2026-05-19T20:24:44.491433Z",
"iopub.status.idle": "2026-05-19T20:24:44.710925Z",
"shell.execute_reply": "2026-05-19T20:24:44.705243Z"
}
},
"outputs": [],
"source": [
"int nbx1 = 50;\n",
"int nby1 = 50;\n",
"int nbx2 = 50;\n",
"int nby2 = 50;\n",
"double xlow1 = 0.;\n",
"double ylow1 = 0.;\n",
"double xup1 = 10.;\n",
"double yup1 = 10.;\n",
"double xlow2 = 5.;\n",
"double ylow2 = 5.;\n",
"double xup2 = 20.;\n",
"double yup2 = 20.;\n",
"\n",
"auto h1 = new TH2D(\"h1\",\"core\",nbx1,xlow1,xup1,nby1,ylow1,yup1);\n",
"auto h2 = new TH2D(\"h2\",\"tails\",nbx2,xlow2,xup2,nby2,ylow2,yup2);\n",
"\n",
"double iniParams[10] = { 100, 6., 2., 7., 3, 100, 12., 3., 11., 2. };"
]
},
{
"cell_type": "markdown",
"id": "f7275558",
"metadata": {},
"source": [
"create fit function"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "ca32064a",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:44.717374Z",
"iopub.status.busy": "2026-05-19T20:24:44.717241Z",
"iopub.status.idle": "2026-05-19T20:24:44.923354Z",
"shell.execute_reply": "2026-05-19T20:24:44.922714Z"
}
},
"outputs": [],
"source": [
"TF2 * func = new TF2(\"func\",my2Dfunc,xlow2,xup2,ylow2,yup2, 10);\n",
"func->SetParameters(iniParams);"
]
},
{
"cell_type": "markdown",
"id": "c760fc7a",
"metadata": {},
"source": [
"fill Histos"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "71cd3934",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:44.925537Z",
"iopub.status.busy": "2026-05-19T20:24:44.925412Z",
"iopub.status.idle": "2026-05-19T20:24:45.279782Z",
"shell.execute_reply": "2026-05-19T20:24:45.278549Z"
}
},
"outputs": [],
"source": [
"int n1 = 1000000;\n",
"int n2 = 1000000;\n",
"FillHisto(h1,n1,iniParams);\n",
"FillHisto(h2,n2,iniParams);"
]
},
{
"cell_type": "markdown",
"id": "d6319929",
"metadata": {},
"source": [
"scale histograms to same heights (for fitting)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "712d5ccd",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:45.281462Z",
"iopub.status.busy": "2026-05-19T20:24:45.281337Z",
"iopub.status.idle": "2026-05-19T20:24:45.483798Z",
"shell.execute_reply": "2026-05-19T20:24:45.483126Z"
}
},
"outputs": [],
"source": [
"double dx1 = (xup1-xlow1)/double(nbx1);\n",
"double dy1 = (yup1-ylow1)/double(nby1);\n",
"double dx2 = (xup2-xlow2)/double(nbx2);\n",
"double dy2 = (yup2-ylow2)/double(nby2);"
]
},
{
"cell_type": "markdown",
"id": "3589f11d",
"metadata": {},
"source": [
"scale histo 2 to scale of 1"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "c33713c6",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:45.485857Z",
"iopub.status.busy": "2026-05-19T20:24:45.485736Z",
"iopub.status.idle": "2026-05-19T20:24:45.701431Z",
"shell.execute_reply": "2026-05-19T20:24:45.695668Z"
}
},
"outputs": [],
"source": [
"h2->Sumw2();\n",
"h2->Scale( ( double(n1) * dx1 * dy1 ) / ( double(n2) * dx2 * dy2 ) );\n",
"\n",
"bool global = false;\n",
"if (option > 10) global = true;"
]
},
{
"cell_type": "markdown",
"id": "c3072c83",
"metadata": {},
"source": [
"do global combined fit"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "ffe7f921",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:45.715026Z",
"iopub.status.busy": "2026-05-19T20:24:45.714890Z",
"iopub.status.idle": "2026-05-19T20:24:45.916916Z",
"shell.execute_reply": "2026-05-19T20:24:45.916621Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_62:10:4: error: unknown type name 'MyFcn'\n",
" MyFcn myFcn(h1,h2);\n",
" ^\n"
]
}
],
"source": [
"if (global) {\n",
" // fill data structure for fit (coordinates + values + errors)\n",
" std::cout << \"Do global fit\" << std::endl;\n",
" // fit now all the function together\n",
"\n",
" ROOT::Fit::Fitter fitter;\n",
" //The default minimizer is Minuit, you can also try Minuit2\n",
"\n",
" MyFcn myFcn(h1,h2);\n",
" fitter.SetFCN(10, myFcn);\n",
" if (option%10 == 2) fitter.Config().SetMinimizer(\"Minuit2\");\n",
"\n",
" // set parameter initial value, name and step size\n",
" for (int i = 0; i < 10; ++i) {\n",
" fitter.Config().ParSettings(i) = ROOT::Fit::ParameterSettings(func->GetParName(i), func->GetParameter(i), 0.01);\n",
" }\n",
"\n",
" bool ret = fitter.FitFCN();\n",
" if (!ret) {\n",
" Error(\"fit2DHist\",\"Fit Failed to converge\");\n",
" return -1;\n",
" }\n",
"\n",
" //get result\n",
" double minParams[10];\n",
" double parErrors[10];\n",
" for (int i = 0; i < 10; ++i) {\n",
" minParams[i] = fitter.Result().Parameter(i);\n",
" parErrors[i] = fitter.Result().Error(i);\n",
" }\n",
" double chi2 = fitter.Result().MinFcnValue();\n",
" double edm = fitter.Result().Edm();\n",
" int npfits = myFcn.npfits;\n",
"\n",
" func->SetParameters(minParams);\n",
" func->SetParErrors(parErrors);\n",
" func->SetChisquare(chi2);\n",
" int ndf = npfits - fitter.Result().NFreeParameters();\n",
" func->SetNDF(ndf);\n",
"\n",
" // add to list of functions\n",
" h1->GetListOfFunctions()->Add(func);\n",
" h2->GetListOfFunctions()->Add(func);\n",
"}\n",
"else {\n",
" // fit independently\n",
" h1->Fit(func, \"0\");\n",
" h2->Fit(func, \"0\");\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "c6b7d588",
"metadata": {},
"source": [
"Create a new canvas."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "2591b8f4",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:45.941559Z",
"iopub.status.busy": "2026-05-19T20:24:45.941418Z",
"iopub.status.idle": "2026-05-19T20:24:46.266182Z",
"shell.execute_reply": "2026-05-19T20:24:46.265617Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"\n",
"
\n",
"\n",
"\n",
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"TCanvas * c1 = new TCanvas(\"c1\",\"Two HIstogram Fit example\",100,10,900,800);\n",
"c1->Divide(2,2);\n",
"gStyle->SetOptFit();\n",
"gStyle->SetStatY(0.6);\n",
"\n",
"c1->cd(1);\n",
"h1->Draw();\n",
"func->SetRange(xlow1,ylow1,xup1,yup1);\n",
"func->DrawCopy(\"cont3 same\");\n",
"c1->cd(2);\n",
"h1->Draw(\"lego\");\n",
"func->DrawCopy(\"surf1 same\");\n",
"c1->cd(3);\n",
"func->SetRange(xlow2,ylow2,xup2,yup2);\n",
"h2->Draw();\n",
"func->DrawCopy(\"cont3 same\");\n",
"c1->cd(4);\n",
"h2->Draw(\"lego\");\n",
"gPad->SetLogz();\n",
"func->Draw(\"surf1 same\");\n",
"\n",
"return 0;"
]
},
{
"cell_type": "markdown",
"id": "eff07ffe",
"metadata": {},
"source": [
"Draw all canvases "
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "24925018",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:24:46.269738Z",
"iopub.status.busy": "2026-05-19T20:24:46.269585Z",
"iopub.status.idle": "2026-05-19T20:24:46.476271Z",
"shell.execute_reply": "2026-05-19T20:24:46.475521Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"\n",
"
\n",
"\n",
"\n",
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"gROOT->GetListOfCanvases()->Draw()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "ROOT C++",
"language": "c++",
"name": "root"
},
"language_info": {
"codemirror_mode": "text/x-c++src",
"file_extension": ".C",
"mimetype": " text/x-c++src",
"name": "c++"
}
},
"nbformat": 4,
"nbformat_minor": 5
}