Creating your own pdf#
A core feature of zfit is the ability to create custom pdfs and functions in an simple and straightforward way.
There are two main possibilities to create a custom pdf, an easier for most use-cases and an advanced way..
The simple way#
While the same works for functions, an example with a PDF is shown here.
import numpy as np
import zfit
from zfit import z
The first way is the most simple and should only be used for the trivial cases, i.e. if you’re not familiar with Python classes (especially not with the __init__
method).
class MyGauss(zfit.pdf.ZPDF):
_N_OBS = 1 # dimension, can be omitted
_PARAMS = ['mean', 'std'] # the name of the parameters
@zfit.supports()
def _unnormalized_pdf(self, x, params):
x0 = x[0] # using the 0th axis
mean = params['mean']
std = params['std']
return z.exp(- ((x0 - mean) / std) ** 2)
Done. Now we can use our pdf already!
The slightly more general way involves overwritting the __init__
and gives you all the possible flexibility: to use custom parameters, to preprocess them etc.
Here we inherit from BasePDF
class MyGauss(zfit.pdf.BasePDF):
def __init__(self, mean, std, obs, extended=None, norm=None, name=None, label=None):
params = {'mean': mean, # 'mean' is the name as it will be named in the PDF, mean is just the parameter to create the PDF
'std': std
}
super().__init__(obs=obs, params=params, extended=extended, norm=norm,
name=name, label=label)
@zfit.supports()
def _unnormalized_pdf(self, x, params):
x0 = x[0] # using the 0th axis
mean = params['mean']
std = params['std']
return z.exp(- ((x0 - mean) / std) ** 2)
obs = zfit.Space('obs1', -3, 6)
data_np = np.random.random(size=1000)
data = zfit.Data(data_np, obs=obs)
Create two parameters and an instance of your own pdf
mean = zfit.Parameter("mean", 1.)
std = zfit.Parameter("std", 1.)
my_gauss = MyGauss(obs=obs, mean=mean, std=std)
probs = my_gauss.pdf(data)
print(probs[:20])
tf.Tensor(
[0.3541033 0.45441998 0.31550083 0.51059312 0.53073164 0.36003354
0.44722167 0.43450162 0.53486056 0.39177145 0.21730318 0.5551485
0.48442049 0.50232375 0.37894167 0.26333474 0.50005773 0.4680993
0.41902941 0.47698597], shape=(20,), dtype=float64)
If we want to make sure it’s a numpy array, we can use zfit.run
We could improve our PDF by registering an integral
def gauss_integral_from_any_to_any(limits, params, model):
lower, upper = limits.v1.limits
mean = params['mean']
std = params['std']
# write your integral here
return 42. # dummy integral, must be a scalar!
limits = zfit.Space(axes=0, lower=zfit.Space.ANY_LOWER, upper=zfit.Space.ANY_UPPER)
MyGauss.register_analytic_integral(func=gauss_integral_from_any_to_any, limits=limits)
More advanced custom PDFs are introduced in the guide on custom PDFs.