Source code for statsmodels.emplike.elanova

"""
This script contains empirical likelihood ANOVA

Currently the script only contains one feature that allows the user to compare
means of multiple groups.

References
----------

Owen, A. B. (2001). Empirical Likelihood. Chapman and Hall.
"""

from typing import NamedTuple
import warnings

import numpy as np
from scipy import optimize
from scipy.stats import chi2

from statsmodels.tools.validation import bool_like

from .descriptive import _OptFuncts


[docs] class ANOVAResult(NamedTuple): """ Result of :meth:`ANOVA.compute_ANOVA`. Parameters ---------- llr : float -2 times the log-likelihood ratio, the test statistic. pvalue : float The p-value of the test statistic. mu : float The common mean, either the value supplied by the caller or the maximum empirical likelihood estimate. weights : ndarray or None The observation weights that maximize the likelihood. """ llr: float pvalue: float mu: float weights: np.ndarray | None
class _ANOVAOpt(_OptFuncts): """ Class containing functions that are optimized over when conducting ANOVA """ def _opt_common_mu(self, mu): """ Optimizes the likelihood under the null hypothesis that all groups have mean mu Parameters ---------- mu : float The common mean. Returns ------- llr : float -2 times the llr ratio, which is the test statistic. """ nobs = self.nobs endog = self.endog num_groups = self.num_groups endog_asarray = np.zeros((nobs, num_groups)) obs_num = 0 for arr_num in range(len(endog)): new_obs_num = obs_num + len(endog[arr_num]) endog_asarray[obs_num:new_obs_num, arr_num] = endog[arr_num] - mu obs_num = new_obs_num est_vect = endog_asarray wts = np.ones(est_vect.shape[0]) * (1.0 / (est_vect.shape[0])) eta_star = self._modif_newton(np.zeros(num_groups), est_vect, wts) denom = 1.0 + np.dot(eta_star, est_vect.T) self.new_weights = 1.0 / nobs * 1.0 / denom llr = np.sum(np.log(nobs * self.new_weights)) return -2 * llr
[docs] class ANOVA(_ANOVAOpt): """ A class for ANOVA and comparing means Parameters ---------- endog : list of arrays endog should be a list containing 1 dimensional arrays. Each array is the data collected from a certain group. """ def __init__(self, endog): self.endog = endog self.num_groups = len(self.endog) self.nobs = 0 for i in self.endog: self.nobs = self.nobs + len(i)
[docs] def compute_ANOVA( self, mu=None, mu_start=0, return_weights=False, *, use_namedtuple=None ): """ Returns -2 log likelihood, the pvalue and the maximum likelihood estimate for a common mean Parameters ---------- mu : float If a mu is specified, ANOVA is conducted with mu as the common mean. Otherwise, the common mean is the maximum empirical likelihood estimate of the common mean. Default is None. mu_start : float Starting value for common mean if specific mu is not specified. Default = 0. return_weights : bool if TRUE, returns the weights on observations that maximize the likelihood. Default is FALSE. use_namedtuple : bool, optional Flag indicating whether to return the results as an ``ANOVAResult`` NamedTuple instead of a plain tuple. When ``return_weights=True`` the NamedTuple holds the same four elements as the legacy tuple, so it unpacks identically and is always returned, with no warning. When ``return_weights=False`` the legacy three-element tuple is returned by default and a ``FutureWarning`` is issued. .. deprecated:: 0.15.0 In release 0.16.0 or after July 2027, whichever is later, the default will change to always return an ``ANOVAResult``. Set ``use_namedtuple=True`` to opt in now, or ``use_namedtuple=False`` to silence the warning and keep the current return type. Returns ------- ANOVAResult or tuple If ``use_namedtuple=True`` or ``return_weights=True``, a NamedTuple with fields ``llr``, ``pvalue``, ``mu`` and ``weights``. See :class:`~statsmodels.emplike.elanova.ANOVAResult`. Otherwise (the deprecated default), the plain ``(llr, pvalue, mu)`` tuple. """ use_namedtuple = bool_like(use_namedtuple, "use_namedtuple", optional=True) if mu is not None: llr = self._opt_common_mu(mu) mu_common = mu else: res = optimize.fmin_powell( self._opt_common_mu, mu_start, full_output=1, disp=False ) llr = res[1] mu_common = float(np.squeeze(res[0])) pval = 1 - chi2.cdf(llr, self.num_groups - 1) if use_namedtuple is None and not return_weights: warnings.warn( "ANOVA.compute_ANOVA currently returns a plain tuple whose " "length depends on the return_weights argument. In release " "0.16.0 or after July 2027, whichever is later, the default " "behavior will switch to always returning an ANOVAResult " "NamedTuple. Set use_namedtuple=True to switch now, or " "use_namedtuple=False to keep the current behavior and " "silence this warning.", FutureWarning, stacklevel=2, ) if use_namedtuple or return_weights: return ANOVAResult(llr, pval, mu_common, self.new_weights) return llr, pval, mu_common