Exclusion Fit and Extrapolation

I am trying to fit a histogram in a specific range, while excluding some entries in there. The procedure seems pretty forward and the fitting parameters seem reasonable. What is bizarre however is the drawing of the “exclusion fitting function” : There is a sinking, right at the coordinate where I define the exclusion range, as shown in the image


My code is the following

[code] #include “Riostream.h”

Bool_t reject;
Double_t exclude(Double_t *x, Double_t *par)
{
if (reject && x[0] > 420 && x[0] < 460) {
TF1::RejectPoint();
return 0;
}
//return par[0] + par[1]*x[0];
return par[0] + par[1]*x[0] + par[2]*x[0]*x[0];
}

void testFitExclude(){//(char * file_c) {

gROOT->SetStyle("Plain");
gStyle->SetOptStat(0000);
gStyle->SetOptFit(1111);
gStyle->SetOptTitle(0);

TCanvas *c = new TCanvas("c", "c");
c->SetFillColor(5);
c->SetFrameFillColor(10);

TString dir = gSystem->UnixPathName(gInterpreter->GetCurrentMacroName());
dir.ReplaceAll("testFitExclude.C","");
dir.ReplaceAll("/./","/");
ifstream in;
in.open(Form("%sB_p_1500_120.dat",dir.Data()));//

Float_t x,y;//,z,w,k,l,m;
Int_t nlines = 0;
TH1F *h1 = new TH1F("h1","Experimental",1024,1,1024);

while (1) {
in >> x >> y;
if (!in.good()) break;
if (nlines < 5) printf("x=%8f, y=%8f\n");
h1->Fill(x,y);
nlines++;
}
printf("Found %d points\n",nlines);

in.close();
h1->SetLineColor(kBlack);
h1->SetLineWidth(1);
h1->Draw();
h1->GetXaxis()->SetTitle("Channels");
h1->GetYaxis()->SetTitle("Events");

reject = kTRUE;
TF1 *fit = new TF1("fit",exclude,0,1024,3);//<--------------------------
fit->SetParameters(2,-1);
h1->Fit("fit","W",NULL,400,850);
reject = kFALSE;

//TF1 *fit = new TF1("fit","pol2",0,1024);

// h1->Fit(“fit”,“W”,NULL,465,850);//465 850
h1->GetFunction(“fit”)->SetLineColor(kRed);

TF1  *extraPol = new TF1("extraPol",exclude,0,1024,3);
//TF1  *extraPol = new TF1("extraPol","pol2",0,1024);
extraPol->SetParameters(fit->GetParameters());
extraPol->Draw("same");

TH1F *hnew = (TH1F*)h1->Clone("hnew");
hnew->Add(extraPol, -1);
hnew->Draw("same");
hnew->SetLineColor(kMagenta);

TString histfilename150 = "testHisto.dat";//TString::Format("%s_Projected_150.dat",file.Data());
SingleExportAscii(hnew,histfilename150);

//TLegend *leg = c->BuildLegend();
//print(fline->GetParameters());

}

/**

  • \brief Export Single Histogram into ASCII file
    /
    Bool_t SingleExportAscii(TH1
    hist, TString &filename, TString folder="", TString separator="\t")
    {
    Int_t i,j;
    Double_t xcenter, xwidth;
    Bool_t success=kFALSE;
    //filename = folder + hist->GetName() + “.dat”;
    ofstream file_out(filename);
    //file_out << "# Output " << hist->ClassName() << “: " << hist->GetName() << " (” << hist->GetTitle() << “)\n”;
    if (hist->GetDimension()==1)
    {
    //file_out << “# BinCenter” << separator << “Content\n”;
    for (i=1; i<=hist->GetNbinsX(); i++)
    file_out << int(hist->GetBinCenter(i)) << separator << hist->GetBinContent(i) << endl;
    if (i>1)
    success=kTRUE;
    }
    else if (hist->GetDimension()==2)
    {
    file_out << “# xBinCenter” << separator << “yBinCenter” << separator << “Content” << separator << “xBinHalfWidth” << separator << “yBinHalfWidth” << separator << “Error” << endl;
    for (i=1; i <= hist->GetNbinsX(); i++)
    {
    xcenter = hist->GetXaxis()->GetBinCenter(i);
    xwidth = hist->GetXaxis()->GetBinWidth(i)/2;
    for (j=1; j <= hist->GetNbinsY(); j++)
    file_out << xcenter << separator << hist->GetYaxis()->GetBinCenter(j) << separator << hist->GetBinContent(i,j) << separator << xwidth << separator << hist->GetYaxis()->GetBinWidth(j)/2 << separator << hist->GetBinError(i,j) << endl;
    if (j>1)
    file_out << endl; // produce a blank line after each set of Ybins for a certain Xbin. Gnuplot likes this.
    }
    if (i>1)
    success=kTRUE;
    }
    file_out.close();
    if (success == kTRUE)
    cout << "*** TRemiHistExport: Histogram " << hist->GetName() << " written to " << filename << endl;
    return success;
    }[/code]

How can I get rid of these sinking, right and left of the exclusion range?
Thank you very much in advance!

First of all, the sinking is due to the fact that the TF1 object you are using to fit your data connects each point (regions with zeros due to point exclusion included) with a straight line by default. If you increase the number of displayed points like this:

//Right after the declaration of TF1 *fit
fit->SetNpx(10000);

you’ll probably see sharper edges (that is, the valley will become a sort of box).

In order to avoid it you could Clone the fitted TF1 two times and set different drawing ranges.
First of all, call:

h1->Fit("fit","W0",NULL,465,850);

instead of:

h1->Fit("fit","W",NULL,465,850);

This way, the (boxed/gapped) fit result won’t show on the graph itself. Then:

/*
After the fit has been done and the parameters from TF1 fit have been stored in extraPol, clone the function two times
*/
TF1 *f_left =  (TF1*)fit->Clone("f_left");
TF1 *f_right = (TF1*)fit->Clone("f_right");
/*
Set the proper range for functions to be drawn
*/
f_left->SetRange(400, 420); //The ranges here have been taken from your macro.
f_right->SetRange(460, 850); //The ranges here have been taken from your macro.
//Draw the new ranged functions
f_left->Draw("LSAME");
f_right->Draw("LSAME");
//Update the canvas, if needed
c->Modified();
c->Update();

This is someway brutal, but should solve your issue. If anyone knows a better way to do this, I think it would be better.
I hope I have helped anyway.

Andrea Demetrio

I think we need your data file in order to reproduce the problem.
BTW. your “fit” function has 3 parameters so you should initialize 3 when you call fit->SetParameters(…);

Andrea, I tried to play with the ${ROOTSYS}/tutorials/fit/fitExclude.C tutorial so that the whole fitted function is drawn afterwards (i.e. not the two partial clones) and I do NOT get any problem in this case (i.e. no “dip” in the function), so there seems to be something screwy around.

Thanks for pointing it out;)
I don’t know then what the problem could actually be. However, that tutorial should solve the issue - in a way or another.

Best regards,

Andrea Demetrio

Andrea, his source code is based on this tutorial.
The whole point is why this tutorial works (i.e. when I modify it to draw the fitted function instead of the two clones) and his code does not.

[quote=“Wile E. Coyote”]I think we need your data file in order to reproduce the problem.
BTW. your “fit” function has 3 parameters so you should initialize 3 when you call fit->SetParameters(…)[/quote]

Please find attached the data file
B_p_1500_120.dat (7.99 KB)

So, the “red line” with the “dip” comes from hnew->Draw(“same”);.

I think you should do two things:

  1. move the line:
    TH1F hnew = (TH1F)h1->Clone(“hnew”);
    to a place BEFORE you execute “h1->Fit(…);” (so that the fitted function does NOT get cloned/copied into “hnew”).
  2. fit your “h1” histogram adding the “0” (“zero”, not “capital O”) fit option:
    h1->Fit(fit, “0W”, “”, 400, 850);
    so that the fitted function will not be drawn by default.

And, of course, don’t forget to initialize all three parameters in advance:
fit->SetParameters(0, 0, 0);

B.T.W. See also the ‘Warning when using the option “0”’ in the TH1:Fit method description.

P.S. Just for the record. I believe that the underlaying problem is that a function created with the “TF1(const char* name, void* fcn, Double_t xmin, Double_t xmax, Int_t npar)” constructor cannot be cloned. So, when you try to clone a histogram which has such an associated function in it, then the cloning procedure produces a “misbehaving” function’s copy (in the new cloned histogram).

Thank you very much for your help.
I did what you suggested, but in this way I cannot draw the fitting function at all.
I tried to draw it by hand, but it didn’t work : No fitting function was drawn.
My code is the following

[code] #include “Riostream.h”

Bool_t reject;
Double_t exclude(Double_t *x, Double_t *par)
{
if (reject && x[0] > 420 && x[0] < 460) {
TF1::RejectPoint();
return 0;
}
//return par[0] + par[1]*x[0];
return par[0] + par[1]*x[0] + par[2]*x[0]*x[0];
}

void testFitExclude(){//(char * file_c) {

gROOT->SetStyle("Plain");
gStyle->SetOptStat(0000);
gStyle->SetOptFit(1111);
gStyle->SetOptTitle(0);

TCanvas *c = new TCanvas("c", "c");
c->SetFillColor(5);
c->SetFrameFillColor(10);

TString dir = gSystem->UnixPathName(gInterpreter->GetCurrentMacroName());
dir.ReplaceAll("testFitExclude.C","");
dir.ReplaceAll("/./","/");
ifstream in;
in.open(Form("%sB_p_1500_120.dat",dir.Data()));//

Float_t x,y;//,z,w,k,l,m;
Int_t nlines = 0;
TH1F *h1 = new TH1F("h1","Experimental",1024,1,1024);

while (1) {
in >> x >> y;
if (!in.good()) break;
if (nlines < 5) printf("x=%8f, y=%8f\n");
h1->Fill(x,y);
nlines++;
}
printf("Found %d points\n",nlines);

in.close();
h1->SetLineColor(kBlack);
h1->SetLineWidth(1);
h1->Draw();
h1->GetXaxis()->SetTitle("Channels");
h1->GetYaxis()->SetTitle("Events");

reject = kTRUE;
TF1 *fit = new TF1("fit",exclude,0,1024,3);//<--------------------------
fit->SetParameters(0,0,0);
TH1F *hnew = (TH1F*)h1->Clone("hnew");
h1->Fit("fit","0W",NULL,400,850);
reject = kFALSE;

//TF1 *fit = new TF1("fit","pol2",0,1024);

// h1->Fit(“fit”,“W”,NULL,465,850);//465 850
fit->Draw(“same”);
fit->SetLineColor(kRed);
// h1->GetFunction(“fit”)->SetLineColor(kRed);

TF1  *extraPol = new TF1("extraPol",exclude,0,1024,3);
//TF1  *extraPol = new TF1("extraPol","pol2",0,1024);
extraPol->SetParameters(fit->GetParameters());
extraPol->Draw("same");

//TH1F *hnew = (TH1F*)h1->Clone("hnew");
hnew->Add(extraPol, -1);
hnew->Draw("same");
hnew->SetLineColor(kMagenta);

TString histfilename150 = "B_p_1500_120_Subtracted.dat";//TString::Format("%s_Projected_150.dat",file.Data());
SingleExportAscii(hnew,histfilename150);

//TLegend *leg = c->BuildLegend();
//print(fline->GetParameters());

}

/**

  • \brief Export Single Histogram into ASCII file
    /
    Bool_t SingleExportAscii(TH1
    hist, TString &filename, TString folder="", TString separator="\t")
    {
    Int_t i,j;
    Double_t xcenter, xwidth;
    Bool_t success=kFALSE;
    //filename = folder + hist->GetName() + “.dat”;
    ofstream file_out(filename);
    //file_out << "# Output " << hist->ClassName() << “: " << hist->GetName() << " (” << hist->GetTitle() << “)\n”;
    if (hist->GetDimension()==1)
    {
    //file_out << “# BinCenter” << separator << “Content\n”;
    for (i=1; i<=hist->GetNbinsX(); i++)
    file_out << int(hist->GetBinCenter(i)) << separator << hist->GetBinContent(i) << endl;
    if (i>1)
    success=kTRUE;
    }
    else if (hist->GetDimension()==2)
    {
    file_out << “# xBinCenter” << separator << “yBinCenter” << separator << “Content” << separator << “xBinHalfWidth” << separator << “yBinHalfWidth” << separator << “Error” << endl;
    for (i=1; i <= hist->GetNbinsX(); i++)
    {
    xcenter = hist->GetXaxis()->GetBinCenter(i);
    xwidth = hist->GetXaxis()->GetBinWidth(i)/2;
    for (j=1; j <= hist->GetNbinsY(); j++)
    file_out << xcenter << separator << hist->GetYaxis()->GetBinCenter(j) << separator << hist->GetBinContent(i,j) << separator << xwidth << separator << hist->GetYaxis()->GetBinWidth(j)/2 << separator << hist->GetBinError(i,j) << endl;
    if (j>1)
    file_out << endl; // produce a blank line after each set of Ybins for a certain Xbin. Gnuplot likes this.
    }
    if (i>1)
    success=kTRUE;
    }
    file_out.close();
    if (success == kTRUE)
    cout << "*** TRemiHistExport: Histogram " << hist->GetName() << " written to " << filename << endl;
    return success;
    }[/code]

I believe that the fitting parameters aren’t parsed in the fit function.
Any idea on how to fix this?
Thank’s in advance!

Your fit->Draw(“same”); gets overdrawn by extraPol->Draw(“same”); (both functions, “fit” and “extraPol”, are exactly the same). Try to extraPol->SetLineStyle(2); (well, you can also comment out the line extraPol->Draw(“same”); completely and then you will see your “fit”).

The thing is that I need both functions drawn in solid color.
Is there a way to do so?

Also if I use

extraPol->SetLineStyle(2)

i can actually see two functions, but the fitting one is not drawn on the specified range, but on histogram’s full range, as shown in the following image

Try with:
fit->SetLineWidth(4);
extraPol->SetLineWidth(2);

If you want to draw your fitted function just in the “range” you used for the fit then try to remove the “0” fit option from the fitting line and then:
h1->GetFunction(“fit”)->SetLineColor(kRed);
h1->GetFunction(“fit”)->SetLineWidth(4);
and:
extraPol->SetLineWidth(2);
(and comment out the line fit->Draw(“same”); completely).

Well, actually you do NOT need to remove the “0” fit option from the fitting line, just add:
h1->GetFunction(“fit”)->ResetBit(TF1::kNotDraw);
and change the colors and widths of drawn functions.

That seemed to work half way.
What I want is to plot the extrapolation function only outside the fitting range.

The simplest way is to overdraw it:
extraPol->Draw(“same”);
h1->GetFunction(“fit”)->Draw(“same”);

BTW. I have forgotten that you can also define your “fit” function in your desired “range” only:
TF1 *fit = new TF1(“fit”, exclude, 480, 850, 3);
and then when you fit->Draw(“same”); it will appear just in the predefined “range”.

Thank you very much for your help!