From 33f8f85e13f27472136e939dd7a11b8390c18406 Mon Sep 17 00:00:00 2001 From: braniii Date: Tue, 8 Oct 2024 07:55:39 +0200 Subject: [PATCH] Pep8ify code. --- src/normi/__init__.py | 1 + src/normi/__main__.py | 9 +- src/normi/_estimators.py | 30 ++++-- src/normi/_typing.py | 18 ++-- src/normi/_utils.py | 4 +- tests/test__estimators.py | 207 +++++++++++++++++++++----------------- tests/test__utils.py | 1 + 7 files changed, 159 insertions(+), 111 deletions(-) diff --git a/src/normi/__init__.py b/src/normi/__init__.py index 2dc47a4..b71bda0 100644 --- a/src/normi/__init__.py +++ b/src/normi/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- """Normalized mutual information""" + __all__ = ['NormalizedMI'] NORMS = {'joint', 'geometric', 'arithmetic', 'min', 'max'} diff --git a/src/normi/__main__.py b/src/normi/__main__.py index 84bfb41..ef03a8f 100644 --- a/src/normi/__main__.py +++ b/src/normi/__main__.py @@ -5,6 +5,7 @@ All rights reserved. """ + import click import numpy as np @@ -96,7 +97,13 @@ help='Activate verbose mode.', ) def main( - input_file, output_basename, norm, inv_measure, n_dims, precision, verbose, + input_file, + output_basename, + norm, + inv_measure, + n_dims, + precision, + verbose, ): # load file if verbose: diff --git a/src/normi/_estimators.py b/src/normi/_estimators.py index b05a027..de34d06 100644 --- a/src/normi/_estimators.py +++ b/src/normi/_estimators.py @@ -6,6 +6,7 @@ All rights reserved. """ + __all__ = ['NormalizedMI'] # noqa: WPS410 import numpy as np @@ -32,7 +33,7 @@ PositiveFloat, PositiveInt, PositiveMatrix, - ArrayLikePositiveInt + ArrayLikePositiveInt, ) @@ -221,7 +222,8 @@ def _reset(self) -> None: @beartype def nmi( - self, normalize_method: Optional[NormString] = None, + self, + normalize_method: Optional[NormString] = None, ) -> NormalizedMatrix: """Return the normalized mutual information matrix. @@ -265,11 +267,13 @@ def nmi( @beartype def _kraskov_estimator( - self, X: List[Float2DArray], + self, + X: List[Float2DArray], ) -> Tuple[PositiveMatrix, FloatMatrix, FloatMatrix, FloatMatrix]: """Estimate the mutual information and entropies matrices.""" mi: PositiveMatrix = np.empty( # noqa: WPS317 - (self._n_features, self._n_features), dtype=self._dtype, + (self._n_features, self._n_features), + dtype=self._dtype, ) hxy: FloatMatrix = np.empty_like(mi) hx: FloatMatrix = np.empty_like(mi) @@ -286,7 +290,7 @@ def _kraskov_estimator( hxy[idx_i, idx_i] = 1 hx[idx_i, idx_i] = 1 hy[idx_i, idx_i] = 1 - for idx_j, xj in enumerate(X[idx_i + 1:], idx_i + 1): + for idx_j, xj in enumerate(X[idx_i + 1 :], idx_i + 1): mi_ij, hxy_ij, hx_ij, hy_ij = kraskov_estimator( xi, xj, @@ -337,9 +341,7 @@ def _scale_nearest_neighbor_distance( if invariant_measure == 'radius': return radii / np.mean(radii) elif invariant_measure == 'volume': - return radii / ( - np.mean(radii ** n_dims) ** (1 / n_dims) - ) + return radii / (np.mean(radii**n_dims) ** (1 / n_dims)) elif invariant_measure == 'kraskov': return radii # This should never be reached @@ -399,7 +401,9 @@ def kraskov_estimator( # Here we rely on NearestNeighbors to select the fastest algorithm. tree = KDTree(xy) radii: FloatArray = tree.query( - xy, k=n_neighbors + 1, **kdtree_kwargs, + xy, + k=n_neighbors + 1, + **kdtree_kwargs, )[0][:, 1:] # neglect self count # take next smaller radii radii: FloatArray = np.nextafter(radii[:, -1], 0) @@ -415,8 +419,12 @@ def kraskov_estimator( ny: FloatArray nx, ny = [ KDTree(z).query_ball_point( - z, r=radii, return_length=True, **kdtree_kwargs, - ) - 1 # fix self count + z, + r=radii, + return_length=True, + **kdtree_kwargs, + ) + - 1 # fix self count for z in (x, y) ] diff --git a/src/normi/_typing.py b/src/normi/_typing.py index e3c8776..747b639 100644 --- a/src/normi/_typing.py +++ b/src/normi/_typing.py @@ -9,9 +9,11 @@ All rights reserved. """ + import numpy as np from beartype.typing import List, Union from beartype.vale import Is, IsAttr, IsEqual + from normi import INVMEASURES, NORMS try: # for python <= 3.8 use typing_extensions @@ -29,23 +31,27 @@ def _get_resolution(x): def _allclose(x, y) -> bool: """Wrapper around np.allclose with dtype dependent atol.""" - atol = np.max([ - _get_resolution(x), - _get_resolution(y), - # default value of numpy - 1e-8, - ]) + atol = np.max( + [ + _get_resolution(x), + _get_resolution(y), + # default value of numpy + 1e-8, + ] + ) return np.allclose(x, y, atol=atol) class NDim: """Class for creating Validators checking for desired dimensions.""" + def __class_getitem__(self, ndim): return IsAttr['ndim', IsEqual[ndim]] class DType: """Class for creating Validators checking for desired dtype.""" + def __class_getitem__(self, dtype): return Is[lambda arr: np.issubdtype(arr.dtype, dtype)] diff --git a/src/normi/_utils.py b/src/normi/_utils.py index 2bcacd1..847d345 100644 --- a/src/normi/_utils.py +++ b/src/normi/_utils.py @@ -6,6 +6,7 @@ All rights reserved. """ + __all__ = ['savetxt'] # noqa: WPS410 import datetime @@ -37,8 +38,7 @@ def _get_rui() -> str: } return ( - 'This file was generated by nmi:\n{args}' + - '\n\n{date}, {user}@{pc}' + 'This file was generated by nmi:\n{args}\n\n{date}, {user}@{pc}' ).format(**rui) diff --git a/tests/test__estimators.py b/tests/test__estimators.py index 46333d9..0c029c3 100644 --- a/tests/test__estimators.py +++ b/tests/test__estimators.py @@ -6,6 +6,7 @@ All rights reserved. """ + import numpy as np import pytest from beartype.roar import BeartypeException @@ -42,50 +43,64 @@ def X1_result(method, measure): }[measure][method] -@pytest.mark.parametrize('invariant_measure, n_dims, radii, result, error', [ - ('radius', 1, np.arange(5), np.arange(5) / 2, None), - ('radius', 2, np.arange(5), np.arange(5) / 2, None), - ('volume', 1, np.arange(5), np.arange(5) / 2, None), - ( - 'volume', - 2, - np.arange(5), - [0, 0.40824829, 0.81649658, 1.22474487, 1.63299316], - None, - ), - ('kraskov', 1, np.arange(5), np.arange(5), None), - ('none', 1, np.arange(5), np.arange(5), BeartypeException), -]) +@pytest.mark.parametrize( + 'invariant_measure, n_dims, radii, result, error', + [ + ('radius', 1, np.arange(5), np.arange(5) / 2, None), + ('radius', 2, np.arange(5), np.arange(5) / 2, None), + ('volume', 1, np.arange(5), np.arange(5) / 2, None), + ( + 'volume', + 2, + np.arange(5), + [0, 0.40824829, 0.81649658, 1.22474487, 1.63299316], + None, + ), + ('kraskov', 1, np.arange(5), np.arange(5), None), + ('none', 1, np.arange(5), np.arange(5), BeartypeException), + ], +) def test__scale_nearest_neighbor_distance( - invariant_measure, n_dims, radii, result, error, + invariant_measure, + n_dims, + radii, + result, + error, ): # cast radii to float to fulfill beartype typing req. radii = radii.astype(float) if error is None: scaled_radii = estimators._scale_nearest_neighbor_distance( - invariant_measure, n_dims, radii, + invariant_measure, + n_dims, + radii, ) assert_array_almost_equal(scaled_radii, result) else: with pytest.raises(error): estimators._scale_nearest_neighbor_distance( - invariant_measure, n_dims, radii, + invariant_measure, + n_dims, + radii, ) -@pytest.mark.parametrize('X, n_dims, error', [ - (np.random.uniform(size=(10, 9)), 1, None), - (np.random.uniform(size=(10, 9)), 3, None), - (np.random.uniform(size=(10, 9)), np.array([3, 3, 3]), None), - (np.random.uniform(size=(10, 9)), np.array([4, 3, 2]), None), - (np.random.uniform(size=(10, 9)), np.array([3, 2, 3]), ValueError), - (np.random.uniform(size=(10, 9)), np.array([9]), ValueError), - (np.random.uniform(size=(10, 9)), 5, ValueError), - (np.random.uniform(size=(10, 9)), 2, ValueError), - (np.zeros((10, 9)).astype(float), 1, ValueError), - (np.vander((1, 2, 3, 4), 3).astype(float), 1, ValueError), -]) +@pytest.mark.parametrize( + 'X, n_dims, error', + [ + (np.random.uniform(size=(10, 9)), 1, None), + (np.random.uniform(size=(10, 9)), 3, None), + (np.random.uniform(size=(10, 9)), np.array([3, 3, 3]), None), + (np.random.uniform(size=(10, 9)), np.array([4, 3, 2]), None), + (np.random.uniform(size=(10, 9)), np.array([3, 2, 3]), ValueError), + (np.random.uniform(size=(10, 9)), np.array([9]), ValueError), + (np.random.uniform(size=(10, 9)), 5, ValueError), + (np.random.uniform(size=(10, 9)), 2, ValueError), + (np.zeros((10, 9)).astype(float), 1, ValueError), + (np.vander((1, 2, 3, 4), 3).astype(float), 1, ValueError), + ], +) def test__check_X(X, n_dims, error): if error is None: estimators._check_X(X, n_dims) @@ -94,9 +109,12 @@ def test__check_X(X, n_dims, error): estimators._check_X(X, n_dims) -@pytest.mark.parametrize('normalize_method, X, kwargs', [ - ('joint', X1(), {}), -]) +@pytest.mark.parametrize( + 'normalize_method, X, kwargs', + [ + ('joint', X1(), {}), + ], +) def test__reset(normalize_method, X, kwargs): nmi = NormalizedMI(normalize_method=normalize_method, **kwargs) nmi.fit(X) @@ -105,65 +123,72 @@ def test__reset(normalize_method, X, kwargs): assert not hasattr(nmi, 'nmi_') -@pytest.mark.parametrize('X, kwargs, result, error', [ - (X1(), {}, X1_result('geometric', 'volume'), None), - ( - X1(), - {'normalize_method': 'joint', 'invariant_measure': 'radius'}, - X1_result('joint', 'radius'), - None, - ), - (X1(), {'normalize_method': 'max'}, X1_result('max', 'volume'), None), - (X1(), {'normalize_method': 'min'}, X1_result('min', 'volume'), None), - ( - X1(), - {'normalize_method': 'arithmetic'}, - X1_result('arithmetic', 'volume'), - None, - ), - ( - X1(), - {'normalize_method': 'geometric'}, - X1_result('geometric', 'volume'), - None, - ), - ( - X1(), - {'normalize_method': 'joint', 'invariant_measure': 'volume'}, - X1_result('joint', 'volume'), - None, - ), - ( - X1(), - {'normalize_method': 'max', 'invariant_measure': 'volume'}, - X1_result('max', 'volume'), - None, - ), - ( - X1(), - {'normalize_method': 'min', 'invariant_measure': 'volume'}, - X1_result('min', 'volume'), - None, - ), - ( - X1(), - {'normalize_method': 'arithmetic', 'invariant_measure': 'volume'}, - X1_result('arithmetic', 'volume'), - None, - ), - ( - X1(), - {'normalize_method': 'geometric', 'invariant_measure': 'volume'}, - X1_result('geometric', 'volume'), - None, - ), - ( - X1(), - {'n_dims': np.array([1, 1]), 'normalize_method': 'geometric', 'invariant_measure': 'volume'}, - X1_result('geometric', 'volume'), - None, - ), -]) +@pytest.mark.parametrize( + 'X, kwargs, result, error', + [ + (X1(), {}, X1_result('geometric', 'volume'), None), + ( + X1(), + {'normalize_method': 'joint', 'invariant_measure': 'radius'}, + X1_result('joint', 'radius'), + None, + ), + (X1(), {'normalize_method': 'max'}, X1_result('max', 'volume'), None), + (X1(), {'normalize_method': 'min'}, X1_result('min', 'volume'), None), + ( + X1(), + {'normalize_method': 'arithmetic'}, + X1_result('arithmetic', 'volume'), + None, + ), + ( + X1(), + {'normalize_method': 'geometric'}, + X1_result('geometric', 'volume'), + None, + ), + ( + X1(), + {'normalize_method': 'joint', 'invariant_measure': 'volume'}, + X1_result('joint', 'volume'), + None, + ), + ( + X1(), + {'normalize_method': 'max', 'invariant_measure': 'volume'}, + X1_result('max', 'volume'), + None, + ), + ( + X1(), + {'normalize_method': 'min', 'invariant_measure': 'volume'}, + X1_result('min', 'volume'), + None, + ), + ( + X1(), + {'normalize_method': 'arithmetic', 'invariant_measure': 'volume'}, + X1_result('arithmetic', 'volume'), + None, + ), + ( + X1(), + {'normalize_method': 'geometric', 'invariant_measure': 'volume'}, + X1_result('geometric', 'volume'), + None, + ), + ( + X1(), + { + 'n_dims': np.array([1, 1]), + 'normalize_method': 'geometric', + 'invariant_measure': 'volume', + }, + X1_result('geometric', 'volume'), + None, + ), + ], +) def test_NormalizedMI(X, kwargs, result, error): # cast radii to float to fulfill beartype typing req. nmi = NormalizedMI(**kwargs) diff --git a/tests/test__utils.py b/tests/test__utils.py index 64911cc..5115f7f 100644 --- a/tests/test__utils.py +++ b/tests/test__utils.py @@ -6,6 +6,7 @@ All rights reserved. """ + import os.path import numpy as np