From cdf3cdfc1870a4b5100814c4355fed7f181ea956 Mon Sep 17 00:00:00 2001 From: "quant-ranger[bot]" <132915763+quant-ranger[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 06:56:52 +0100 Subject: [PATCH 1/5] Pre-commit autoupdate (#672) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 149a9f59..4f1d68ec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - --safe - --target-version=py37 - repo: https://github.com/Quantco/pre-commit-mirrors-flake8 - rev: 6.0.0 + rev: 6.1.0 hooks: - id: flake8-conda additional_dependencies: [ From 17f1f69f116a54007457e5642004a75e0cd06fea Mon Sep 17 00:00:00 2001 From: Jan Tilly Date: Mon, 7 Aug 2023 18:20:53 +0200 Subject: [PATCH 2/5] Use boa in CI. (#673) --- .github/workflows/conda-build.sh | 4 ++-- .github/workflows/macos-conda-build.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/conda-build.sh b/.github/workflows/conda-build.sh index 71ec92f4..f93b0ad9 100755 --- a/.github/workflows/conda-build.sh +++ b/.github/workflows/conda-build.sh @@ -7,5 +7,5 @@ export CONDA_BUILD_YML=$1 SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" source ${SCRIPT_DIR}/base.sh $* conda activate base -mamba install -y conda-build -conda build -m .ci_support/${CONDA_BUILD_YML}.yaml conda.recipe +mamba install -y boa conda-build +conda mambabuild -m .ci_support/${CONDA_BUILD_YML}.yaml conda.recipe diff --git a/.github/workflows/macos-conda-build.sh b/.github/workflows/macos-conda-build.sh index 6fbd9ce9..598f8cf4 100755 --- a/.github/workflows/macos-conda-build.sh +++ b/.github/workflows/macos-conda-build.sh @@ -6,7 +6,7 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" source ${SCRIPT_DIR}/base.sh $* conda activate base -mamba install -y conda-build -c conda-forge +mamba install -y conda-build boa -c conda-forge # Don't test cross-compiled result (there is no emulation) and use the latest MacOS SDK. if grep -q "osx-arm64" .ci_support/${CONDA_BUILD_YML}.yaml; then CONDA_BUILD_ARGS="--no-test" @@ -16,4 +16,4 @@ CONDA_BUILD_SYSROOT: - "${CONDA_BUILD_SYSROOT}" EOF fi -conda build -m .ci_support/${CONDA_BUILD_YML}.yaml conda.recipe ${CONDA_BUILD_ARGS:-} +conda mambabuild -m .ci_support/${CONDA_BUILD_YML}.yaml conda.recipe ${CONDA_BUILD_ARGS:-} From f60a08870d473063e4e1ffa14d6be5351812ecc3 Mon Sep 17 00:00:00 2001 From: Martin Stancsics Date: Tue, 8 Aug 2023 12:47:27 +0200 Subject: [PATCH 3/5] Fix covariance matrix mutating feature names (#671) * Do not use _set_up_... in covariance_matrix * Add changelog entry --- CHANGELOG.rst | 4 ++++ src/glum/_glm.py | 37 +++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b63f3376..508b78e9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,10 @@ Changelog - Added the complementary log-log (`cloglog`) link function. +**Bug fix** + +- Fixed :meth:`~glum.GeneralizedLinearRegressorBase.covariance_matrix` mutating feature names when called with a data frame. See `here `_. + **Other changes:** - When computing the covariance matrix, check for ill-conditionedness for all types of input. Furthermore, do it in a more efficient way. diff --git a/src/glum/_glm.py b/src/glum/_glm.py index db8a2086..55171258 100644 --- a/src/glum/_glm.py +++ b/src/glum/_glm.py @@ -1456,26 +1456,31 @@ def covariance_matrix( Cambridge university press """ - ( - X, - y, - sample_weight, - offset, - sum_weights, - P1, - P2, - ) = self._set_up_and_check_fit_args( + + if isinstance(X, pd.DataFrame) and hasattr(self, "feature_dtypes_"): + X = _align_df_categories(X, self.feature_dtypes_) + + X, y = check_X_y_tabmat_compliant( X, y, - sample_weight, - offset, - solver=self.solver, - force_all_finite=self.force_all_finite, + accept_sparse=["csr", "csc", "coo"], + dtype="numeric", + copy=self._should_copy_X(), + ensure_2d=True, + allow_nd=False, + drop_first=self.drop_first, ) - # Here we don't want sample_weight to be normalized to sum up to 1 - # We want sample_weight to sum up to the number of samples - sample_weight = sample_weight * sum_weights + if isinstance(X, np.ndarray): + X = tm.DenseMatrix(X) + if sparse.issparse(X) and not isinstance(X, tm.SparseMatrix): + X = tm.SparseMatrix(X) + + sample_weight = _check_weights( + sample_weight, y.shape[0], X.dtype, force_all_finite=self.force_all_finite + ) + sum_weights = np.sum(sample_weight) + offset = _check_offset(offset, y.shape[0], X.dtype) mu = self.predict(X, offset=offset) if mu is None else np.asanyarray(mu) From 5a65ed6d286ad56550fc7db3a69e51616a7272d1 Mon Sep 17 00:00:00 2001 From: Martin Stancsics Date: Tue, 8 Aug 2023 14:28:06 +0200 Subject: [PATCH 4/5] Add the option to store the covariance matrix to avoid recomputing it (#661) * Add option to store covariance matrix during fit * Fix fitting with variance matrix estimation `.covariance_matrix()` expects X and weights in a different format than what we have at the end of `.fit(). * Store covariance matrix after estimation * Handle the alpha_search and glm_cv cases * Propagate covariance parameters * Add changelog * Slightly more lenient tests --- CHANGELOG.rst | 1 + src/glum/_glm.py | 203 ++++++++++++++++++++++++++++++++++-------- src/glum/_glm_cv.py | 36 ++++++++ tests/glm/test_glm.py | 131 +++++++++++++++++++++++++++ 4 files changed, 333 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 508b78e9..e45052e7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,6 +13,7 @@ Changelog **New feature** - Added the complementary log-log (`cloglog`) link function. +- Added the option to store the covariance matrix after estimating it. In this case, the covariance matrix does not have to be recomputed when calling inference methods. **Bug fix** diff --git a/src/glum/_glm.py b/src/glum/_glm.py index 55171258..c4a3fe5a 100644 --- a/src/glum/_glm.py +++ b/src/glum/_glm.py @@ -718,6 +718,8 @@ def __init__( b_ineq: Optional[np.ndarray] = None, force_all_finite: bool = True, drop_first: bool = False, + robust: bool = True, + expected_information: bool = False, ): self.l1_ratio = l1_ratio self.P1 = P1 @@ -748,6 +750,8 @@ def __init__( self.b_ineq = b_ineq self.force_all_finite = force_all_finite self.drop_first = drop_first + self.robust = robust + self.expected_information = expected_information @property def family_instance(self) -> ExponentialDispersionModel: @@ -1326,15 +1330,16 @@ def predict( def std_errors( self, - X, - y, + X=None, + y=None, mu=None, offset=None, sample_weight=None, dispersion=None, - robust=True, + robust=None, clusters: np.ndarray = None, - expected_information=False, + expected_information=None, + store_covariance_matrix=False, ): """Calculate standard errors for generalized linear models. @@ -1355,14 +1360,19 @@ def std_errors( Individual weights for each sample. dispersion : float, optional, default=None The dispersion parameter. Estimated if absent. - robust : boolean, optional, default=True + robust : boolean, optional, default=None Whether to compute robust standard errors instead of normal ones. + If not specified, the model's ``robust`` attribute is used. clusters : array-like, optional, default=None Array with clusters membership. Clustered standard errors are computed if clusters is not None. - expected_information : boolean, optional, default=False + expected_information : boolean, optional, default=None Whether to use the expected or observed information matrix. Only relevant when computing robust std-errors. + If not specified, the model's ``expected_information`` attribute is used. + store_covariance_matrix : boolean, optional, default=False + Whether to store the covariance matrix in the model instance. + If a covariance matrix has already been stored, it will be overwritten. """ return np.sqrt( self.covariance_matrix( @@ -1375,29 +1385,34 @@ def std_errors( robust=robust, clusters=clusters, expected_information=expected_information, + store_covariance_matrix=store_covariance_matrix, ).diagonal() ) def covariance_matrix( self, - X, - y, + X=None, + y=None, mu=None, offset=None, sample_weight=None, dispersion=None, - robust=True, - clusters: np.ndarray = None, - expected_information=False, + robust=None, + clusters: Optional[np.ndarray] = None, + expected_information=None, + store_covariance_matrix=False, + skip_checks=False, ): """Calculate the covariance matrix for generalized linear models. Parameters ---------- - X : {array-like, sparse matrix}, shape (n_samples, n_features) - Training data. - y : array-like, shape (n_samples,) - Target values. + X : {array-like, sparse matrix}, shape (n_samples, n_features), optional + Training data. Can be omitted if a covariance matrix has already + been computed. + y : array-like, shape (n_samples,), optional + Target values. Can be omitted if a covariance matrix has already + been computed. mu : array-like, optional, default=None Array with predictions. Estimated if absent. offset : array-like, optional, default=None @@ -1406,14 +1421,21 @@ def covariance_matrix( Individual weights for each sample. dispersion : float, optional, default=None The dispersion parameter. Estimated if absent. - robust : boolean, optional, default=True + robust : boolean, optional, default=None Whether to compute robust standard errors instead of normal ones. + If not specified, the model's ``robust`` attribute is used. clusters : array-like, optional, default=None Array with clusters membership. Clustered standard errors are computed if clusters is not None. - expected_information : boolean, optional, default=False + expected_information : boolean, optional, default=None Whether to use the expected or observed information matrix. Only relevant when computing robust standard errors. + If not specified, the model's ``expected_information`` attribute is used. + store_covariance_matrix : boolean, optional, default=False + Whether to store the covariance matrix in the model instance. + If a covariance matrix has already been stored, it will be overwritten. + skip_checks : boolean, optional, default=False + Whether to skip input validation. For internal use only. Notes ----- @@ -1456,29 +1478,95 @@ def covariance_matrix( Cambridge university press """ + self.covariance_matrix_: Union[np.ndarray, None] - if isinstance(X, pd.DataFrame) and hasattr(self, "feature_dtypes_"): - X = _align_df_categories(X, self.feature_dtypes_) + if robust is None: + _robust = self.robust + else: + _robust = robust - X, y = check_X_y_tabmat_compliant( - X, - y, - accept_sparse=["csr", "csc", "coo"], - dtype="numeric", - copy=self._should_copy_X(), - ensure_2d=True, - allow_nd=False, - drop_first=self.drop_first, - ) + if expected_information is None: + _expected_information = self.expected_information + else: + _expected_information = expected_information - if isinstance(X, np.ndarray): - X = tm.DenseMatrix(X) - if sparse.issparse(X) and not isinstance(X, tm.SparseMatrix): - X = tm.SparseMatrix(X) + if ( + (hasattr(self, "alpha") and self.alpha is None) + or ( + hasattr(self, "alpha") + and isinstance(self.alpha, (int, float)) + and self.alpha > 0 + ) + or (hasattr(self, "alpha_") and self.alpha_ > 0) # glm_cv + or (hasattr(self, "_alphas") and self._alphas[-1] > 0) # alpha_search + ): + warnings.warn( + "Covariance matrix estimation assumes that the model is not " + "penalized. You are estimating a penalized model. The covariance " + "matrix will be incorrect." + ) + + if not skip_checks: + if (X is None or y is None) and self.covariance_matrix_ is None: + raise ValueError( + "Either X and y must be provided or the covariance matrix " + "must have been previously computed." + ) + + if (X is None or y is None) and store_covariance_matrix: + raise ValueError( + "X and y must be provided if 'store_covariance_matrix' is True." + ) + + if store_covariance_matrix and self.covariance_matrix_ is not None: + warnings.warn( + "A covariance matrix has already been computed. " + "It will be overwritten." + ) + + if X is None and y is None: + if ( + offset is not None + or mu is not None + or offset is not None + or sample_weight is not None + or dispersion is not None + or robust is not None + or clusters is not None + or expected_information is not None + ): + raise ValueError( + "Cannot reestimate the covariance matrix with different " + "parameters if X and y are not provided." + ) + return self.covariance_matrix_ + + if isinstance(X, pd.DataFrame) and hasattr(self, "feature_dtypes_"): + X = _align_df_categories(X, self.feature_dtypes_) + + X, y = check_X_y_tabmat_compliant( + X, + y, + accept_sparse=["csr", "csc", "coo"], + dtype="numeric", + copy=self._should_copy_X(), + ensure_2d=True, + allow_nd=False, + drop_first=self.drop_first, + ) + + if isinstance(X, np.ndarray): + X = tm.DenseMatrix(X) + if sparse.issparse(X) and not isinstance(X, tm.SparseMatrix): + X = tm.SparseMatrix(X) + + sample_weight = _check_weights( + sample_weight, + y.shape[0], + X.dtype, + force_all_finite=self.force_all_finite, + ) - sample_weight = _check_weights( - sample_weight, y.shape[0], X.dtype, force_all_finite=self.force_all_finite - ) sum_weights = np.sum(sample_weight) offset = _check_offset(offset, y.shape[0], X.dtype) @@ -1503,8 +1591,8 @@ def covariance_matrix( "Matrix is singular. Cannot estimate standard errors." ) - if robust or clusters is not None: - if expected_information: + if _robust or clusters is not None: + if _expected_information: oim_fct = self._family_instance._fisher_information else: oim_fct = self._family_instance._observed_information @@ -1559,6 +1647,9 @@ def covariance_matrix( sum_weights - self.n_features_in_ - int(self.fit_intercept) ) + if store_covariance_matrix: + self.covariance_matrix_ = vcov + return vcov # Note: check_estimator(GeneralizedLinearRegressor) might raise @@ -2156,6 +2247,13 @@ class GeneralizedLinearRegressor(GeneralizedLinearRegressorBase): Set this to True when alpha=0 and solver='auto' to prevent an error due to a singular feature matrix. + robust : bool, optional (default = False) + If true, then robust standard errors are computed by default. + + expected_information : bool, optional (default = False) + If true, then the expected information matrix is computed by default. + Only relevant when computing robust standard errors. + Attributes ---------- coef_ : numpy.array, shape (n_features,) @@ -2237,6 +2335,8 @@ def __init__( b_ineq: Optional[np.ndarray] = None, force_all_finite: bool = True, drop_first: bool = False, + robust: bool = True, + expected_information: bool = False, ): self.alphas = alphas self.alpha = alpha @@ -2270,6 +2370,8 @@ def __init__( b_ineq=b_ineq, force_all_finite=force_all_finite, drop_first=drop_first, + robust=robust, + expected_information=expected_information, ) def _validate_hyperparameters(self) -> None: @@ -2316,6 +2418,8 @@ def fit( y: ArrayLike, sample_weight: Optional[ArrayLike] = None, offset: Optional[ArrayLike] = None, + store_covariance_matrix: bool = False, + clusters: Optional[np.ndarray] = None, # TODO: take out weights_sum (or use it properly) weights_sum: Optional[float] = None, ): @@ -2351,6 +2455,15 @@ def fit( ``y`` by 3 if the link is linear and will multiply expected ``y`` by 3 if the link is logarithmic. + store_covariance_matrix : bool, optional (default=False) + Whether to estimate and store the covariance matrix of the parameter + estimates. If ``True``, the covariance matrix will be available in the + ``covariance_matrix_`` attribute after fitting. + + clusters : array-like, optional, default=None + Array with clusters membership. Clustered standard errors are + computed if clusters is not None. + weights_sum: float, optional (default=None) Returns @@ -2546,6 +2659,20 @@ def fit( self._tear_down_from_fit() + self.covariance_matrix_ = None + if store_covariance_matrix: + self.covariance_matrix( + X=X.unstandardize(), + y=y, + offset=offset, + sample_weight=sample_weight * weights_sum, + robust=self.robust, + clusters=clusters, + expected_information=self.expected_information, + store_covariance_matrix=True, + skip_checks=True, + ) + return self def _compute_information_criteria( diff --git a/src/glum/_glm_cv.py b/src/glum/_glm_cv.py index 54979a34..cd27f1c4 100644 --- a/src/glum/_glm_cv.py +++ b/src/glum/_glm_cv.py @@ -273,6 +273,13 @@ class GeneralizedLinearRegressorCV(GeneralizedLinearRegressorBase): deviance_path_: array, shape(n_folds, n_alphas) Deviance for the test set on each fold, varying alpha. + + robust : bool, optional (default = False) + If true, then robust standard errors are computed by default. + + expected_information : bool, optional (default = False) + If true, then the expected information matrix is computed by default. + Only relevant when computing robust standard errors. """ def __init__( @@ -308,6 +315,8 @@ def __init__( cv=None, n_jobs: Optional[int] = None, drop_first: bool = False, + robust: bool = True, + expected_information: bool = False, ): self.alphas = alphas self.cv = cv @@ -341,6 +350,8 @@ def __init__( b_ineq=b_ineq, force_all_finite=force_all_finite, drop_first=drop_first, + robust=robust, + expected_information=expected_information, ) def _validate_hyperparameters(self) -> None: @@ -365,6 +376,8 @@ def fit( y: ArrayLike, sample_weight: Optional[ArrayLike] = None, offset: Optional[ArrayLike] = None, + store_covariance_matrix: bool = False, + clusters: Optional[np.ndarray] = None, ): r""" Choose the best model along a 'regularization path' by cross-validation. @@ -398,6 +411,15 @@ def fit( Added to linear predictor. An offset of 3 will increase expected ``y`` by 3 if the link is linear and will multiply expected ``y`` by 3 if the link is logarithmic. + + store_covariance_matrix : bool, optional (default=False) + Whether to store the covariance matrix of the parameter estimates + corresponding to the best best model. + + clusters : array-like, optional, default=None + Array with clusters membership. Clustered standard errors are + computed if clusters is not None. + """ self._validate_hyperparameters() @@ -694,4 +716,18 @@ def _get_deviance(coef): self._tear_down_from_fit() + self.covariance_matrix_ = None + if store_covariance_matrix: + self.covariance_matrix( + X=X.unstandardize(), + y=y, + offset=offset, + sample_weight=sample_weight * weights_sum, + robust=self.robust, + clusters=clusters, + expected_information=self.expected_information, + store_covariance_matrix=True, + skip_checks=True, + ) + return self diff --git a/tests/glm/test_glm.py b/tests/glm/test_glm.py index fd36dd6b..3c1e1b71 100644 --- a/tests/glm/test_glm.py +++ b/tests/glm/test_glm.py @@ -2113,3 +2113,134 @@ def test_P1_P2_with_drop_first(): regressor.fit(X, y) regressor = GeneralizedLinearRegressor(alpha=0.1, l1_ratio=0.5, P1=P_1, P2=P_2) regressor.fit(X, y) + + +@pytest.mark.parametrize("clustered", [True, False], ids=["clustered", "nonclustered"]) +@pytest.mark.parametrize("expected_information", [True, False], ids=["opg", "oim"]) +@pytest.mark.parametrize("robust", [True, False], ids=["robust", "nonrobust"]) +def test_store_covariance_matrix( + regression_data, robust, expected_information, clustered +): + X, y = regression_data + + if clustered: + rng = np.random.default_rng(42) + clu = rng.integers(5, size=len(y)) + else: + clu = None + + regressor = GeneralizedLinearRegressor( + family="gaussian", + alpha=0, + robust=robust, + expected_information=expected_information, + ) + regressor.fit(X, y, store_covariance_matrix=True, clusters=clu) + + np.testing.assert_array_almost_equal( + regressor.covariance_matrix( + X, y, robust=robust, expected_information=expected_information, clusters=clu + ), + regressor.covariance_matrix(), + ) + + np.testing.assert_array_almost_equal( + regressor.std_errors( + X, y, robust=robust, expected_information=expected_information, clusters=clu + ), + regressor.std_errors(), + ) + + +def test_store_covariance_matrix_errors(regression_data): + X, y = regression_data + + regressor = GeneralizedLinearRegressor(family="gaussian", alpha=0) + regressor.fit(X, y, store_covariance_matrix=False) + + with pytest.raises(ValueError, match="Either X and y must be provided"): + regressor.covariance_matrix() + + with pytest.raises(ValueError, match="Either X and y must be provided"): + regressor.covariance_matrix(X=X) + + with pytest.raises(ValueError, match="Either X and y must be provided"): + regressor.covariance_matrix(y=y) + + regressor.covariance_matrix(X, y, store_covariance_matrix=True) + + with pytest.raises( + ValueError, match="Cannot reestimate the covariance matrix with different" + ): + regressor.covariance_matrix(robust=False) + + with pytest.warns(match="A covariance matrix has already been computed."): + regressor.covariance_matrix(X, y, store_covariance_matrix=True) + + regressor_penalized = GeneralizedLinearRegressor(family="gaussian", alpha=0.1) + with pytest.warns(match="Covariance matrix estimation assumes"): + regressor_penalized.fit(X, y, store_covariance_matrix=True) + + +@pytest.mark.parametrize("clustered", [True, False], ids=["clustered", "nonclustered"]) +@pytest.mark.parametrize("expected_information", [True, False], ids=["opg", "oim"]) +@pytest.mark.parametrize("robust", [True, False], ids=["robust", "nonrobust"]) +def test_store_covariance_matrix_alpha_search( + regression_data, robust, expected_information, clustered +): + X, y = regression_data + + if clustered: + rng = np.random.default_rng(42) + clu = rng.integers(5, size=len(y)) + else: + clu = None + + regressor = GeneralizedLinearRegressor( + family="gaussian", + alpha=[0, 0.1, 0.5], + alpha_search=True, + robust=robust, + expected_information=expected_information, + ) + with pytest.warns(match="Covariance matrix estimation assumes"): + regressor.fit(X, y, store_covariance_matrix=True, clusters=clu) + + np.testing.assert_array_almost_equal( + regressor.covariance_matrix( + X, y, robust=robust, expected_information=expected_information, clusters=clu + ), + regressor.covariance_matrix(), + ) + + +@pytest.mark.parametrize("clustered", [True, False], ids=["clustered", "nonclustered"]) +@pytest.mark.parametrize("expected_information", [True, False], ids=["opg", "oim"]) +@pytest.mark.parametrize("robust", [True, False], ids=["robust", "nonrobust"]) +def test_store_covariance_matrix_cv( + regression_data, robust, expected_information, clustered +): + X, y = regression_data + + if clustered: + rng = np.random.default_rng(42) + clu = rng.integers(5, size=len(y)) + else: + clu = None + + regressor = GeneralizedLinearRegressorCV( + family="gaussian", + n_alphas=5, + robust=robust, + expected_information=expected_information, + ) + with pytest.warns(match="Covariance matrix estimation assumes"): + # regressor.alpha_ == 1e-5 > 0 + regressor.fit(X, y, store_covariance_matrix=True, clusters=clu) + + np.testing.assert_array_almost_equal( + regressor.covariance_matrix( + X, y, robust=robust, expected_information=expected_information, clusters=clu + ), + regressor.covariance_matrix(), + ) From 0ee967ac83dce67eaf2e964ec2fbea333de2d0c9 Mon Sep 17 00:00:00 2001 From: "quant-ranger[bot]" <132915763+quant-ranger[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 06:20:31 +0200 Subject: [PATCH 5/5] Pre-commit autoupdate (#676) Co-authored-by: quant-ranger[bot] <132915763+quant-ranger[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4f1d68ec..e3748d32 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: - id: isort-conda additional_dependencies: [toml] - repo: https://github.com/Quantco/pre-commit-mirrors-mypy - rev: "1.4.1" + rev: "1.5.0" hooks: - id: mypy-conda args: @@ -37,7 +37,7 @@ repos: exclude: ^tests/ additional_dependencies: [-c, conda-forge, types-setuptools=67.5, attrs] - repo: https://github.com/Quantco/pre-commit-mirrors-pyupgrade - rev: 3.9.0 + rev: 3.10.1 hooks: - id: pyupgrade-conda exclude: ^src/glum_benchmarks/orig_sklearn_fork/