125 lines
4.2 KiB
Python
125 lines
4.2 KiB
Python
|
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)
|