Source code for statsmodels.miscmodels.tmodel

"""
Linear Model with Student-t distributed errors

Because the t distribution has fatter tails than the normal distribution, it
can be used to model observations with heavier tails and observations that have
some outliers. For the latter case, the t-distribution provides more robust
estimators for mean or mean parameters (what about var?).

Created on 2010-09-24
Author: josef-pktd
License: BSD

References
----------
Kenneth L. Lange, Roderick J. A. Little, Jeremy M. G. Taylor (1989)
Robust Statistical Modeling Using the t Distribution
Journal of the American Statistical Association
Vol. 84, No. 408 (Dec., 1989), pp. 881-896
Published by: American Statistical Association
Stable URL: http://www.jstor.org/stable/2290063

Notes
-----
Reference above not read yet.

TODO:

* add starting values based on OLS
* bugs: store_params does not seem to be defined, I think this was a module
  global for debugging - commented out
* parameter restriction: check whether version with some fixed parameters works
"""
# mostly copied from the examples directory written for trying out generic mle.

import numpy as np
from scipy import special, stats

from statsmodels.base.model import GenericLikelihoodModel

# redefine some shortcuts
np_log = np.log
np_pi = np.pi
sps_gamln = special.gammaln


[docs] class TLinearModel(GenericLikelihoodModel): """ Maximum Likelihood Estimation of Linear Model with t-distributed errors This is an example for generic MLE. Except for defining the negative log-likelihood method, all methods and results are generic. Gradients and Hessian and all resulting statistics are based on numerical differentiation. """
[docs] def initialize(self): """Initialize the model, setting up parameter names and start values""" print("running Tmodel initialize") # TODO: here or in __init__ self.k_vars = self.exog.shape[1] if not hasattr(self, "fix_df"): self.fix_df = False if self.fix_df is False: # df will be estimated, no parameter restrictions self.fixed_params = None self.fixed_paramsmask = None self.k_params = self.exog.shape[1] + 2 extra_params_names = ["df", "scale"] else: # df fixed self.k_params = self.exog.shape[1] + 1 fixdf = np.nan * np.zeros(self.exog.shape[1] + 2) fixdf[-2] = self.fix_df self.fixed_params = fixdf self.fixed_paramsmask = np.isnan(fixdf) extra_params_names = ["scale"] super().initialize() # Note: this needs to be after super initialize # super initialize sets default df_resid, # _set_extra_params_names adjusts it self._set_extra_params_names(extra_params_names) self._set_start_params()
def _set_start_params(self, start_params=None, use_kurtosis=False): """ Set starting values for the parameters Parameters ---------- start_params : array_like, optional Starting values to use directly. If None, starting values are constructed from an OLS fit of `endog` on `exog`. use_kurtosis : bool If True and `start_params` is None and `df` is not fixed, use the kurtosis of the OLS residuals to construct a starting value for the degrees of freedom parameter. Otherwise a starting value of 5 is used. """ if start_params is not None: self.start_params = start_params else: from statsmodels.regression.linear_model import OLS res_ols = OLS(self.endog, self.exog).fit() start_params = 0.1*np.ones(self.k_params) start_params[:self.k_vars] = res_ols.params if self.fix_df is False: if use_kurtosis: kurt = stats.kurtosis(res_ols.resid) df = 6./kurt + 4 else: df = 5 start_params[-2] = df # TODO adjust scale for df start_params[-1] = np.sqrt(res_ols.scale) self.start_params = start_params
[docs] def loglike(self, params): """ Loglikelihood of the model evaluated at params Parameters ---------- params : ndarray The parameters of the model. The last 2 parameters are degrees of freedom and scale. Returns ------- float The log likelihood of the model evaluated at `params`, summed over all observations. """ return -self.nloglikeobs(params).sum(0)
[docs] def nloglikeobs(self, params): """ Negative loglikelihood of linear model with t distributed errors Parameters ---------- params : ndarray The parameters of the model. The last 2 parameters are degrees of freedom and scale. Returns ------- ndarray The negative log likelihood of the model evaluated at `params` for each observation defined by self.endog and self.exog. Notes ----- .. math:: \\ln f(x)=\\ln\\Gamma\\left(\\frac{df+1}{2}\\right)-\\ln\\Gamma\\left(\\frac{df}{2}\\right)-\\frac{1}{2}\\ln(df\\pi)-\\frac{df+1}{2}\\ln\\left(1+\\frac{x^{2}}{df}\\right)-\\ln(scale) where :math:`x=(y-X\\beta)/scale`. The t distribution is the standard t distribution and not a standardized t distribution, which means that the scale parameter is not equal to the standard deviation. self.fixed_params and self.expandparams can be used to fix some parameters. (I doubt this has been tested in this model.) """ # print len(params), # store_params.append(params) if self.fixed_params is not None: # print 'using fixed' params = self.expandparams(params) beta = params[:-2] df = params[-2] scale = np.abs(params[-1]) # TODO check behavior around zero loc = np.dot(self.exog, beta) endog = self.endog x = (endog - loc)/scale # next part is stats.t._logpdf lPx = sps_gamln((df+1)/2) - sps_gamln(df/2.) lPx -= 0.5*np_log(df*np_pi) + (df+1)/2.*np_log(1+(x**2)/df) lPx -= np_log(scale) # correction for scale return -lPx
[docs] def predict(self, params, exog=None): """ Return predicted mean values Parameters ---------- params : ndarray The parameters of the model. Only the first `exog.shape[1]` elements (the regression coefficients) are used. exog : array_like, optional Explanatory variables to use for prediction. If None, `self.exog` is used. Returns ------- ndarray The predicted mean, ``exog @ beta``. """ if exog is None: exog = self.exog return np.dot(exog, params[:self.exog.shape[1]])