Пример #1
0
 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')]
Пример #2
0
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
Пример #3
0
 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')]
Пример #4
0
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
Пример #5
0
    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()
Пример #6
0
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)}")
Пример #7
0
    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()
Пример #8
0
    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()
Пример #9
0
    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
Пример #10
0
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}")
Пример #11
0
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
Пример #12
0
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}")
Пример #13
0
    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