def _force_psd(self): """ Forces covariance matrix to be positive semi-definite. This is useful when user is overwriting covariance parameters """ cov = near_psd(self.sigma) self._rhos = cov[tri_indices(self.dim, 1, 'lower')]
def fit_cor(copula: Copula, data: np.ndarray, typ: str) -> np.ndarray: """ Constructs parameter matrix from matrix of Kendall's Taus or Spearman's Rho Parameters ---------- copula: AbstractCopula Copula instance data: ndarray Data to fit copula with typ: {'irho', 'itau'} The type of rank correlation measure to use. 'itau' uses Kendall's tau while 'irho' uses Spearman's rho Returns ------- ndarray Parameter matrix is copula is elliptical. Otherwise, a vector """ indices = tri_indices(copula.dim, 1, 'lower') if typ == 'itau': tau = kendall_tau(data)[indices] theta = copula.itau(tau) elif typ == 'irho': rho = spearman_rho(data)[indices] theta = copula.irho(rho) else: raise ValueError("Correlation Inversion must be either 'itau' or 'irho'") if is_elliptical(copula): theta = near_psd(create_cov_matrix(theta))[indices] return theta
def fit_cor(copula, data: np.ndarray, typ: str) -> np.ndarray: """ Constructs parameter matrix from matrix of Kendall's Taus or Spearman's Rho Parameters ---------- copula: BaseCopula Copula instance data: ndarray Data to fit copula with typ: {'irho', 'itau'} The type of rank correlation measure to use. 'itau' uses Kendall's tau while 'irho' uses Spearman's rho Returns ------- ndarray Parameter matrix is copula is elliptical. Otherwise, a vector """ indices = tri_indices(copula.dim, 1, 'lower') if typ == 'itau': tau = kendall_tau(data)[indices] theta = copula.itau(tau) elif typ == 'irho': rho = spearman_rho(data)[indices] theta = copula.irho(rho) else: raise ValueError( "Correlation Inversion must be either 'itau' or 'irho'") if is_elliptical(copula): theta = near_psd(create_cov_matrix(theta))[indices] return theta
def __setitem__(self, index: Union[int, Tuple[Union[slice, int], Union[slice, int]], slice], value: Union[float, Collection[float], np.ndarray]): d = self.dim if np.isscalar(value): if value < -1 or value > 1: raise ValueError("correlation value must be between -1 and 1") else: value = np.asarray(value) if not np.all((value >= -1 - EPS) & (value <= 1 + EPS)): raise ValueError("correlation value must be between -1 and 1") if isinstance(index, slice): value = near_psd(value) if value.shape != (d, d): raise ValueError( f"The value being set should be a matrix of dimension ({d}, {d})" ) self._rhos = value[tri_indices(d, 1, 'lower')] return if isinstance(index, int): self._rhos[index] = value else: index = tuple(index) if len(index) != 2: raise IndexError('index can only be 1 or 2-dimensional') x, y = index # having 2 slices for indices is equivalent to self[:] if isinstance(x, slice) and isinstance(y, slice): self[:] = value return elif isinstance(x, slice) or isinstance(y, slice): value = np.repeat( value, d) if np.isscalar(value) else np.asarray(value) if len(value) != d: raise ValueError( f"value must be a scalar or be a vector with length {d}" ) # one of the item is for i, v in enumerate(value): idx = (i, y) if isinstance(x, slice) else (x, i) if idx[0] == idx[1]: # skip diagonals continue idx = _get_rho_index(d, idx) self._rhos[idx] = v else: # both are integers idx = _get_rho_index(d, index) self._rhos[idx] = float(value) self._force_psd()
def _get_rho_index(dim: int, index: Tuple[int, int]) -> int: x, y = index if x < 0 or y < 0: raise IndexError('Only positive indices are supported') elif x >= dim or y >= dim: raise IndexError('Index cannot be greater than dimension of copula') elif x == y: raise IndexError('Cannot set values along the diagonal') for j, v in enumerate(zip(*tri_indices(dim, 1, 'upper' if x < y else 'lower'))): if (x, y) == v: return j raise IndexError(f"Unable to find index {(x, y)}")
def __setitem__(self, i, value): d = self.dim if isinstance(i, slice): value = near_psd(value) if value.shape != (d, d): return IndexError(f"The value being set should be a matrix of dimension ({d}, {d})") self._rhos = value[tri_indices(d, 1, 'lower')] return if isinstance(i, int): self._rhos[i] = value else: i = _get_rho_index(d, i) self._rhos[i] = value self._force_psd()
def __setitem__(self, i, value): d = self.dim if isinstance(i, slice): value = near_psd(value) if value.shape != (d, d): return IndexError(f"The value being set should be a matrix of dimension ({d}, {d})") self._rhos = value[tri_indices(d, 1, 'lower')] return assert -1.0 <= value <= 1.0, "correlation value must be between -1 and 1" if isinstance(i, int): self._rhos[i] = value else: i = _get_rho_index(d, i) self._rhos[i] = value self._force_psd()
def initial_params(self) -> np.ndarray: if self.x0 is not None: return self.x0 if is_elliptical(self.copula): corr = pearson_rho(self.data) rhos = corr[tri_indices(self.copula.dim, 1, 'lower')] if hasattr(self.copula, '_df'): # T-distribution return np.array([4.669, *rhos]) # set df as Feigenbaum's constant else: # Gaussian return rhos try: start = CorrInversionEstimator(self.copula, self.data, self.verbose).fit('itau') ll = self.copula.log_lik(self.data) if np.isfinite(ll): return start else: if self.copula.name.lower( ) == 'clayton' and self.copula.dim == 2: # The support of bivariate claytonCopula with negative parameter is not # the full unit square; the further from 0, the more restricted. while start < 0: start += .2 self.copula.params = start if np.isfinite(self.copula.log_lik(self.data)): break if not np.isnan(ll) and np.isinf(ll): # for perfectly correlated data return start return self.copula.params except NotImplementedError: return self.copula.params
def _get_rho_index(d: int, i: Tuple[int, int]) -> int: i = tuple(i) if not isinstance(i, tuple): raise IndexError("only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean " "arrays are valid indices") if len(i) != 2: raise IndexError('only 2-dimensional indices supported') x, y = i if x < 0 or y < 0: raise IndexError('Only positive indices are supported') elif x >= d or y >= d: raise IndexError('Index cannot be greater than dimension of copula') elif x == y: raise IndexError('Cannot set values along the diagonal') for j, v in enumerate(zip(*tri_indices(d, 1, 'upper' if x < y else 'lower'))): if i == v: return j raise IndexError(f"Unable to find index {i}")
def initial_params(copula: Copula, data: np.ndarray, x0: np.ndarray): # ensure that initial is defined. If it is defined, checks that all x0 values are finite # neither infinite nor nan if x0 is not None and \ ((np.isscalar(x0) and not np.isfinite(x0)) or (not np.isscalar(x0) and all(np.isfinite(x0)))): return x0 if is_elliptical(copula): corr = pearson_rho(data) rhos = corr[tri_indices(copula.dim, 1, 'lower')] if copula.name.lower() == 'student': # T-distribution return np.array([4.669, *rhos]) # set df as Feigenbaum's constant else: # Gaussian return rhos try: start = estimate_corr_inverse_params(copula, data, 'itau') ll = copula.log_lik(data) if np.isfinite(ll): return start else: if copula.name.lower() == 'clayton' and copula.dim == 2: # The support of bivariate claytonCopula with negative parameter is not # the full unit square; the further from 0, the more restricted. while start < 0: start += .2 copula.params = start if np.isfinite(copula.log_lik(data)): break if not np.isnan(ll) and np.isinf(ll): # for perfectly correlated data return start return copula.params except NotImplementedError: return copula.params
def initial_params(self): if self._x0 is not None: return self._x0 if is_elliptical(self.copula): corr = pearson_rho(self.data) rhos = corr[tri_indices(self.copula.dim, 1, 'lower')] if hasattr(self.copula, '_df'): # T-distribution return np.array([4.669, *rhos]) # set df as Feigenbaum's constant else: # Gaussian return rhos try: start = (CorrInversionEstimator(self.copula, self.data, False, self._verbose).fit('itau')) ll = self.copula.log_lik(self.data) if np.isfinite(ll): return start else: if self.copula.name.lower() == 'clayton' and self.copula.dim == 2: # The support of bivariate claytonCopula with negative parameter is not # the full unit square; the further from 0, the more restricted. while start < 0: start += .2 self.copula.params = start if np.isfinite(self.copula.log_lik(self.data)): break if not np.isnan(ll) and np.isinf(ll): # for perfectly correlated data return start return self.copula.params except NotImplementedError: return self.copula.params