ROOT::Fitter fails on linear fitting

Hi,

I am trying to use ROOT::Fit::Fitter to perform a simple linear regression.

Here is how I set up the Fitter:

fitter = ROOT.Fit.Fitter()
func = ROOT.TF1("f1", "pol1", 0, 130)
fitter.SetFunction(ROOT.Math.WrappedMultiTF1(func, func.GetNdim()), False)
fitter.Config().SetMinimizer("Minuit2");

After this, I add the data in the BinData type:

x = np.array([117.5, 97.5, 87.5, 77.5])
x_err = np.array([2.5, 2.5, 2.5, 2.5])
y = np.array([-12.5, -75, -102.5, -127.5])
y_err = np.array([2.5, 2.5, 2.5, 2.5])
bin_data = ROOT.Fit.BinData(len(x), x, y, x_err, y_err)

Then, use Fit function to perform the fit:

fitter.Config().ParSettings(0).SetValue(0.)
fitter.Config().ParSettings(1).SetValue(1.)
is_ok = fitter.Fit(bin_data)
print(f"is ok: {is_ok}")
res = fitter.Result()
print(f"slope: {res.Parameter(1)}")
print(f"offset: {res.Parameter(0)}")
print(f"pvalue: {res.Prob()}")

Here is the result:

is ok: True
slope: -215605.28611662454
offset: 20481809.40923477
pvalue: 3.967978809222436e-31

In the meanwhile, I also use a python library to do the linear fitting:

res = stats.linregress(x, y)
print(f"slope: {res.slope}")
print(f"offset: {res.intercept}")
print(f"pvalue: {res.pvalue}")

With python linear regression, I got a satisfying result:

slope: 2.892857142857143
offset: -354.19642857142856
pvalue: 0.0013385665091588672

Why did ROOT Fitter algorithm failed to fit the parameters? Are there additional settings I should add?

Thanks for your attention


Please read tips for efficient and successful posting and posting code

ROOT Version: 6.30.08
Platform: Fedora 40
Compiler: gcc


Dear @EdwinYZ,

you’re very close to the correct usage of the of the minimizer. The only changes you should make are the followings:

  1. choose the linear minimizer for a linear fit by calling fitter.Config().SetMinimizer("Linear")
  2. allow the use of gradient by setting the fitter function as follows fitter.SetFunction(ROOT.Math.WrappedMultiTF1(func, func.GetNdim()), True)

Cheers,
Monica

Dear @mdessole

Thanks for your help. Yes, it works.

I understand now why the second parameter should be False as I overlooked this documentation which states:

Note that the ROOT::Math::WrappedMultiTF1 wrapper class implements also the gradient interface, using internally TF1::GradientPar, which is based on numerical differentiation, apart for the case of linear functions (this is when TF1::IsLinear() is true). The parameter derivatives of the model function can be useful to some minimization algorithms, such as FUMILI (see → FUMILI). However, in general is better to leave the minimization algorithm (for example TMinuit, see → TMinuit) to compute the needed derivatives using its own customised numerical differentiation algorithm. To avoid providing the parameter derivations to the fitter, explicitly set Fitter::SetFunction to false.

So as far as I understand, the second input parameter of SetFunction is only true if the fitting function is linear and false if not linear?

For

fitter.Config().SetMinimizer("Linear")

is there any documentation where I could find the usage of “Linear” option?

From ROOT::Math::Minimizer Class Referenceabstract, I don’t see the “Linear” option available there.

You’re right, the documentation for the “Linear” option is not complete. Similarly, you could use the LinearFit method of the Fitter class which is better documented.

For what concerns the SetFunction method, my understanding is that when the option is set to true, the minimizer expects that derivatives of the model are provided by the user. In this case, I would expect this to work with the option set to false, as you’re not providing the gradients. This needs a deeper investigation.

Docu has been updated: [skip-ci] improve and sync Fitter docu by ferdymercury · Pull Request #17752 · root-project/root · GitHub