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
No module named 'nlopt'
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
def _unnormalized_pdf(self, x):
x = z.unstack_x(x) # returns a list with the columns: do x, y, z = z.unstack_x(x) for 3D
mean = self.params['mean']
std = self.params['std']
return z.exp(- ((x - 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):
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)
def _unnormalized_pdf(self, x):
x = z.unstack_x(x)
mean = self.params['mean']
std = self.params['std']
return z.exp(- ((x - mean) / std) ** 2)
obs = zfit.Space('obs1', limits=(-3, 6))
data_np = np.random.random(size=1000)
data = zfit.data.Data.from_numpy(array=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.51757937 0.41831597 0.54418191 0.542958 0.23268375 0.55285151
0.27772533 0.56409576 0.51026856 0.55642136 0.20802155 0.56285633
0.33878087 0.45248238 0.46990824 0.54277072 0.28640948 0.49710957
0.43360974 0.29180561], 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.limit1d
mean = params['mean']
std = params['std']
# write your integral here
return 42. # dummy integral, must be a scalar!
limits = zfit.Space(axes=0, limits=(zfit.Space.ANY_LOWER, 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.