From 8c213e35318e47937665aa71f0b024b7d3424aff Mon Sep 17 00:00:00 2001 From: Kevin Sheppard Date: Fri, 31 Jan 2020 17:23:43 +0000 Subject: [PATCH] CLN: Improve typing Iterate accross typing to improve type info --- arch/bootstrap/base.py | 10 +- arch/data/binary/__init__.py | 2 +- arch/tests/bootstrap/test_bootstrap.py | 4 +- arch/tests/univariate/test_mean.py | 16 +-- arch/tests/univariate/test_moment.py | 5 +- .../simulation/dfgls_simulation_process.py | 2 +- arch/unitroot/unitroot.py | 2 +- arch/univariate/base.py | 24 ++-- arch/univariate/distribution.py | 120 +++++++++++++----- arch/univariate/mean.py | 3 +- 10 files changed, 122 insertions(+), 66 deletions(-) diff --git a/arch/bootstrap/base.py b/arch/bootstrap/base.py index 943e71e93d..36a42b7645 100644 --- a/arch/bootstrap/base.py +++ b/arch/bootstrap/base.py @@ -71,7 +71,7 @@ def _loo_jackknife( func: Callable[..., NDArray], nobs: int, args: Sequence[ArrayLike], - kwargs: ArrayLike, + kwargs: Dict[str, ArrayLike], ) -> NDArray: """ Leave one out jackknife estimation @@ -137,7 +137,9 @@ def _add_extra_kwargs( if extra_kwargs is None: return kwargs else: - return dict(list(kwargs.items()) + list(extra_kwargs.items())) + kwargs_copy = kwargs.copy() + kwargs_copy.update(extra_kwargs) + return kwargs_copy class IIDBootstrap(object, metaclass=DocStringInheritor): @@ -666,7 +668,7 @@ def clone(self, *args: ArrayLike, **kwargs: ArrayLike) -> "IIDBootstrap": bs Bootstrap instance """ - pos_arguments = copy.deepcopy(self._parameters) + pos_arguments: List[Union[int, ArrayLike]] = copy.deepcopy(self._parameters) pos_arguments.extend(args) bs = self.__class__(*pos_arguments, **kwargs) if self._seed is not None: @@ -1088,7 +1090,7 @@ def update_indices(self) -> Tuple[List[NDArray], Dict[str, NDArray]]: return pos_indices, kw_indices @property - def index(self) -> NDArray: + def index(self) -> Tuple[List[NDArray], Dict[str, NDArray]]: """ Returns the current index of the bootstrap diff --git a/arch/data/binary/__init__.py b/arch/data/binary/__init__.py index feae97093c..9d24fc57a0 100644 --- a/arch/data/binary/__init__.py +++ b/arch/data/binary/__init__.py @@ -5,7 +5,7 @@ def load() -> DataFrame: """ - Load the graduate school admissions dataused in the examples + Load the graduate school admissions data used in the examples Returns ------- diff --git a/arch/tests/bootstrap/test_bootstrap.py b/arch/tests/bootstrap/test_bootstrap.py index b1a5b8144f..ad382fdde2 100644 --- a/arch/tests/bootstrap/test_bootstrap.py +++ b/arch/tests/bootstrap/test_bootstrap.py @@ -606,7 +606,7 @@ def test_apply(bs_setup): bs.seed(23456) results = bs.apply(bs_setup.func, 1000) - bs.reset(23456) + bs.reset(True) direct_results = [] for pos, _ in bs.bootstrap(1000): direct_results.append(bs_setup.func(*pos)) @@ -619,7 +619,7 @@ def test_apply_series(bs_setup): bs.seed(23456) results = bs.apply(bs_setup.func, 1000) - bs.reset(23456) + bs.reset(True) direct_results = [] for pos, _ in bs.bootstrap(1000): direct_results.append(bs_setup.func(*pos)) diff --git a/arch/tests/univariate/test_mean.py b/arch/tests/univariate/test_mean.py index 91f3e25d60..d039b21308 100644 --- a/arch/tests/univariate/test_mean.py +++ b/arch/tests/univariate/test_mean.py @@ -123,7 +123,7 @@ def test_constant_mean(self): direct = pd.DataFrame( index=np.arange(self.y.shape[0]), columns=["h.{0:>02d}".format(i + 1) for i in range(20)], - dtype=np.float64, + dtype="double", ) direct.iloc[20:, :] = res.params.iloc[0] # TODO @@ -156,7 +156,7 @@ def test_zero_mean(self): direct = pd.DataFrame( index=np.arange(self.y.shape[0]), columns=["h.{0:>02d}".format(i + 1) for i in range(99)], - dtype=np.float64, + dtype="double", ) direct.iloc[:, :] = 0.0 assert isinstance(forecasts, ARCHModelForecast) @@ -271,7 +271,7 @@ def test_har(self): direct = pd.DataFrame( index=np.arange(t), columns=["h." + str(i + 1) for i in range(6)], - dtype=np.float64, + dtype="float64", ) params = np.asarray(res.params) @@ -303,7 +303,7 @@ def test_har(self): direct = pd.DataFrame( index=self.y_series.index, columns=["h." + str(i + 1) for i in range(6)], - dtype=np.float64, + dtype="float64", ) forecasts = res.forecast(horizon=6) params = np.asarray(res.params) @@ -393,7 +393,7 @@ def test_ar(self): direct = pd.DataFrame( index=np.arange(y.shape[0]), columns=["h." + str(i + 1) for i in range(5)], - dtype=np.float64, + dtype="float64", ) params = res.params.iloc[:-1] for i in range(2, y.shape[0]): @@ -458,10 +458,10 @@ def test_ar_plot(self): res.plot(scale=360) res.hedgehog_plot(start=500) - res.hedgehog_plot(start=500, type="mean") - res.hedgehog_plot(type="volatility") + res.hedgehog_plot(start=500, plot_type="mean") + res.hedgehog_plot(plot_type="volatility") res.hedgehog_plot(start=500, method="simulation", simulations=100) - res.hedgehog_plot(type="volatility", method="bootstrap") + res.hedgehog_plot(plot_type="volatility", method="bootstrap") def test_arch_arx(self): self.rng.seed(12345) diff --git a/arch/tests/univariate/test_moment.py b/arch/tests/univariate/test_moment.py index 62e37de1c5..b7e3232208 100644 --- a/arch/tests/univariate/test_moment.py +++ b/arch/tests/univariate/test_moment.py @@ -1,4 +1,4 @@ -from numpy import exp, inf, log, nan, pi +from numpy import exp, inf, log, nan, ones_like, pi from numpy.testing import assert_almost_equal, assert_equal import pytest from scipy.integrate import quad @@ -43,7 +43,8 @@ def test_moment(dist, params): # verify moments that exist def f(x, n): - return (x ** n) * exp(dist.loglikelihood(params, x, 1, True)) + sigma2 = ones_like(x) + return (x ** n) * exp(dist.loglikelihood(params, x, sigma2, True)) for n in range(6): # moments 0-5 diff --git a/arch/unitroot/critical_values/simulation/dfgls_simulation_process.py b/arch/unitroot/critical_values/simulation/dfgls_simulation_process.py index 7d9cead2ea..60715cb4ee 100644 --- a/arch/unitroot/critical_values/simulation/dfgls_simulation_process.py +++ b/arch/unitroot/critical_values/simulation/dfgls_simulation_process.py @@ -120,7 +120,7 @@ # Compute tau star err_large = res_large.resid # Missing 1 parameter here, replace with 0 - params = np.append(res_small.params, 0.0) + params = np.append(np.asarray(res_small.params), np.zeros(1)) err_small = lhs_large - rhs_large.dot(params) # Find the location that minimizes the total absolute error m = lhs_large.shape[0] diff --git a/arch/unitroot/unitroot.py b/arch/unitroot/unitroot.py index 3f4049c3d7..f1bbfb18d1 100644 --- a/arch/unitroot/unitroot.py +++ b/arch/unitroot/unitroot.py @@ -1820,7 +1820,7 @@ def auto_bandwidth( for i in range(n + 1): a = list(y[i:]) b = list(y[: len(y) - i]) - sig[i] = sum([i * j for (i, j) in zip(a, b)]) + sig[i] = int(sum([i * j for (i, j) in zip(a, b)])) sigma_m1 = sig[1 : len(sig)] # sigma without the 1st element s0 = sig[0] + 2 * sum(sigma_m1) diff --git a/arch/univariate/base.py b/arch/univariate/base.py index 31ab72136d..90a9855e20 100644 --- a/arch/univariate/base.py +++ b/arch/univariate/base.py @@ -427,7 +427,7 @@ def _parse_parameters(self, x: ArrayLike) -> Tuple[NDArray, NDArray, NDArray]: def fix( self, - params: ArrayLike1D, + params: Union[Sequence[float], ArrayLike1D], first_obs: Union[int, DateLike] = None, last_obs: Union[int, DateLike] = None, ) -> "ARCHModelFixedResult": @@ -464,7 +464,7 @@ def fix( var_bounds = v.variance_bounds(resids) - params = np.asarray(params) + params = ensure1d(params, "params", False) loglikelihood = -1.0 * self._loglikelihood(params, sigma2, backcast, var_bounds) mp, vp, dp = self._parse_parameters(params) @@ -1453,7 +1453,7 @@ def hedgehog_plot( >>> sim_data.index = pd.date_range('2000-01-01',periods=250) >>> am = arch_model(sim_data['data'],mean='HAR',lags=[1,5,22], vol='Constant') >>> res = am.fit() - >>> fig = res.hedgehog_plot(type='mean') + >>> fig = res.hedgehog_plot(plot_type='mean') """ import matplotlib.pyplot as plt @@ -1888,7 +1888,7 @@ def _format_forecasts( horizon = values.shape[1] format_str = "{0:>0" + str(int(np.ceil(np.log10(horizon + 0.5)))) + "}" columns = ["h." + format_str.format(h + 1) for h in range(horizon)] - forecasts = DataFrame(values, index=index, columns=columns, dtype=np.float64) + forecasts = DataFrame(values, index=index, columns=columns, dtype="float") return forecasts @@ -1917,10 +1917,10 @@ class ARCHModelForecastSimulation(object): def __init__( self, - values: DataFrame, - residuals: DataFrame, - variances: DataFrame, - residual_variances: DataFrame, + values: NDArray, + residuals: NDArray, + variances: NDArray, + residual_variances: NDArray, ) -> None: self._values = values self._residuals = residuals @@ -1928,19 +1928,19 @@ def __init__( self._residual_variances = residual_variances @property - def values(self) -> DataFrame: + def values(self) -> NDArray: return self._values @property - def residuals(self) -> DataFrame: + def residuals(self) -> NDArray: return self._residuals @property - def variances(self) -> DataFrame: + def variances(self) -> NDArray: return self._variances @property - def residual_variances(self) -> DataFrame: + def residual_variances(self) -> NDArray: return self._residual_variances diff --git a/arch/univariate/distribution.py b/arch/univariate/distribution.py index 1bedae5d32..9bf2e5f327 100644 --- a/arch/univariate/distribution.py +++ b/arch/univariate/distribution.py @@ -52,14 +52,19 @@ def name(self) -> str: """The name of the distribution""" return self._name - def _check_constraints(self, params: ArrayLike1D) -> ArrayLike1D: - bounds = self.bounds(None) - nparams = 0 if params is None else len(params) + def _check_constraints( + self, parameters: Optional[Union[Sequence[float], ArrayLike1D]] + ) -> NDArray: + bounds = self.bounds(empty(0)) + if parameters is not None: + params = ensure1d(parameters, "parameters", False) + nparams = len(params) + else: + nparams = 0 if nparams != len(bounds): raise ValueError("parameters must have {0} elements".format(len(bounds))) if len(bounds) == 0: - return params - params = asarray(params) + return empty(0) for p, n, b in zip(params, self.name, bounds): if not (b[0] <= p <= b[1]): raise ValueError( @@ -152,7 +157,7 @@ def bounds(self, resids: NDArray) -> List[Tuple[float, float]]: @abstractmethod def loglikelihood( self, - parameters: Sequence[float], + parameters: Union[Sequence[float], ArrayLike1D], resids: ArrayLike, sigma2: ArrayLike, individual: bool = False, @@ -215,7 +220,9 @@ def parameter_names(self) -> List[str]: @abstractmethod def ppf( - self, pits: ArrayLike1D, parameters: Optional[ArrayLike1D] = None + self, + pits: Union[Sequence[float], ArrayLike1D], + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> NDArray: """ Inverse cumulative density function (ICDF) @@ -237,7 +244,9 @@ def ppf( @abstractmethod def cdf( - self, resids: ArrayLike, parameters: Optional[ArrayLike1D] = None + self, + resids: Union[Sequence[float], ArrayLike1D], + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> NDArray: """ Cumulative distribution function @@ -258,7 +267,9 @@ def cdf( pass @abstractmethod - def moment(self, n: int, parameters: Optional[ArrayLike1D] = None) -> float: + def moment( + self, n: int, parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None + ) -> float: """ Moment of order n @@ -278,7 +289,10 @@ def moment(self, n: int, parameters: Optional[ArrayLike1D] = None) -> float: @abstractmethod def partial_moment( - self, n: int, z: float = 0.0, parameters: Optional[ArrayLike1D] = None + self, + n: int, + z: float = 0.0, + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> float: r""" Order n lower partial moment from -inf to z @@ -341,7 +355,7 @@ def bounds(self, resids: NDArray) -> List[Tuple[float, float]]: def loglikelihood( self, - parameters: Sequence[float], + parameters: Union[Sequence[float], ArrayLike1D], resids: ArrayLike, sigma2: ArrayLike, individual: bool = False, @@ -398,26 +412,35 @@ def parameter_names(self) -> List[str]: return [] def cdf( - self, resids: ArrayLike, parameters: Optional[ArrayLike1D] = None + self, + resids: Union[Sequence[float], ArrayLike1D], + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> NDArray: self._check_constraints(parameters) - return stats.norm.cdf(resids) + return stats.norm.cdf(asarray(resids)) def ppf( - self, pits: ArrayLike1D, parameters: Optional[ArrayLike1D] = None + self, + pits: Union[Sequence[float], ArrayLike1D], + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> NDArray: self._check_constraints(parameters) pits = asarray(pits) return stats.norm.ppf(pits) - def moment(self, n: int, parameters: Optional[ArrayLike1D] = None) -> float: + def moment( + self, n: int, parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None + ) -> float: if n < 0: return nan return stats.norm.moment(n) def partial_moment( - self, n: int, z: float = 0.0, parameters: Optional[ArrayLike1D] = None + self, + n: int, + z: float = 0.0, + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> float: if n < 0: return nan @@ -449,7 +472,7 @@ def bounds(self, resids: NDArray) -> List[Tuple[float, float]]: def loglikelihood( self, - parameters: Sequence[float], + parameters: Union[Sequence[float], ArrayLike1D], resids: ArrayLike, sigma2: ArrayLike, individual: bool = False, @@ -541,15 +564,19 @@ def parameter_names(self) -> List[str]: return ["nu"] def cdf( - self, resids: ArrayLike, parameters: Optional[ArrayLike1D] = None + self, + resids: Union[Sequence[float], ArrayLike1D], + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> NDArray: parameters = self._check_constraints(parameters) nu = parameters[0] var = nu / (nu - 2) - return stats.t(nu, scale=1.0 / sqrt(var)).cdf(resids) + return stats.t(nu, scale=1.0 / sqrt(var)).cdf(asarray(resids)) def ppf( - self, pits: ArrayLike1D, parameters: Optional[ArrayLike1D] = None + self, + pits: Union[Sequence[float], ArrayLike1D], + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> NDArray: parameters = self._check_constraints(parameters) pits = asarray(pits) @@ -557,17 +584,21 @@ def ppf( var = nu / (nu - 2) return stats.t(nu, scale=1.0 / sqrt(var)).ppf(pits) - def moment(self, n: int, parameters: Optional[ArrayLike1D] = None) -> float: + def moment( + self, n: int, parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None + ) -> float: if n < 0: return nan - parameters = self._check_constraints(parameters) nu = parameters[0] var = nu / (nu - 2) return stats.t.moment(n, nu, scale=1.0 / sqrt(var)) def partial_moment( - self, n: int, z: float = 0.0, parameters: Optional[ArrayLike1D] = None + self, + n: int, + z: float = 0.0, + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> float: parameters = self._check_constraints(parameters) nu = parameters[0] @@ -658,12 +689,13 @@ def bounds(self, resids: NDArray) -> List[Tuple[float, float]]: def loglikelihood( self, - parameters: Sequence[float], + parameters: Union[Sequence[float], ArrayLike1D], resids: ArrayLike, sigma2: ArrayLike, individual: bool = False, ) -> NDArray: - r"""Computes the log-likelihood of assuming residuals are have a + r""" + Computes the log-likelihood of assuming residuals are have a standardized (to have unit variance) Skew Student's t distribution, conditional on the variance. @@ -830,7 +862,9 @@ def __const_c(parameters: Sequence[float]) -> float: return gammaln((eta + 1) / 2) - gammaln(eta / 2) - log(pi * (eta - 2)) / 2 def cdf( - self, resids: ArrayLike, parameters: Optional[ArrayLike1D] = None + self, + resids: ArrayLike, + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> NDArray: parameters = self._check_constraints(parameters) scalar = isscalar(resids) @@ -846,6 +880,7 @@ def cdf( y1 = (b * resids + a) / (1 - lam) * sqrt(var) y2 = (b * resids + a) / (1 + lam) * sqrt(var) tcdf = stats.t(eta).cdf + resids = asarray(resids) p = (1 - lam) * tcdf(y1) * (resids < (-a / b)) p += (resids >= (-a / b)) * ((1 - lam) / 2 + (1 + lam) * (tcdf(y2) - 0.5)) if scalar: @@ -853,7 +888,9 @@ def cdf( return p def ppf( - self, pits: ArrayLike1D, parameters: Optional[ArrayLike1D] = None + self, + pits: Union[Sequence[float], ArrayLike1D], + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> NDArray: parameters = self._check_constraints(parameters) scalar = isscalar(pits) @@ -879,7 +916,9 @@ def ppf( icdf = icdf[0] return icdf - def moment(self, n: int, parameters: Optional[ArrayLike1D] = None) -> float: + def moment( + self, n: int, parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None + ) -> float: parameters = self._check_constraints(parameters) eta, lam = parameters @@ -910,7 +949,10 @@ def moment(self, n: int, parameters: Optional[ArrayLike1D] = None) -> float: return moment def partial_moment( - self, n: int, z: float = 0.0, parameters: Optional[ArrayLike1D] = None + self, + n: int, + z: float = 0.0, + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> float: parameters = self._check_constraints(parameters) eta, lam = parameters @@ -971,7 +1013,7 @@ def bounds(self, resids: NDArray) -> List[Tuple[float, float]]: def loglikelihood( self, - parameters: Sequence[float], + parameters: Union[Sequence[float], ArrayLike1D], resids: ArrayLike, sigma2: ArrayLike, individual: bool = False, @@ -1070,7 +1112,9 @@ def parameter_names(self) -> List[str]: return ["nu"] def ppf( - self, pits: ArrayLike1D, parameters: Optional[ArrayLike1D] = None + self, + pits: Union[Sequence[float], ArrayLike1D], + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> NDArray: parameters = self._check_constraints(parameters) pits = asarray(pits) @@ -1079,14 +1123,19 @@ def ppf( return stats.gennorm(nu, scale=1.0 / sqrt(var)).ppf(pits) def cdf( - self, resids: ArrayLike, parameters: Optional[ArrayLike1D] = None + self, + resids: ArrayLike, + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> NDArray: parameters = self._check_constraints(parameters) nu = parameters[0] var = stats.gennorm(nu).var() + resids = asarray(resids) return stats.gennorm(nu, scale=1.0 / sqrt(var)).cdf(resids) - def moment(self, n: int, parameters: Optional[ArrayLike1D] = None) -> float: + def moment( + self, n: int, parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None + ) -> float: if n < 0: return nan @@ -1096,7 +1145,10 @@ def moment(self, n: int, parameters: Optional[ArrayLike1D] = None) -> float: return stats.gennorm.moment(n, nu, scale=1.0 / sqrt(var)) def partial_moment( - self, n: int, z: float = 0.0, parameters: Optional[ArrayLike1D] = None + self, + n: int, + z: float = 0.0, + parameters: Optional[Union[Sequence[float], ArrayLike1D]] = None, ) -> float: parameters = self._check_constraints(parameters) nu = parameters[0] diff --git a/arch/univariate/mean.py b/arch/univariate/mean.py index 2ffb868cc2..e9185c2221 100644 --- a/arch/univariate/mean.py +++ b/arch/univariate/mean.py @@ -1002,7 +1002,7 @@ def _model_description(self, include_lags: bool = False) -> Dict[str, str]: def simulate( self, - params: ArrayLike, + params: Union[Sequence[float], ArrayLike1D], nobs: int, burn: int = 500, initial_value: Optional[Union[float, NDArray]] = None, @@ -1052,6 +1052,7 @@ def simulate( >>> zm.volatility = GARCH(p=1, o=1, q=1) >>> sim_data = zm.simulate([0.05, 0.1, 0.1, 0.8], 300) """ + params = ensure1d(params, "params", False) if initial_value is not None or x is not None: raise ValueError( "Both initial value and x must be none when "