def _cov(self, gls): x = self._x eps = self._eps k = len(x) sigma = self.sigma weights = inv(sigma) if gls else eye(k) xpx = blocked_inner_prod(x, weights) weights = inv_matrix_sqrt(sigma) if gls else eye(k) bigx = blocked_diag_product(x, weights) nobs = eps.shape[0] e = eps.T.ravel()[:, None] bigxe = bigx * e m = bigx.shape[1] xeex = zeros((m, m)) for i in range(nobs): xe = bigxe[i::nobs].sum(0)[None, :] xeex += xe.T @ xe if self._constraints is None: xpxi = inv(xpx) cov = xpxi @ xeex @ xpxi else: cons = self._constraints xpx = cons.t.T @ xpx @ cons.t xpxi = inv(xpx) xeex = cons.t.T @ xeex @ cons.t cov = cons.t @ (xpxi @ xeex @ xpxi) @ cons.t.T cov = (cov + cov.T) / 2 return cov
def test_ols_against_gls(data): mod = SUR(data) res = mod.fit(method='gls') sigma = res.sigma sigma_m12 = inv_matrix_sqrt(sigma) key = list(data.keys())[0] if isinstance(data[key], Mapping): y = [data[key]['dependent'] for key in data] x = [data[key]['exog'] for key in data] try: w = [data[key]['weights'] for key in data] except KeyError: w = [np.ones_like(data[key]['dependent']) for key in data] else: y = [data[key][0] for key in data] x = [data[key][1] for key in data] try: w = [data[key][2] for key in data] except IndexError: w = [np.ones_like(data[key][0]) for key in data] wy = [_y * np.sqrt(_w / _w.mean()) for _y, _w in zip(y, w)] wx = [_x * np.sqrt(_w / _w.mean()) for _x, _w in zip(x, w)] wy = blocked_column_product(wy, sigma_m12) wx = blocked_diag_product(wx, sigma_m12) ols_res = OLS(wy, wx).fit(debiased=False) assert_allclose(res.params, ols_res.params)
def test_ols_against_gls(data): mod = SUR(data) res = mod.fit(method="gls") if isinstance(data[list(data.keys())[0]], dict): predictions = mod.predict(res.params, equations=data) predictions2 = mod.predict(np.asarray(res.params)[:, None], equations=data) assert_allclose(predictions, predictions2) sigma = res.sigma sigma_m12 = inv_matrix_sqrt(np.asarray(sigma)) key = list(data.keys())[0] if isinstance(data[key], Mapping): y = [data[key]["dependent"] for key in data] x = [data[key]["exog"] for key in data] try: w = [data[key]["weights"] for key in data] except KeyError: w = [np.ones_like(data[key]["dependent"]) for key in data] else: y = [data[key][0] for key in data] x = [data[key][1] for key in data] try: w = [data[key][2] for key in data] except IndexError: w = [np.ones_like(data[key][0]) for key in data] wy = [_y * np.sqrt(_w / _w.mean()) for _y, _w in zip(y, w)] wx = [_x * np.sqrt(_w / _w.mean()) for _x, _w in zip(x, w)] wy = blocked_column_product(wy, sigma_m12) wx = blocked_diag_product(wx, sigma_m12) ols_res = OLS(wy, wx).fit(debiased=False) assert_allclose(res.params, ols_res.params)
def _multivariate_ls_fit(self): wy, wx = self._wy, self._wx k = len(wx) if self.constraints is not None: cons = self.constraints x = blocked_diag_product(wx, eye(len(wx))) y = np.vstack(wy) xt = x @ cons.t # TODO: Make more memory efficient # Replace with t.T @ xpx @ t xpx = xt.T @ xt # Replace with t.T @ xpy - t.T @ xpx @ a.T xpy = xt.T @ (y - x @ cons.a.T) paramsc = np.linalg.solve(xpx, xpy) params = cons.t @ paramsc + cons.a.T else: xpx = blocked_inner_prod(wx, eye(len(wx))) xpy = [] for i in range(k): xpy.append(wx[i].T @ wy[i]) xpy = np.vstack(xpy) xpx, xpy = xpx, xpy params = solve(xpx, xpy) beta = params loc = 0 eps = [] for i in range(k): nb = wx[i].shape[1] b = beta[loc:loc + nb] eps.append(wy[i] - wx[i] @ b) loc += nb eps = hstack(eps) return beta, eps
def __init__(self, x, eps, sigma, full_sigma, gls=False, debiased=False, constraints=None): super(HeteroskedasticCovariance, self).__init__(x, eps, sigma, full_sigma, gls=gls, debiased=debiased, constraints=constraints) self._name = 'Heteroskedastic (Robust) Covariance' k = len(x) weights = inv(sigma) if gls else eye(k) bigx = blocked_diag_product(x, weights) nobs = eps.shape[0] e = eps.T.ravel()[:, None] bigxe = bigx * e m = bigx.shape[1] xe = zeros((nobs, m)) for i in range(nobs): xe[i, :] = bigxe[i::nobs].sum(0)[None, :] self._moments = xe
def __init__( self, x: List[ndarray], eps: NDArray, sigma: NDArray, full_sigma: NDArray, *, gls: bool = False, debiased: bool = False, constraints: Optional[LinearConstraint] = None, ) -> None: super(HeteroskedasticCovariance, self).__init__( x, eps, sigma, full_sigma, gls=gls, debiased=debiased, constraints=constraints, ) self._name = "Heteroskedastic (Robust) Covariance" k = len(x) nobs = eps.shape[0] if gls: weights = inv(sigma) bigx = blocked_diag_product(x, weights) e = eps.T.ravel()[:, None] bigxe = bigx * e m = bigx.shape[1] xe = zeros((nobs, m)) for i in range(nobs): xe[i, :] = bigxe[i::nobs].sum(0)[None, :] else: # Do not require blocking when not using GLS k_tot = sum(map(lambda a: a.shape[1], x)) xe = empty((nobs, k_tot)) loc = 0 for i in range(k): offset = x[i].shape[1] xe[:, loc:loc + offset] = x[i] * eps[:, i:i + 1] loc += offset self._moments = xe
def _gls_estimate(self, eps, nobs, total_cols, ci, full_cov, debiased): """Core estimation routine for iterative GLS""" wx, wy = self._wx, self._wy sigma = self._sigma if sigma is None: sigma = eps.T @ eps / nobs sigma *= self._sigma_scale(debiased) if not full_cov: sigma = diag(diag(sigma)) sigma_inv = inv(sigma) k = len(wy) if self.constraints is not None: cons = self.constraints sigma_m12 = inv_matrix_sqrt(sigma) x = blocked_diag_product(wx, sigma_m12) y = blocked_column_product(wy, sigma_m12) xt = x @ cons.t xpx = xt.T @ xt xpy = xt.T @ (y - x @ cons.a.T) paramsc = solve(xpx, xpy) params = cons.t @ paramsc + cons.a.T else: xpx = blocked_inner_prod(wx, sigma_inv) xpy = zeros((total_cols, 1)) for i in range(k): sy = zeros((nobs, 1)) for j in range(k): sy += sigma_inv[i, j] * wy[j] xpy[ci[i]:ci[i + 1]] = wx[i].T @ sy params = solve(xpx, xpy) beta = params loc = 0 for j in range(k): _wx = wx[j] _wy = wy[j] kx = _wx.shape[1] eps[:, [j]] = _wy - _wx @ beta[loc:loc + kx] loc += kx return beta, eps, sigma
def test_diag_product(data): y, x, sigma = data efficient = blocked_diag_product(x, sigma) nobs = x[0].shape[0] omega = np.kron(sigma, np.eye(nobs)) k = len(x) bigx = [] for i in range(k): row = [] for j in range(k): if i == j: row.append(x[i]) else: row.append(np.zeros((nobs, x[j].shape[1]))) bigx.append(np.hstack(row)) bigx = np.vstack(bigx) expected = omega @ bigx assert_allclose(efficient, expected)
def fit(self, *, method=None, full_cov=True, iterate=False, iter_limit=100, tol=1e-6, cov_type='robust', **cov_config): """ Estimate model parameters Parameters ---------- method : {None, 'gls', 'ols'} Estimation method. Default auto selects based on regressors, using OLS only if all regressors are identical. The other two arguments force the use of GLS or OLS. full_cov : bool Flag indicating whether to utilize information in correlations when estimating the model with GLS iterate : bool Flag indicating to iterate GLS until convergence of iter limit iterations have been completed iter_limit : int Maximum number of iterations for iterative GLS tol : float Tolerance to use when checking for convergence in iterative GLS cov_type : str Name of covariance estimator. Valid options are * 'unadjusted', 'homoskedastic' - Classic covariance estimator * 'robust', 'heteroskedastic' - Heteroskedasticit robust covariance estimator **cov_config Additional parameters to pass to covariance estimator. All estimators support debiased which employs a small-sample adjustment Returns ------- results : SURResults Estimation results """ cov_type = cov_type.lower() if cov_type not in ('unadjusted', 'robust', 'homoskedastic', 'heteroskedastic'): raise ValueError('Unknown cov_type: {0}'.format(cov_type)) cov_type = 'unadjusted' if cov_type in ('unadjusted', 'homoskedastic') else 'robust' k = len(self._dependent) col_sizes = [0] + list(map(lambda v: v.ndarray.shape[1], self._exog)) col_idx = cumsum(col_sizes) total_cols = col_idx[-1] beta, eps = self._multivariate_ls_fit() nobs = eps.shape[0] debiased = cov_config.get('debiased', False) full_sigma = sigma = (eps.T @ eps / nobs) * self._sigma_scale(debiased) if (self._common_exog and method is None and self._constraints is None) or method == 'ols': return self._multivariate_ls_finalize(beta, eps, sigma, cov_type, **cov_config) beta_hist = [beta] nobs = eps.shape[0] iter_count = 0 delta = inf while ((iter_count < iter_limit and iterate) or iter_count == 0) and delta >= tol: beta, eps, sigma = self._gls_estimate(eps, nobs, total_cols, col_idx, full_cov, debiased) beta_hist.append(beta) delta = beta_hist[-1] - beta_hist[-2] delta = sqrt(np.mean(delta ** 2)) iter_count += 1 sigma_m12 = inv_matrix_sqrt(sigma) wy = blocked_column_product(self._wy, sigma_m12) wx = blocked_diag_product(self._wx, sigma_m12) gls_eps = wy - wx @ beta y = blocked_column_product(self._y, eye(k)) x = blocked_diag_product(self._x, eye(k)) eps = y - x @ beta return self._gls_finalize(beta, sigma, full_sigma, gls_eps, eps, cov_type, iter_count, **cov_config)