diff --git a/statsmodels/compat/pandas.py b/statsmodels/compat/pandas.py index 5b94a8421ce..d9a0e5e29a8 100644 --- a/statsmodels/compat/pandas.py +++ b/statsmodels/compat/pandas.py @@ -1,4 +1,5 @@ from distutils.version import LooseVersion +from typing import Optional import numpy as np import pandas as pd @@ -26,6 +27,9 @@ "make_dataframe", "to_numpy", "pandas_lt_1_0_0", + "get_cached_func", + "get_cached_doc", + "call_cached_func", ] version = LooseVersion(pd.__version__) @@ -181,3 +185,19 @@ def to_numpy(po: pd.DataFrame) -> np.ndarray: return po.to_numpy() except AttributeError: return po.values + + +def get_cached_func(cached_prop): + try: + return cached_prop.fget + except AttributeError: + return cached_prop.func + + +def call_cached_func(cached_prop, *args, **kwargs): + f = get_cached_func(cached_prop) + return f(*args, **kwargs) + + +def get_cached_doc(cached_prop) -> Optional[str]: + return get_cached_func(cached_prop).__doc__ diff --git a/statsmodels/regression/rolling.py b/statsmodels/regression/rolling.py index c682e0f4662..6666aaac454 100644 --- a/statsmodels/regression/rolling.py +++ b/statsmodels/regression/rolling.py @@ -8,7 +8,13 @@ License: 3-clause BSD """ from statsmodels.compat.numpy import lstsq -from statsmodels.compat.pandas import Appender, Substitution, cache_readonly +from statsmodels.compat.pandas import ( + Appender, + Substitution, + cache_readonly, + call_cached_func, + get_cached_doc, +) from collections import namedtuple @@ -474,6 +480,7 @@ class RollingRegressionResults(object): cov_type : str Name of covariance estimator """ + _data_in_cache = tuple() def __init__( @@ -515,19 +522,20 @@ def _wrap(self, val): return DataFrame(val, columns=col_names, index=mi) @cache_readonly - @Appender(RegressionResults.aic.func.__doc__) + @Appender(get_cached_doc(RegressionResults.aic)) def aic(self): - return self._wrap(RegressionResults.aic.func(self)) + return self._wrap(call_cached_func(RegressionResults.aic, self)) @cache_readonly - @Appender(RegressionResults.bic.func.__doc__) + @Appender(get_cached_doc(RegressionResults.bic)) def bic(self): with np.errstate(divide="ignore"): - return self._wrap(RegressionResults.bic.func(self)) + return self._wrap(call_cached_func(RegressionResults.bic, self)) def info_criteria(self, crit, dk_params=0): - return self._wrap(RegressionResults.info_criteria( - self, crit, dk_params=dk_params)) + return self._wrap( + RegressionResults.info_criteria(self, crit, dk_params=dk_params) + ) @cache_readonly def params(self): @@ -535,12 +543,12 @@ def params(self): return self._wrap(self._params) @cache_readonly - @Appender(RegressionResults.ssr.func.__doc__) + @Appender(get_cached_doc(RegressionResults.ssr)) def ssr(self): return self._wrap(self._ssr) @cache_readonly - @Appender(RegressionResults.llf.func.__doc__) + @Appender(get_cached_doc(RegressionResults.llf)) def llf(self): return self._wrap(self._llf) @@ -555,27 +563,29 @@ def k_constant(self): return self._k_constant @cache_readonly - @Appender(RegressionResults.centered_tss.func.__doc__) + @Appender(get_cached_doc(RegressionResults.centered_tss)) def centered_tss(self): return self._centered_tss @cache_readonly - @Appender(RegressionResults.uncentered_tss.func.__doc__) + @Appender(get_cached_doc(RegressionResults.uncentered_tss)) def uncentered_tss(self): return self._uncentered_tss @cache_readonly - @Appender(RegressionResults.rsquared.func.__doc__) + @Appender(get_cached_doc(RegressionResults.rsquared)) def rsquared(self): - return self._wrap(RegressionResults.rsquared.func(self)) + return self._wrap(call_cached_func(RegressionResults.rsquared, self)) @cache_readonly - @Appender(RegressionResults.rsquared_adj.func.__doc__) + @Appender(get_cached_doc(RegressionResults.rsquared_adj)) def rsquared_adj(self): - return self._wrap(RegressionResults.rsquared_adj.func(self)) + return self._wrap( + call_cached_func(RegressionResults.rsquared_adj, self) + ) @cache_readonly - @Appender(RegressionResults.nobs.func.__doc__) + @Appender(get_cached_doc(RegressionResults.nobs)) def nobs(self): return self._wrap(self._nobs) @@ -590,24 +600,24 @@ def use_t(self): return self._use_t @cache_readonly - @Appender(RegressionResults.ess.func.__doc__) + @Appender(get_cached_doc(RegressionResults.ess)) def ess(self): - return self._wrap(RegressionResults.ess.func(self)) + return self._wrap(call_cached_func(RegressionResults.ess, self)) @cache_readonly - @Appender(RegressionResults.mse_model.func.__doc__) + @Appender(get_cached_doc(RegressionResults.mse_model)) def mse_model(self): - return self._wrap(RegressionResults.mse_model.func(self)) + return self._wrap(call_cached_func(RegressionResults.mse_model, self)) @cache_readonly - @Appender(RegressionResults.mse_resid.func.__doc__) + @Appender(get_cached_doc(RegressionResults.mse_resid)) def mse_resid(self): - return self._wrap(RegressionResults.mse_resid.func(self)) + return self._wrap(call_cached_func(RegressionResults.mse_resid, self)) @cache_readonly - @Appender(RegressionResults.mse_total.func.__doc__) + @Appender(get_cached_doc(RegressionResults.mse_total)) def mse_total(self): - return self._wrap(RegressionResults.mse_total.func(self)) + return self._wrap(call_cached_func(RegressionResults.mse_total, self)) @cache_readonly def _cov_params(self): @@ -629,17 +639,19 @@ def cov_params(self): the returned covariance is a DataFrame with a MultiIndex with key (observation, variable), so that the covariance for observation with index i is cov.loc[i]. - """ + """ return self._wrap(self._cov_params) @cache_readonly - @Appender(RegressionResults.f_pvalue.func.__doc__) + @Appender(get_cached_doc(RegressionResults.f_pvalue)) def f_pvalue(self): with np.errstate(invalid="ignore"): - return self._wrap(RegressionResults.f_pvalue.func(self)) + return self._wrap( + call_cached_func(RegressionResults.f_pvalue, self) + ) @cache_readonly - @Appender(RegressionResults.fvalue.func.__doc__) + @Appender(get_cached_doc(RegressionResults.fvalue)) def fvalue(self): if self._cov_type == "nonrobust": return self.mse_model / self.mse_resid @@ -665,19 +677,21 @@ def fvalue(self): return stat @cache_readonly - @Appender(RegressionResults.bse.func.__doc__) + @Appender(get_cached_doc(RegressionResults.bse)) def bse(self): with np.errstate(invalid="ignore"): return self._wrap(np.sqrt(np.diagonal(self._cov_params, 0, 2))) @cache_readonly - @Appender(LikelihoodModelResults.tvalues.func.__doc__) + @Appender(get_cached_doc(LikelihoodModelResults.tvalues)) def tvalues(self): with np.errstate(invalid="ignore"): - return self._wrap(LikelihoodModelResults.tvalues.func(self)) + return self._wrap( + call_cached_func(LikelihoodModelResults.tvalues, self) + ) @cache_readonly - @Appender(LikelihoodModelResults.pvalues.func.__doc__) + @Appender(get_cached_doc(LikelihoodModelResults.pvalues)) def pvalues(self): if self.use_t: df_resid = getattr(self, "df_resid_inference", self.df_resid) diff --git a/statsmodels/tsa/ar_model.py b/statsmodels/tsa/ar_model.py index 623e0402c25..42c64b51a4e 100644 --- a/statsmodels/tsa/ar_model.py +++ b/statsmodels/tsa/ar_model.py @@ -1,7 +1,12 @@ # -*- coding: utf-8 -*- from __future__ import annotations -from statsmodels.compat.pandas import Appender, Substitution, to_numpy +from statsmodels.compat.pandas import ( + Appender, + Substitution, + call_cached_func, + to_numpy, +) from collections.abc import Iterable import datetime as dt @@ -1774,9 +1779,9 @@ def compute_ics(res): nobs=nobs, df_model=df_model, sigma2=sigma2, llf=llf ) - aic = AutoRegResults.aic.func(res) - bic = AutoRegResults.bic.func(res) - hqic = AutoRegResults.hqic.func(res) + aic = call_cached_func(AutoRegResults.aic, res) + bic = call_cached_func(AutoRegResults.bic, res) + hqic = call_cached_func(AutoRegResults.hqic, res) return aic, bic, hqic diff --git a/statsmodels/tsa/ardl/model.py b/statsmodels/tsa/ardl/model.py index 08a572f4349..26ee906b3d5 100644 --- a/statsmodels/tsa/ardl/model.py +++ b/statsmodels/tsa/ardl/model.py @@ -1,6 +1,6 @@ from __future__ import annotations -from statsmodels.compat.pandas import Appender, Substitution +from statsmodels.compat.pandas import Appender, Substitution, call_cached_func from statsmodels.compat.python import Literal from collections import defaultdict @@ -1450,9 +1450,9 @@ def compute_ics(y, x, df): nobs=nobs, df_model=df + x.shape[1], sigma2=sigma2, llf=llf ) - aic = ARDLResults.aic.func(res) - bic = ARDLResults.bic.func(res) - hqic = ARDLResults.hqic.func(res) + aic = call_cached_func(ARDLResults.aic, res) + bic = call_cached_func(ARDLResults.bic, res) + hqic = call_cached_func(ARDLResults.hqic, res) return aic, bic, hqic