How can I do a fit with a custom function in PyROOT?

I’m trying to do a fit using a custom function. I’ve made an attempt as shown below and I’m not sure if I’m doing it in quite the right way

#/bin/usr/env python

import sys
import math
import ROOT
import numpy as np

def main():
  
    canvas  = ROOT.TCanvas(
        "canvas",
        "fitting",
        200,
        10,
        500,
        500
    )
    
    # create function for fitting
    fit = ROOT.TF1(
        "fit",
        function_Frechet(),
        0,
        30,
        4
    )
    fit.SetParameters(-2, 0.1, 1, 90)
    fit.SetParNames("a", "m", "s", "b")
    fit.SetParLimits(0,   -1,   -4)
    fit.SetParLimits(1, 0.01,  0.2)
    fit.SetParLimits(2,    0,    2)
    fit.SetParLimits(3,    0, 1000)

    # create histogram with example data
    histogram = ROOT.TH1F(
        "histogram",
        "px",
        1000,
        -1.,
        1.
    )
    function = ROOT.TF1(
        "function",
        "100.-3.*((x-0.11)**(-4.))*TMath::Exp(-(x-0.11)**3.)",
        -1,
        1.
    )
    histogram.FillRandom("function", 10000)

    # fit
    histogram.Fit(fit)

    canvas.Print("fit.png")
  
    # print results
    parameters = fit.GetParameters()
    print("fitting result: {parameter1}, {parameter2}, {parameter3}, {parameter4}".format(
        parameter1 = parameters[0],
        parameter2 = parameters[1],
        parameter3 = parameters[2],
        parameter4 = parameters[3]
    ))

class function_Frechet:
    def __call__(self, x, parameters):
        a = parameters[0] # shape
        m = parameters[1] # location of minimum
        s = parameters[2] # scale
        b = parameters[3] # baseline
        x = x[0]
        y = b+(a/s)*(((x-m)/s)**(-1-a))*np.exp(-((x-m)/s)**(-a))
        return y

if __name__ == "__main__":
    main()

Is it correct to define the custom function via a class? It seems odd to me. I would appreciate guidance on defining the custom function, using parameters with it and any other suggestions for improvement to the code. Thanks

class, function, all same same: all first python citizen. Instance from class nice to store parameters, but can do on function also, if not use multiple TFn objets for same.

Taking local var in call makes slow. Argue if same during fit, set in init, not bother setting through C++ just to get back in Python where already have. But anyway all Python callback slow: many layer.

Oh, not make too many fit objets to TFn. PyROOT keep all alive (is other word: leak).

-Dom

Hi,

you may also pass any python function (which you need to wrap in a class :slight_smile:) you want to minimize to ROOT.Fit.Fitter, see How to pass fcn func defined on python side to ROOT.Fitter? . In this was you may configure the fitter (minimizer) of your choice as you need.

testpyfit.py contains simple example how to use it:
How to pass fcn func defined on python side to ROOT.Fitter?

In your case you will need to define also some “Chi**2” (FCN) function which you need to construct separetely (this is the function being minimized) using your function_Frechet .

The adventage of this approach is that you may define also your "Chi2" as you need (necessity for some more advanced fitting, solving some more complicated problems where you need to have the settings under control). This may be seen also as disadventage in the case of simple fitting when some default choices may be fine (you don’t need to specify your chi2 in the case of “TF1 approach”).

Cheers,
Jiri