import numpy as np from statsmodels.tools.tools import Bunch class _MinimalWLS: """ Minimal implementation of WLS optimized for performance. Parameters ---------- endog : array_like 1-d endogenous response variable. The dependent variable. exog : array_like A nobs x k array where `nobs` is the number of observations and `k` is the number of regressors. An intercept is not included by default and should be added by the user. See :func:`statsmodels.tools.add_constant`. weights : array_like, optional 1d array of weights. If you supply 1/W then the variables are pre- multiplied by 1/sqrt(W). If no weights are supplied the default value is 1 and WLS reults are the same as OLS. check_endog : bool, optional Flag indicating whether to check for inf/nan in endog. If True and any are found, ValueError is raised. check_weights : bool, optional Flag indicating whether to check for inf/nan in weights. If True and any are found, ValueError is raised. Notes ----- Provides only resid, scale, fittedvalues, model.weights which are used by methods that iteratively apply WLS. Does not perform any checks on the input data for type or shape compatibility """ msg = 'NaN, inf or invalid value detected in {0}, estimation infeasible.' def __init__(self, endog, exog, weights=1.0, check_endog=False, check_weights=False): self.endog = endog self.exog = exog self.weights = weights w_half = np.sqrt(weights) if check_weights: if not np.all(np.isfinite(w_half)): raise ValueError(self.msg.format('weights')) if check_endog: if not np.all(np.isfinite(endog)): raise ValueError(self.msg.format('endog')) self.wendog = w_half * endog if np.isscalar(weights): self.wexog = w_half * exog else: self.wexog = np.asarray(w_half)[:, None] * exog def fit(self, method='pinv'): """ Minimal implementation of WLS optimized for performance. Parameters ---------- method : str, optional Method to use to estimate parameters. "pinv", "qr" or "lstsq" * "pinv" uses the Moore-Penrose pseudoinverse to solve the least squares problem. * "qr" uses the QR factorization. * "lstsq" uses the least squares implementation in numpy.linalg Returns ------- results : namedtuple Named tuple containing the fewest terms needed to implement iterative estimation in models. Currently * params : Estimated parameters * fittedvalues : Fit values using original data * resid : Residuals using original data * model : namedtuple with one field, weights * scale : scale computed using weighted residuals Notes ----- Does not perform and checks on the input data See Also -------- statsmodels.regression.linear_model.WLS """ if method == 'pinv': pinv_wexog = np.linalg.pinv(self.wexog) params = pinv_wexog.dot(self.wendog) elif method == 'qr': Q, R = np.linalg.qr(self.wexog) params = np.linalg.solve(R, np.dot(Q.T, self.wendog)) else: params, _, _, _ = np.linalg.lstsq(self.wexog, self.wendog, rcond=-1) return self.results(params) def results(self, params): """ Construct results params : ndarray Model parameters Notes ----- Allows results to be constructed from either existing parameters or when estimated using using ``fit`` """ fitted_values = self.exog.dot(params) resid = self.endog - fitted_values wresid = self.wendog - self.wexog.dot(params) df_resid = self.wexog.shape[0] - self.wexog.shape[1] scale = np.dot(wresid, wresid) / df_resid return Bunch(params=params, fittedvalues=fitted_values, resid=resid, model=self, scale=scale)