diff --git a/examples/asset-pricing_examples.ipynb b/examples/asset-pricing_examples.ipynb index 5ee8bce920..d24f82ac8c 100644 --- a/examples/asset-pricing_examples.ipynb +++ b/examples/asset-pricing_examples.ipynb @@ -412,7 +412,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/examples/asset-pricing_formulas.ipynb b/examples/asset-pricing_formulas.ipynb index e3675e62e5..6d8d5e34d8 100644 --- a/examples/asset-pricing_formulas.ipynb +++ b/examples/asset-pricing_formulas.ipynb @@ -139,7 +139,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/examples/iv_absorbing-regression.ipynb b/examples/iv_absorbing-regression.ipynb index 9a6c0bcb8c..409746cdb3 100644 --- a/examples/iv_absorbing-regression.ipynb +++ b/examples/iv_absorbing-regression.ipynb @@ -151,7 +151,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/examples/iv_advanced-examples.ipynb b/examples/iv_advanced-examples.ipynb index 87e04ca688..e297d0282f 100644 --- a/examples/iv_advanced-examples.ipynb +++ b/examples/iv_advanced-examples.ipynb @@ -570,7 +570,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "nbsphinx": { "allow_errors": true diff --git a/examples/iv_basic-examples.ipynb b/examples/iv_basic-examples.ipynb index ac308fafc3..e11a65108c 100644 --- a/examples/iv_basic-examples.ipynb +++ b/examples/iv_basic-examples.ipynb @@ -805,7 +805,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/examples/iv_using-formulas.ipynb b/examples/iv_using-formulas.ipynb index eb061eabf1..c73d429c92 100644 --- a/examples/iv_using-formulas.ipynb +++ b/examples/iv_using-formulas.ipynb @@ -191,7 +191,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/examples/panel_data-formats.ipynb b/examples/panel_data-formats.ipynb index b5f354d5b0..d95d60314f 100644 --- a/examples/panel_data-formats.ipynb +++ b/examples/panel_data-formats.ipynb @@ -230,7 +230,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/examples/panel_examples.ipynb b/examples/panel_examples.ipynb index 60d4a6a5c0..3f3f398dae 100644 --- a/examples/panel_examples.ipynb +++ b/examples/panel_examples.ipynb @@ -429,7 +429,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/examples/panel_using-formulas.ipynb b/examples/panel_using-formulas.ipynb index 5a21b62be9..6e20bc0283 100644 --- a/examples/panel_using-formulas.ipynb +++ b/examples/panel_using-formulas.ipynb @@ -182,7 +182,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/examples/system_examples.ipynb b/examples/system_examples.ipynb index f389b98829..5748e85863 100644 --- a/examples/system_examples.ipynb +++ b/examples/system_examples.ipynb @@ -779,7 +779,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/examples/system_formulas.ipynb b/examples/system_formulas.ipynb index 509abd6a17..16f63e20e8 100644 --- a/examples/system_formulas.ipynb +++ b/examples/system_formulas.ipynb @@ -180,7 +180,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/examples/system_three-stage-ls.ipynb b/examples/system_three-stage-ls.ipynb index ca39dd26c1..0b9da5f41b 100644 --- a/examples/system_three-stage-ls.ipynb +++ b/examples/system_three-stage-ls.ipynb @@ -353,7 +353,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.9" }, "pycharm": { "stem_cell": { diff --git a/linearmodels/asset_pricing/covariance.py b/linearmodels/asset_pricing/covariance.py index c45fed3e1f..8a1a33933c 100644 --- a/linearmodels/asset_pricing/covariance.py +++ b/linearmodels/asset_pricing/covariance.py @@ -192,7 +192,7 @@ def cov(self) -> linearmodels.typing.data.Float64Array: out = ji @ s @ ji.T else: j = self.jacobian - out = inv(j.T @ inv(s) @ j) + out = inv(j.T @ inv(s) @ j).astype(float, copy=False) out = (scale / 2) * (out + out.T) return out @@ -371,4 +371,4 @@ def w( moments = moments - moments.mean(0)[None, :] out = self._kernel_cov(moments) - return inv(out) + return inv(out).astype(float, copy=False) diff --git a/linearmodels/asset_pricing/model.py b/linearmodels/asset_pricing/model.py index 968d835aa1..9bbe382a27 100644 --- a/linearmodels/asset_pricing/model.py +++ b/linearmodels/asset_pricing/model.py @@ -442,7 +442,7 @@ def __init__( self._sigma = np.asarray(sigma) vals, vecs = np.linalg.eigh(sigma) self._sigma_m12 = vecs @ np.diag(1.0 / np.sqrt(vals)) @ vecs.T - self._sigma_inv = np.linalg.inv(self._sigma) + self._sigma_inv = np.linalg.inv(self._sigma).astype(float, copy=False) def __str__(self) -> str: out = super().__str__() @@ -966,10 +966,10 @@ def fit( self.portfolios, self.factors, risk_free=self._risk_free ) res = mod.fit() - betas = np.asarray(res.betas).ravel() + betas_1d = np.asarray(res.betas).ravel() lam = np.asarray(res.risk_premia) mu = self.factors.ndarray.mean(0) - sv = np.r_[betas, lam, mu][:, None] + sv = np.r_[betas_1d, lam, mu][:, None] if starting is not None: starting = np.asarray(starting) if starting.ndim == 1: diff --git a/linearmodels/iv/__init__.py b/linearmodels/iv/__init__.py index 3a89bfc8eb..49b9b10924 100644 --- a/linearmodels/iv/__init__.py +++ b/linearmodels/iv/__init__.py @@ -1,6 +1,6 @@ -from .absorbing import AbsorbingLS, Interaction # flake8: noqa -from .model import IV2SLS, IVGMM, IVGMMCUE, IVLIML # flake8: noqa -from .results import compare # flake8: noqa +from .absorbing import AbsorbingLS, Interaction +from .model import IV2SLS, IVGMM, IVGMMCUE, IVLIML +from .results import compare __all__ = [ "IV2SLS", diff --git a/linearmodels/iv/absorbing.py b/linearmodels/iv/absorbing.py index 553d5808dd..4fb9c4f707 100644 --- a/linearmodels/iv/absorbing.py +++ b/linearmodels/iv/absorbing.py @@ -913,10 +913,10 @@ def _first_time_fit( self._regressors_hash = areg.hash self._constant_absorbed = self._has_constant_exog and areg_constant - dep = self._dependent.ndarray + dep = self._dependent.ndarray.astype(float, copy=False) exog = cast(linearmodels.typing.data.Float64Array, self._exog.ndarray) - root_w = sqrt(self._weight_data.ndarray) + root_w = sqrt(self._weight_data.ndarray.astype(float, copy=False)) dep = root_w * dep exog = root_w * exog denom = root_w.T @ root_w @@ -931,13 +931,13 @@ def _first_time_fit( absorb_options["drop_singletons"] = False algo = create(self._absorb_inter.cat, **absorb_options) - dep_exog = column_stack((dep, exog)) + dep_exog = column_stack((dep, exog)).astype(float, copy=False) resids = algo.residualize(dep_exog) dep_resid = resids[:, :1] exog_resid = resids[:, 1:] else: self._regressors = preconditioner(self._regressors)[0] - dep_exog = column_stack((dep, exog)) + dep_exog = column_stack((dep, exog)).astype(float, copy=False) resid = lsmr_annihilate( self._regressors, dep_exog, diff --git a/linearmodels/iv/covariance.py b/linearmodels/iv/covariance.py index 43dea961d4..96216c17a6 100644 --- a/linearmodels/iv/covariance.py +++ b/linearmodels/iv/covariance.py @@ -104,8 +104,8 @@ def kernel_weight_quadratic_spectral( w[0] = 0 return w - z = arange(n + 1) / float(bw) - w = 6 * pi * z / 5 + z = arange(n + 1).astype(float) / float(bw) + w = cast(linearmodels.typing.data.FloatArray1D, 6 * pi * z / 5) w[0] = 1 w[1:] = 3 / w[1:] ** 2 * (sin(w[1:]) / w[1:] - cos(w[1:])) diff --git a/linearmodels/iv/results.py b/linearmodels/iv/results.py index cb2d641aaf..777dcd2594 100644 --- a/linearmodels/iv/results.py +++ b/linearmodels/iv/results.py @@ -710,9 +710,9 @@ def diagnostics(self) -> DataFrame: endog, exog, instr, weights = self.endog, self.exog, self.instr, self.weights w = sqrt(weights.ndarray) - z = w * instr.ndarray + z = w * instr.ndarray.astype(float, copy=False) nz = z.shape[1] - x = w * exog.ndarray + x = w * exog.ndarray.astype(float, copy=False) ez = annihilate(z, x) individual_results = self.individual out_df = DataFrame( diff --git a/linearmodels/panel/data.py b/linearmodels/panel/data.py index 811963f9c5..60acef9d0c 100644 --- a/linearmodels/panel/data.py +++ b/linearmodels/panel/data.py @@ -195,9 +195,15 @@ def __init__( if x.ndim == 2: x = x.to_pandas() else: - items: list[Hashable] = np.asarray(x.coords[x.dims[0]]).tolist() - major: list[Hashable] = np.asarray(x.coords[x.dims[1]]).tolist() - minor: list[Hashable] = np.asarray(x.coords[x.dims[2]]).tolist() + items: list[Hashable] = cast( + list[Hashable], np.asarray(x.coords[x.dims[0]]).tolist() + ) + major: list[Hashable] = cast( + list[Hashable], np.asarray(x.coords[x.dims[1]]).tolist() + ) + minor: list[Hashable] = cast( + list[Hashable], np.asarray(x.coords[x.dims[2]]).tolist() + ) values = x.values x = panel_to_frame(values, items, major, minor, True) except ImportError: @@ -538,7 +544,7 @@ def demean( # noqa: E704 group: Literal["entity", "time", "both"], weights: PanelData | None, return_panel: Literal[False], - ) -> linearmodels.typing.data.Float64Array: ... # noqa: E704 + ) -> linearmodels.typing.data.Float64Array: ... def demean( self, diff --git a/linearmodels/panel/model.py b/linearmodels/panel/model.py index f1a60e5cc7..cf02fb5a07 100644 --- a/linearmodels/panel/model.py +++ b/linearmodels/panel/model.py @@ -54,6 +54,7 @@ from linearmodels.shared.linalg import has_constant from linearmodels.shared.typed_getters import get_panel_data_like from linearmodels.shared.utility import AttrDict, ensure_unique_column, panel_to_frame +from linearmodels.typing import BoolArray import linearmodels.typing.data CovarianceEstimator = Union[ @@ -319,7 +320,9 @@ def __init__( self._is_weighted = True self._name = self.__class__.__name__ self.weights = self._adapt_weights(weights) - self._not_null = np.ones(self.dependent.values2d.shape[0], dtype=bool) + self._not_null: BoolArray = np.ones( + self.dependent.values2d.shape[0], dtype=bool + ) self._cov_estimators = CovarianceManager( self.__class__.__name__, HomoskedasticCovariance, @@ -466,10 +469,11 @@ def _validate_data(self) -> None: ) all_missing = np.any(np.isnan(y), axis=1) & np.all(np.isnan(x), axis=1) - missing = ( + missing = np.asarray( np.any(np.isnan(y), axis=1) | np.any(np.isnan(x), axis=1) - | np.any(np.isnan(w), axis=1) + | np.any(np.isnan(w), axis=1), + dtype=bool, ) missing_warning(np.asarray(all_missing ^ missing), stacklevel=4) @@ -479,7 +483,7 @@ def _validate_data(self) -> None: self.weights.drop(missing) x = cast(linearmodels.typing.data.Float64Array, self.exog.values2d) - self._not_null = np.asarray(~missing) + self._not_null = cast(BoolArray, np.asarray(~missing)) w_df = self.weights.dataframe if np.any(np.asarray(w_df) <= 0): @@ -778,7 +782,9 @@ def _setup_clusters( if cluster_entity: group_ids_arr = self.dependent.entity_ids.squeeze() name = "cov.cluster.entity" - group_ids = Series(group_ids_arr, index=self.dependent.index, name=name) + group_ids: Series[int] = Series( + group_ids_arr, index=self.dependent.index, name=name + ) if clusters_frame is not None: clusters_frame[name] = group_ids else: @@ -2021,7 +2027,7 @@ def fit( linearmodels.typing.data.Float64Array, np.sqrt(self.weights.values2d) ) y_ex = root_w * self.dependent.values2d - mu_ex = 0 + mu_ex = np.array(0.0, dtype=float) if ( self.has_constant or self.entity_effects @@ -2469,7 +2475,9 @@ def _setup_clusters( if cluster_entity: group_ids = self.dependent.entity_ids.squeeze() name = "cov.cluster.entity" - group_ids_s = Series(group_ids, index=self.dependent.index, name=name) + group_ids_s: Series[int] = Series( + group_ids, index=self.dependent.index, name=name + ) if clusters_frame is not None: clusters_frame[name] = group_ids_s else: diff --git a/linearmodels/system/_utility.py b/linearmodels/system/_utility.py index f79be8cfa7..d69fed4463 100644 --- a/linearmodels/system/_utility.py +++ b/linearmodels/system/_utility.py @@ -308,7 +308,11 @@ def _compute_transform(self) -> None: t, left = vecs[:, : k - c], vecs[:, k - c :] q = self._qa[:, None] a = q.T @ inv(left.T @ r.T) @ left.T - self._t, self._l, self._a = t, left, a + self._t, self._l, self._a = ( + cast(linearmodels.typing.data.FloatArray2D, t), + cast(linearmodels.typing.data.FloatArray2D, left), + cast(linearmodels.typing.data.FloatArray2D, a), + ) self._computed = True @property diff --git a/linearmodels/system/covariance.py b/linearmodels/system/covariance.py index 8a52ff183f..d7cc31624d 100644 --- a/linearmodels/system/covariance.py +++ b/linearmodels/system/covariance.py @@ -129,7 +129,7 @@ def _mvreg_cov(self) -> linearmodels.typing.data.Float64Array: def _gls_cov(self) -> linearmodels.typing.data.Float64Array: x = self._x sigma = self._sigma - sigma_inv = inv(sigma) + sigma_inv = cast(linearmodels.typing.data.Float64Array, inv(sigma)) xpx = blocked_inner_prod(x, sigma_inv) # Handles case where sigma_inv is not inverse of full_sigma @@ -229,7 +229,7 @@ def __init__( nobs = eps.shape[0] if gls: - weights = inv(sigma) + weights = cast(linearmodels.typing.data.Float64Array, inv(sigma)) bigx = blocked_diag_product(x, weights) e = eps.T.ravel()[:, None] bigxe = bigx * e @@ -258,7 +258,9 @@ def _cov(self, gls: bool) -> linearmodels.typing.data.Float64Array: nobs = x[0].shape[0] k = len(x) sigma = self.sigma - weights = inv(sigma) if gls else eye(k) + weights = ( + cast(linearmodels.typing.data.Float64Array, inv(sigma)) if gls else eye(k) + ) xpx = blocked_inner_prod(x, weights) / nobs xeex = self._xeex() diff --git a/linearmodels/system/model.py b/linearmodels/system/model.py index 6c067b613e..dd07ead7d9 100644 --- a/linearmodels/system/model.py +++ b/linearmodels/system/model.py @@ -146,7 +146,7 @@ def _parameters_from_xprod( params_c = solve(xpx, xpy) params = cons.t @ params_c + cons.a.T else: - params = solve(xpx, xpy) + params = solve(xpx, xpy).astype(float, copy=False) return params @@ -760,7 +760,7 @@ def _gls_estimate( if not full_cov: sigma = np.diag(np.diag(sigma)) - sigma_inv = inv(sigma) + sigma_inv = cast(linearmodels.typing.data.Float64Array, inv(sigma)) k = len(wy) @@ -1045,7 +1045,7 @@ def _system_r2( est_sigma = sigma if not full_cov: est_sigma = np.diag(np.diag(est_sigma)) - est_sigma_inv = inv(est_sigma) + est_sigma_inv = cast(linearmodels.typing.data.Float64Array, inv(est_sigma)) nobs = wy[0].shape[0] k = len(wy) xpx = blocked_inner_prod(wi, est_sigma_inv) diff --git a/linearmodels/typing/__init__.py b/linearmodels/typing/__init__.py index acf705d6cc..36b03a81fc 100644 --- a/linearmodels/typing/__init__.py +++ b/linearmodels/typing/__init__.py @@ -9,6 +9,7 @@ ArrayLike as ArrayLike, BoolArray as BoolArray, Float64Array as Float64Array, + FloatArray2D as FloatArray2D, Int32Array as Int32Array, Int64Array as Int64Array, IntArray as IntArray, @@ -28,6 +29,7 @@ "Float64Array", "BoolArray", "NumericArray", + "FloatArray2D", ] ArraySequence = Sequence[np.ndarray] @@ -39,7 +41,7 @@ Label = Optional[Hashable] NumericArray = Union[ # pragma: no cover - np.ndarray[Any, np.dtype[np.signedinteger[Any]]], # pragma: no cover - np.ndarray[Any, np.dtype[np.unsignedinteger[Any]]], # pragma: no cover - np.ndarray[Any, np.dtype[np.floating[Any]]], # pragma: no cover + np.ndarray[tuple[int, ...], np.dtype[np.signedinteger[Any]]], # pragma: no cover + np.ndarray[tuple[int, ...], np.dtype[np.unsignedinteger[Any]]], # pragma: no cover + np.ndarray[tuple[int, ...], np.dtype[np.floating[Any]]], # pragma: no cover ] # pragma: no cover diff --git a/linearmodels/typing/data.py b/linearmodels/typing/data.py index ded9b02cd0..746a606f05 100644 --- a/linearmodels/typing/data.py +++ b/linearmodels/typing/data.py @@ -16,14 +16,15 @@ NDArray = Union[np.ndarray] -Float64Array = np.ndarray[Any, np.dtype[np.float64]] # pragma: no cover -Int64Array = np.ndarray[Any, np.dtype[np.int64]] # pragma: no cover -Int32Array = np.ndarray[Any, np.dtype[np.int32]] # pragma: no cover -IntArray = np.ndarray[Any, np.dtype[np.int_]] # pragma: no cover -BoolArray = np.ndarray[Any, np.dtype[np.bool_]] # pragma: no cover -AnyArray = np.ndarray[Any, Any] # pragma: no cover -Uint32Array = np.ndarray[Any, np.dtype[np.uint32]] # pragma: no cover - +Float64Array = np.ndarray[tuple[int, ...], np.dtype[np.float64]] # pragma: no cover +Int64Array = np.ndarray[tuple[int, ...], np.dtype[np.int64]] # pragma: no cover +Int32Array = np.ndarray[tuple[int, ...], np.dtype[np.int32]] # pragma: no cover +IntArray = np.ndarray[tuple[int, ...], np.dtype[np.int_]] # pragma: no cover +BoolArray = np.ndarray[tuple[int, ...], np.dtype[np.bool_]] # pragma: no cover +AnyArray = np.ndarray[tuple[int, ...], Any] # pragma: no cover +Uint32Array = np.ndarray[tuple[int, ...], np.dtype[np.uint32]] # pragma: no cover +FloatArray1D = np.ndarray[tuple[int], np.dtype[np.float64]] +FloatArray2D = np.ndarray[tuple[int, int], np.dtype[np.float64]] __all__ = [ "Float64Array", "Int32Array", @@ -33,4 +34,6 @@ "AnyArray", "Uint32Array", "ArrayLike", + "FloatArray1D", + "FloatArray2D", ] diff --git a/requirements-dev.txt b/requirements-dev.txt index b64564a15e..b82cf4ae11 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,6 @@ xarray>=0.16 mypy>=1.3 -black[jupyter]==24.10.0 +black[jupyter]~=25.1.0 pytest>=7.3.0,<8 isort>=5.12 ipython