For the purpose of defining a lot of Maximum Likelihood estimators, I think I need a metaclass.
At the moment I have to copy/paste a lot of code for every new class definition and just substitute the corresponding scipy.stats functions.
from scipy.stats import fisk, t
from statsmodels.base.model import GenericLikelihoodModel
from inspect import signature
class Fisk(GenericLikelihoodModel):
"""A maximum likelihood estimator for the fisk distribution.
"""
nparams = 3
def loglike(self, params):
return fisk.logpdf(self.endog, *params).sum()
def fit(self, **kwargs):
if 'start_params' not in kwargs:
# This is for performance and! convergence stability.
# The scipy function provides better starting params.
kwargs['start_params'] = fisk.fit(self.endog)
res = super().fit(**kwargs)
res.df_model = self.nparams
res.df_resid = len(self.endog) - self.nparams
return res
class T(GenericLikelihoodModel):
"""A maximum likelihood estimator for the Student-T distribution.
"""
nparams = 3
def loglike(self, params):
return t.logpdf(self.endog, *params).sum()
def fit(self, **kwargs):
if 'start_params' not in kwargs:
# This is for performance and! convergence stability.
# The scipy function provides better starting params.
kwargs['start_params'] = t.fit(self.endog)
res = super().fit(**kwargs)
res.df_model = self.nparams
res.df_resid = len(self.endog) - self.nparams
return res
With a metaclass I automated this code definition
class ML_estimator(type):
def __new__(cls, clsname, bases, dct, f):
return super().__new__(cls, clsname, (GenericLikelihoodModel, ), dct)
def __init__(cls, clsname, bases, dct, f):
cls.nparams = len(signature(f.pdf).parameters)
def loglike(self, params):
return f.logpdf(self.endog, *params).sum()
cls.loglike = loglike
def fit(self, **kwargs):
if 'start_params' not in kwargs:
# This is for performance and! convergence stability.
# The scipy function provides better starting params.
kwargs['start_params'] = f.fit(self.endog)
res = super(cls, self).fit(**kwargs)
res.df_model = self.nparams
res.df_resid = len(self.endog) - self.nparams
return res
cls.fit = fit
class Fisk(metaclass=ML_estimator, f=fisk):
pass
class T(metaclass=ML_estimator, f=t):
pass
Does this seem like a reasonable application of metaclasses? What improvements do you have?
The ML-estimator can be tested with:
sample = fisk.rvs(c=1, size=1000)
res = Fisk(sample).fit()
res.summary()
GenericLikelihoodModelof your own doing or is it from a third-party library? \$\endgroup\$