copulas 0.11.1__tar.gz → 0.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of copulas might be problematic. Click here for more details.
- {copulas-0.11.1 → copulas-0.12.0}/PKG-INFO +1 -1
- copulas-0.12.0/copulas/__init__.py +91 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/bivariate/__init__.py +1 -1
- {copulas-0.11.1 → copulas-0.12.0}/copulas/bivariate/base.py +2 -1
- {copulas-0.11.1 → copulas-0.12.0}/copulas/bivariate/frank.py +1 -1
- {copulas-0.11.1 → copulas-0.12.0}/copulas/datasets.py +1 -1
- copulas-0.12.0/copulas/errors.py +5 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/multivariate/base.py +2 -1
- {copulas-0.11.1 → copulas-0.12.0}/copulas/multivariate/gaussian.py +72 -46
- {copulas-0.11.1 → copulas-0.12.0}/copulas/multivariate/tree.py +1 -1
- {copulas-0.11.1 → copulas-0.12.0}/copulas/multivariate/vine.py +6 -7
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/base.py +3 -3
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/gaussian_kde.py +1 -1
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/selection.py +1 -1
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/truncated_gaussian.py +1 -1
- copulas-0.11.1/copulas/__init__.py → copulas-0.12.0/copulas/utils.py +7 -96
- {copulas-0.11.1 → copulas-0.12.0}/copulas.egg-info/PKG-INFO +1 -1
- {copulas-0.11.1 → copulas-0.12.0}/copulas.egg-info/SOURCES.txt +2 -0
- {copulas-0.11.1 → copulas-0.12.0}/pyproject.toml +21 -7
- {copulas-0.11.1 → copulas-0.12.0}/LICENSE +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/README.md +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/bivariate/clayton.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/bivariate/gumbel.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/bivariate/independence.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/bivariate/utils.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/multivariate/__init__.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/optimize/__init__.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/__init__.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/beta.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/gamma.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/gaussian.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/log_laplace.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/student_t.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/univariate/uniform.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas/visualization.py +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas.egg-info/dependency_links.txt +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas.egg-info/requires.txt +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/copulas.egg-info/top_level.txt +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/setup.cfg +0 -0
- {copulas-0.11.1 → copulas-0.12.0}/tests/test_tasks.py +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Top-level package for Copulas."""
|
|
2
|
+
|
|
3
|
+
__author__ = 'DataCebo, Inc.'
|
|
4
|
+
__email__ = 'info@sdv.dev'
|
|
5
|
+
__version__ = '0.12.0'
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import warnings
|
|
9
|
+
from copy import deepcopy
|
|
10
|
+
from importlib.metadata import entry_points
|
|
11
|
+
from operator import attrgetter
|
|
12
|
+
from types import ModuleType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _get_addon_target(addon_path_name):
|
|
16
|
+
"""Find the target object for the add-on.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
addon_path_name (str):
|
|
20
|
+
The add-on's name. The add-on's name should be the full path of valid Python
|
|
21
|
+
identifiers (i.e. importable.module:object.attr).
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
tuple:
|
|
25
|
+
* object:
|
|
26
|
+
The base module or object the add-on should be added to.
|
|
27
|
+
* str:
|
|
28
|
+
The name the add-on should be added to under the module or object.
|
|
29
|
+
"""
|
|
30
|
+
module_path, _, object_path = addon_path_name.partition(':')
|
|
31
|
+
module_path = module_path.split('.')
|
|
32
|
+
|
|
33
|
+
if module_path[0] != __name__:
|
|
34
|
+
msg = f"expected base module to be '{__name__}', found '{module_path[0]}'"
|
|
35
|
+
raise AttributeError(msg)
|
|
36
|
+
|
|
37
|
+
target_base = sys.modules[__name__]
|
|
38
|
+
for submodule in module_path[1:-1]:
|
|
39
|
+
target_base = getattr(target_base, submodule)
|
|
40
|
+
|
|
41
|
+
addon_name = module_path[-1]
|
|
42
|
+
if object_path:
|
|
43
|
+
if len(module_path) > 1 and not hasattr(target_base, module_path[-1]):
|
|
44
|
+
msg = f"cannot add '{object_path}' to unknown submodule '{'.'.join(module_path)}'"
|
|
45
|
+
raise AttributeError(msg)
|
|
46
|
+
|
|
47
|
+
if len(module_path) > 1:
|
|
48
|
+
target_base = getattr(target_base, module_path[-1])
|
|
49
|
+
|
|
50
|
+
split_object = object_path.split('.')
|
|
51
|
+
addon_name = split_object[-1]
|
|
52
|
+
|
|
53
|
+
if len(split_object) > 1:
|
|
54
|
+
target_base = attrgetter('.'.join(split_object[:-1]))(target_base)
|
|
55
|
+
|
|
56
|
+
return target_base, addon_name
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _find_addons():
|
|
60
|
+
"""Find and load all copulas add-ons."""
|
|
61
|
+
group = 'copulas_modules'
|
|
62
|
+
try:
|
|
63
|
+
eps = entry_points(group=group)
|
|
64
|
+
except TypeError:
|
|
65
|
+
# Load-time selection requires Python >= 3.10 or importlib_metadata >= 3.6
|
|
66
|
+
eps = entry_points().get(group, [])
|
|
67
|
+
|
|
68
|
+
for entry_point in eps:
|
|
69
|
+
try:
|
|
70
|
+
addon = entry_point.load()
|
|
71
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
72
|
+
msg = f'Failed to load "{entry_point.name}" from "{entry_point.value}" with error:\n{e}'
|
|
73
|
+
warnings.warn(msg)
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
addon_target, addon_name = _get_addon_target(entry_point.name)
|
|
78
|
+
except AttributeError as error:
|
|
79
|
+
msg = f"Failed to set '{entry_point.name}': {error}."
|
|
80
|
+
warnings.warn(msg)
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
if isinstance(addon, ModuleType):
|
|
84
|
+
addon_module_name = f'{addon_target.__name__}.{addon_name}'
|
|
85
|
+
if addon_module_name not in sys.modules:
|
|
86
|
+
sys.modules[addon_module_name] = addon
|
|
87
|
+
|
|
88
|
+
setattr(addon_target, addon_name, addon)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
_find_addons()
|
|
@@ -8,8 +8,9 @@ import numpy as np
|
|
|
8
8
|
from scipy import stats
|
|
9
9
|
from scipy.optimize import brentq
|
|
10
10
|
|
|
11
|
-
from copulas import EPSILON, NotFittedError, random_state, validate_random_state
|
|
12
11
|
from copulas.bivariate.utils import split_matrix
|
|
12
|
+
from copulas.errors import NotFittedError
|
|
13
|
+
from copulas.utils import EPSILON, random_state, validate_random_state
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class CopulaTypes(Enum):
|
|
@@ -6,9 +6,9 @@ import numpy as np
|
|
|
6
6
|
import scipy.integrate as integrate
|
|
7
7
|
from scipy.optimize import least_squares
|
|
8
8
|
|
|
9
|
-
from copulas import EPSILON
|
|
10
9
|
from copulas.bivariate.base import Bivariate, CopulaTypes
|
|
11
10
|
from copulas.bivariate.utils import split_matrix
|
|
11
|
+
from copulas.utils import EPSILON
|
|
12
12
|
|
|
13
13
|
MIN_FLOAT_LOG = np.log(sys.float_info.min)
|
|
14
14
|
MAX_FLOAT_LOG = np.log(sys.float_info.max)
|
|
@@ -7,7 +7,9 @@ import numpy as np
|
|
|
7
7
|
import pandas as pd
|
|
8
8
|
from scipy import stats
|
|
9
9
|
|
|
10
|
-
from copulas import
|
|
10
|
+
from copulas.multivariate.base import Multivariate
|
|
11
|
+
from copulas.univariate import GaussianUnivariate, Univariate
|
|
12
|
+
from copulas.utils import (
|
|
11
13
|
EPSILON,
|
|
12
14
|
check_valid_values,
|
|
13
15
|
get_instance,
|
|
@@ -16,8 +18,6 @@ from copulas import (
|
|
|
16
18
|
store_args,
|
|
17
19
|
validate_random_state,
|
|
18
20
|
)
|
|
19
|
-
from copulas.multivariate.base import Multivariate
|
|
20
|
-
from copulas.univariate import GaussianUnivariate, Univariate
|
|
21
21
|
|
|
22
22
|
LOGGER = logging.getLogger(__name__)
|
|
23
23
|
DEFAULT_DISTRIBUTION = Univariate
|
|
@@ -70,26 +70,6 @@ class GaussianMultivariate(Multivariate):
|
|
|
70
70
|
|
|
71
71
|
return stats.norm.ppf(np.column_stack(U))
|
|
72
72
|
|
|
73
|
-
def _get_correlation(self, X):
|
|
74
|
-
"""Compute correlation matrix with transformed data.
|
|
75
|
-
|
|
76
|
-
Args:
|
|
77
|
-
X (numpy.ndarray):
|
|
78
|
-
Data for which the correlation needs to be computed.
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
numpy.ndarray:
|
|
82
|
-
computed correlation matrix.
|
|
83
|
-
"""
|
|
84
|
-
result = self._transform_to_normal(X)
|
|
85
|
-
correlation = pd.DataFrame(data=result).corr().to_numpy()
|
|
86
|
-
correlation = np.nan_to_num(correlation, nan=0.0)
|
|
87
|
-
# If singular, add some noise to the diagonal
|
|
88
|
-
if np.linalg.cond(correlation) > 1.0 / sys.float_info.epsilon:
|
|
89
|
-
correlation = correlation + np.identity(correlation.shape[0]) * EPSILON
|
|
90
|
-
|
|
91
|
-
return pd.DataFrame(correlation, index=self.columns, columns=self.columns)
|
|
92
|
-
|
|
93
73
|
@check_valid_values
|
|
94
74
|
def fit(self, X):
|
|
95
75
|
"""Compute the distribution for each variable and then its correlation matrix.
|
|
@@ -100,42 +80,88 @@ class GaussianMultivariate(Multivariate):
|
|
|
100
80
|
"""
|
|
101
81
|
LOGGER.info('Fitting %s', self)
|
|
102
82
|
|
|
83
|
+
# Validate the input data
|
|
84
|
+
X = self._validate_input(X)
|
|
85
|
+
columns, univariates = self._fit_columns(X)
|
|
86
|
+
|
|
87
|
+
self.columns = columns
|
|
88
|
+
self.univariates = univariates
|
|
89
|
+
|
|
90
|
+
LOGGER.debug('Computing correlation.')
|
|
91
|
+
self.correlation = self._get_correlation(X)
|
|
92
|
+
self.fitted = True
|
|
93
|
+
LOGGER.debug('GaussianMultivariate fitted successfully')
|
|
94
|
+
|
|
95
|
+
def _validate_input(self, X):
|
|
96
|
+
"""Validate the input data."""
|
|
103
97
|
if not isinstance(X, pd.DataFrame):
|
|
104
98
|
X = pd.DataFrame(X)
|
|
105
99
|
|
|
100
|
+
return X
|
|
101
|
+
|
|
102
|
+
def _fit_columns(self, X):
|
|
103
|
+
"""Fit each column to its distribution."""
|
|
106
104
|
columns = []
|
|
107
105
|
univariates = []
|
|
108
106
|
for column_name, column in X.items():
|
|
109
|
-
|
|
110
|
-
distribution = self.distribution.get(column_name, DEFAULT_DISTRIBUTION)
|
|
111
|
-
else:
|
|
112
|
-
distribution = self.distribution
|
|
113
|
-
|
|
107
|
+
distribution = self._get_distribution_for_column(column_name)
|
|
114
108
|
LOGGER.debug('Fitting column %s to %s', column_name, distribution)
|
|
115
109
|
|
|
116
|
-
univariate =
|
|
117
|
-
try:
|
|
118
|
-
univariate.fit(column)
|
|
119
|
-
except BaseException:
|
|
120
|
-
log_message = (
|
|
121
|
-
f'Unable to fit to a {distribution} distribution for column {column_name}. '
|
|
122
|
-
'Using a Gaussian distribution instead.'
|
|
123
|
-
)
|
|
124
|
-
LOGGER.info(log_message)
|
|
125
|
-
univariate = GaussianUnivariate()
|
|
126
|
-
univariate.fit(column)
|
|
127
|
-
|
|
110
|
+
univariate = self._fit_column(column, distribution, column_name)
|
|
128
111
|
columns.append(column_name)
|
|
129
112
|
univariates.append(univariate)
|
|
130
113
|
|
|
131
|
-
|
|
132
|
-
|
|
114
|
+
return columns, univariates
|
|
115
|
+
|
|
116
|
+
def _get_distribution_for_column(self, column_name):
|
|
117
|
+
"""Retrieve the distribution for a given column name."""
|
|
118
|
+
if isinstance(self.distribution, dict):
|
|
119
|
+
return self.distribution.get(column_name, DEFAULT_DISTRIBUTION)
|
|
120
|
+
|
|
121
|
+
return self.distribution
|
|
122
|
+
|
|
123
|
+
def _fit_column(self, column, distribution, column_name):
|
|
124
|
+
"""Fit a single column to its distribution with exception handling."""
|
|
125
|
+
univariate = get_instance(distribution)
|
|
126
|
+
try:
|
|
127
|
+
univariate.fit(column)
|
|
128
|
+
except Exception as error:
|
|
129
|
+
univariate = self._fit_with_fallback_distribution(
|
|
130
|
+
column, distribution, column_name, error
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return univariate
|
|
134
|
+
|
|
135
|
+
def _fit_with_fallback_distribution(self, column, distribution, column_name, error):
|
|
136
|
+
"""Fall back to fitting a Gaussian distribution and log the error."""
|
|
137
|
+
log_message = (
|
|
138
|
+
f'Unable to fit to a {distribution} distribution for column {column_name}. '
|
|
139
|
+
'Using a Gaussian distribution instead.'
|
|
140
|
+
)
|
|
141
|
+
LOGGER.info(log_message)
|
|
142
|
+
univariate = GaussianUnivariate()
|
|
143
|
+
univariate.fit(column)
|
|
144
|
+
return univariate
|
|
133
145
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
self.fitted = True
|
|
146
|
+
def _get_correlation(self, X):
|
|
147
|
+
"""Compute correlation matrix with transformed data.
|
|
137
148
|
|
|
138
|
-
|
|
149
|
+
Args:
|
|
150
|
+
X (numpy.ndarray):
|
|
151
|
+
Data for which the correlation needs to be computed.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
numpy.ndarray:
|
|
155
|
+
computed correlation matrix.
|
|
156
|
+
"""
|
|
157
|
+
result = self._transform_to_normal(X)
|
|
158
|
+
correlation = pd.DataFrame(data=result).corr().to_numpy()
|
|
159
|
+
correlation = np.nan_to_num(correlation, nan=0.0)
|
|
160
|
+
# If singular, add some noise to the diagonal
|
|
161
|
+
if np.linalg.cond(correlation) > 1.0 / sys.float_info.epsilon:
|
|
162
|
+
correlation = correlation + np.identity(correlation.shape[0]) * EPSILON
|
|
163
|
+
|
|
164
|
+
return pd.DataFrame(correlation, index=self.columns, columns=self.columns)
|
|
139
165
|
|
|
140
166
|
def probability_density(self, X):
|
|
141
167
|
"""Compute the probability density for each point in X.
|
|
@@ -6,9 +6,9 @@ from enum import Enum
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import scipy
|
|
8
8
|
|
|
9
|
-
from copulas import EPSILON, get_qualified_name
|
|
10
9
|
from copulas.bivariate.base import Bivariate
|
|
11
10
|
from copulas.multivariate.base import Multivariate
|
|
11
|
+
from copulas.utils import EPSILON, get_qualified_name
|
|
12
12
|
|
|
13
13
|
LOGGER = logging.getLogger(__name__)
|
|
14
14
|
|
|
@@ -7,7 +7,11 @@ import warnings
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import pandas as pd
|
|
9
9
|
|
|
10
|
-
from copulas import
|
|
10
|
+
from copulas.bivariate.base import Bivariate, CopulaTypes
|
|
11
|
+
from copulas.multivariate.base import Multivariate
|
|
12
|
+
from copulas.multivariate.tree import Tree, get_tree
|
|
13
|
+
from copulas.univariate.gaussian_kde import GaussianKDE
|
|
14
|
+
from copulas.utils import (
|
|
11
15
|
EPSILON,
|
|
12
16
|
check_valid_values,
|
|
13
17
|
get_qualified_name,
|
|
@@ -15,10 +19,6 @@ from copulas import (
|
|
|
15
19
|
store_args,
|
|
16
20
|
validate_random_state,
|
|
17
21
|
)
|
|
18
|
-
from copulas.bivariate.base import Bivariate, CopulaTypes
|
|
19
|
-
from copulas.multivariate.base import Multivariate
|
|
20
|
-
from copulas.multivariate.tree import Tree, get_tree
|
|
21
|
-
from copulas.univariate.gaussian_kde import GaussianKDE
|
|
22
22
|
|
|
23
23
|
LOGGER = logging.getLogger(__name__)
|
|
24
24
|
|
|
@@ -76,8 +76,7 @@ class VineCopula(Multivariate):
|
|
|
76
76
|
def __init__(self, vine_type, random_state=None):
|
|
77
77
|
if sys.version_info > (3, 8):
|
|
78
78
|
warnings.warn(
|
|
79
|
-
'Vines have not been fully tested on Python >= 3.8 and might '
|
|
80
|
-
'produce wrong results.'
|
|
79
|
+
'Vines have not been fully tested on Python >= 3.8 and might produce wrong results.'
|
|
81
80
|
)
|
|
82
81
|
|
|
83
82
|
self.random_state = validate_random_state(random_state)
|
|
@@ -6,15 +6,15 @@ from enum import Enum
|
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
|
|
9
|
-
from copulas import
|
|
10
|
-
|
|
9
|
+
from copulas.errors import NotFittedError
|
|
10
|
+
from copulas.univariate.selection import select_univariate
|
|
11
|
+
from copulas.utils import (
|
|
11
12
|
get_instance,
|
|
12
13
|
get_qualified_name,
|
|
13
14
|
random_state,
|
|
14
15
|
store_args,
|
|
15
16
|
validate_random_state,
|
|
16
17
|
)
|
|
17
|
-
from copulas.univariate.selection import select_univariate
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class ParametricType(Enum):
|
|
@@ -4,9 +4,9 @@ import numpy as np
|
|
|
4
4
|
from scipy.special import ndtr
|
|
5
5
|
from scipy.stats import gaussian_kde
|
|
6
6
|
|
|
7
|
-
from copulas import EPSILON, random_state, store_args, validate_random_state
|
|
8
7
|
from copulas.optimize import bisect, chandrupatla
|
|
9
8
|
from copulas.univariate.base import BoundedType, ParametricType, ScipyModel
|
|
9
|
+
from copulas.utils import EPSILON, random_state, store_args, validate_random_state
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class GaussianKDE(ScipyModel):
|
|
@@ -6,8 +6,8 @@ import numpy as np
|
|
|
6
6
|
from scipy.optimize import fmin_slsqp
|
|
7
7
|
from scipy.stats import truncnorm
|
|
8
8
|
|
|
9
|
-
from copulas import EPSILON, store_args, validate_random_state
|
|
10
9
|
from copulas.univariate.base import BoundedType, ParametricType, ScipyModel
|
|
10
|
+
from copulas.utils import EPSILON, store_args, validate_random_state
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class TruncatedGaussian(ScipyModel):
|
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
"""Top-level package for Copulas."""
|
|
4
|
-
|
|
5
|
-
__author__ = 'DataCebo, Inc.'
|
|
6
|
-
__email__ = 'info@sdv.dev'
|
|
7
|
-
__version__ = '0.11.1'
|
|
1
|
+
"""Utils module."""
|
|
8
2
|
|
|
9
3
|
import contextlib
|
|
10
4
|
import importlib
|
|
11
|
-
import sys
|
|
12
|
-
import warnings
|
|
13
5
|
from copy import deepcopy
|
|
14
|
-
from
|
|
15
|
-
from operator import attrgetter
|
|
6
|
+
from functools import wraps
|
|
16
7
|
|
|
17
8
|
import numpy as np
|
|
18
9
|
import pandas as pd
|
|
@@ -20,10 +11,6 @@ import pandas as pd
|
|
|
20
11
|
EPSILON = np.finfo(np.float32).eps
|
|
21
12
|
|
|
22
13
|
|
|
23
|
-
class NotFittedError(Exception):
|
|
24
|
-
"""NotFittedError class."""
|
|
25
|
-
|
|
26
|
-
|
|
27
14
|
@contextlib.contextmanager
|
|
28
15
|
def set_random_state(random_state, set_model_random_state):
|
|
29
16
|
"""Context manager for managing the random state.
|
|
@@ -35,7 +22,6 @@ def set_random_state(random_state, set_model_random_state):
|
|
|
35
22
|
Function to set the random state on the model.
|
|
36
23
|
"""
|
|
37
24
|
original_state = np.random.get_state()
|
|
38
|
-
|
|
39
25
|
np.random.set_state(random_state.get_state())
|
|
40
26
|
|
|
41
27
|
try:
|
|
@@ -55,10 +41,10 @@ def random_state(function):
|
|
|
55
41
|
The function to wrap around.
|
|
56
42
|
"""
|
|
57
43
|
|
|
44
|
+
@wraps(function)
|
|
58
45
|
def wrapper(self, *args, **kwargs):
|
|
59
46
|
if self.random_state is None:
|
|
60
47
|
return function(self, *args, **kwargs)
|
|
61
|
-
|
|
62
48
|
else:
|
|
63
49
|
with set_random_state(self.random_state, self.set_random_state):
|
|
64
50
|
return function(self, *args, **kwargs)
|
|
@@ -123,6 +109,7 @@ def store_args(__init__):
|
|
|
123
109
|
callable: Decorated ``__init__`` function.
|
|
124
110
|
"""
|
|
125
111
|
|
|
112
|
+
@wraps(__init__)
|
|
126
113
|
def new__init__(self, *args, **kwargs):
|
|
127
114
|
args_copy = deepcopy(args)
|
|
128
115
|
kwargs_copy = deepcopy(kwargs)
|
|
@@ -138,7 +125,6 @@ def get_qualified_name(_object):
|
|
|
138
125
|
module = _object.__module__
|
|
139
126
|
if hasattr(_object, '__name__'):
|
|
140
127
|
_class = _object.__name__
|
|
141
|
-
|
|
142
128
|
else:
|
|
143
129
|
_class = _object.__class__.__name__
|
|
144
130
|
|
|
@@ -184,6 +170,7 @@ def vectorize(function):
|
|
|
184
170
|
|
|
185
171
|
"""
|
|
186
172
|
|
|
173
|
+
@wraps(function)
|
|
187
174
|
def decorated(self, X, *args, **kwargs):
|
|
188
175
|
if not isinstance(X, np.ndarray):
|
|
189
176
|
return function(self, X, *args, **kwargs)
|
|
@@ -195,11 +182,9 @@ def vectorize(function):
|
|
|
195
182
|
return np.fromiter(
|
|
196
183
|
(function(self, *x, *args, **kwargs) for x in X), np.dtype('float64')
|
|
197
184
|
)
|
|
198
|
-
|
|
199
185
|
else:
|
|
200
186
|
raise ValueError('Arrays of dimensionality higher than 2 are not supported.')
|
|
201
187
|
|
|
202
|
-
decorated.__doc__ = function.__doc__
|
|
203
188
|
return decorated
|
|
204
189
|
|
|
205
190
|
|
|
@@ -213,6 +198,7 @@ def scalarize(function):
|
|
|
213
198
|
callable: Decorated function that accepts and returns scalars.
|
|
214
199
|
"""
|
|
215
200
|
|
|
201
|
+
@wraps(function)
|
|
216
202
|
def decorated(self, X, *args, **kwargs):
|
|
217
203
|
scalar = not isinstance(X, np.ndarray)
|
|
218
204
|
|
|
@@ -225,7 +211,6 @@ def scalarize(function):
|
|
|
225
211
|
|
|
226
212
|
return result
|
|
227
213
|
|
|
228
|
-
decorated.__doc__ = function.__doc__
|
|
229
214
|
return decorated
|
|
230
215
|
|
|
231
216
|
|
|
@@ -242,10 +227,10 @@ def check_valid_values(function):
|
|
|
242
227
|
ValueError: If there are missing or invalid values or if the dataset is empty.
|
|
243
228
|
"""
|
|
244
229
|
|
|
230
|
+
@wraps(function)
|
|
245
231
|
def decorated(self, X, *args, **kwargs):
|
|
246
232
|
if isinstance(X, pd.DataFrame):
|
|
247
233
|
W = X.to_numpy()
|
|
248
|
-
|
|
249
234
|
else:
|
|
250
235
|
W = X
|
|
251
236
|
|
|
@@ -261,77 +246,3 @@ def check_valid_values(function):
|
|
|
261
246
|
return function(self, X, *args, **kwargs)
|
|
262
247
|
|
|
263
248
|
return decorated
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
def _get_addon_target(addon_path_name):
|
|
267
|
-
"""Find the target object for the add-on.
|
|
268
|
-
|
|
269
|
-
Args:
|
|
270
|
-
addon_path_name (str):
|
|
271
|
-
The add-on's name. The add-on's name should be the full path of valid Python
|
|
272
|
-
identifiers (i.e. importable.module:object.attr).
|
|
273
|
-
|
|
274
|
-
Returns:
|
|
275
|
-
tuple:
|
|
276
|
-
* object:
|
|
277
|
-
The base module or object the add-on should be added to.
|
|
278
|
-
* str:
|
|
279
|
-
The name the add-on should be added to under the module or object.
|
|
280
|
-
"""
|
|
281
|
-
module_path, _, object_path = addon_path_name.partition(':')
|
|
282
|
-
module_path = module_path.split('.')
|
|
283
|
-
|
|
284
|
-
if module_path[0] != __name__:
|
|
285
|
-
msg = f"expected base module to be '{__name__}', found '{module_path[0]}'"
|
|
286
|
-
raise AttributeError(msg)
|
|
287
|
-
|
|
288
|
-
target_base = sys.modules[__name__]
|
|
289
|
-
for submodule in module_path[1:-1]:
|
|
290
|
-
target_base = getattr(target_base, submodule)
|
|
291
|
-
|
|
292
|
-
addon_name = module_path[-1]
|
|
293
|
-
if object_path:
|
|
294
|
-
if len(module_path) > 1 and not hasattr(target_base, module_path[-1]):
|
|
295
|
-
msg = f"cannot add '{object_path}' to unknown submodule '{'.'.join(module_path)}'"
|
|
296
|
-
raise AttributeError(msg)
|
|
297
|
-
|
|
298
|
-
if len(module_path) > 1:
|
|
299
|
-
target_base = getattr(target_base, module_path[-1])
|
|
300
|
-
|
|
301
|
-
split_object = object_path.split('.')
|
|
302
|
-
addon_name = split_object[-1]
|
|
303
|
-
|
|
304
|
-
if len(split_object) > 1:
|
|
305
|
-
target_base = attrgetter('.'.join(split_object[:-1]))(target_base)
|
|
306
|
-
|
|
307
|
-
return target_base, addon_name
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
def _find_addons():
|
|
311
|
-
"""Find and load all copulas add-ons."""
|
|
312
|
-
group = 'copulas_modules'
|
|
313
|
-
try:
|
|
314
|
-
eps = entry_points(group=group)
|
|
315
|
-
except TypeError:
|
|
316
|
-
# Load-time selection requires Python >= 3.10 or importlib_metadata >= 3.6
|
|
317
|
-
eps = entry_points().get(group, [])
|
|
318
|
-
|
|
319
|
-
for entry_point in eps:
|
|
320
|
-
try:
|
|
321
|
-
addon = entry_point.load()
|
|
322
|
-
except Exception: # pylint: disable=broad-exception-caught
|
|
323
|
-
msg = f'Failed to load "{entry_point.name}" from "{entry_point.value}".'
|
|
324
|
-
warnings.warn(msg)
|
|
325
|
-
continue
|
|
326
|
-
|
|
327
|
-
try:
|
|
328
|
-
addon_target, addon_name = _get_addon_target(entry_point.name)
|
|
329
|
-
except AttributeError as error:
|
|
330
|
-
msg = f"Failed to set '{entry_point.name}': {error}."
|
|
331
|
-
warnings.warn(msg)
|
|
332
|
-
continue
|
|
333
|
-
|
|
334
|
-
setattr(addon_target, addon_name, addon)
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
_find_addons()
|
|
@@ -149,7 +149,7 @@ namespaces = false
|
|
|
149
149
|
]
|
|
150
150
|
|
|
151
151
|
[tool.bumpversion]
|
|
152
|
-
current_version = "0.
|
|
152
|
+
current_version = "0.12.0"
|
|
153
153
|
commit = true
|
|
154
154
|
tag = true
|
|
155
155
|
parse = '(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\.(?P<release>[a-z]+)(?P<candidate>\d+))?'
|
|
@@ -182,7 +182,8 @@ exclude = [
|
|
|
182
182
|
".git",
|
|
183
183
|
"__pycache__",
|
|
184
184
|
".ipynb_checkpoints",
|
|
185
|
-
"*.ipynb"
|
|
185
|
+
"*.ipynb",
|
|
186
|
+
"tasks.py",
|
|
186
187
|
]
|
|
187
188
|
|
|
188
189
|
[tool.ruff.lint]
|
|
@@ -192,14 +193,23 @@ select = [
|
|
|
192
193
|
# Pycodestyle
|
|
193
194
|
"E",
|
|
194
195
|
"W",
|
|
195
|
-
|
|
196
|
+
# pydocstyle
|
|
197
|
+
"D",
|
|
196
198
|
# isort
|
|
197
199
|
"I001",
|
|
200
|
+
# print statements
|
|
201
|
+
"T201",
|
|
202
|
+
# pandas-vet
|
|
203
|
+
"PD",
|
|
204
|
+
# numpy 2.0
|
|
205
|
+
"NPY201"
|
|
198
206
|
]
|
|
199
207
|
ignore = [
|
|
200
|
-
|
|
208
|
+
# pydocstyle
|
|
201
209
|
"D107", # Missing docstring in __init__
|
|
202
210
|
"D417", # Missing argument descriptions in the docstring, this is a bug from pydocstyle: https://github.com/PyCQA/pydocstyle/issues/449
|
|
211
|
+
"PD901",
|
|
212
|
+
"PD101",
|
|
203
213
|
]
|
|
204
214
|
|
|
205
215
|
[tool.ruff.format]
|
|
@@ -209,14 +219,18 @@ preview = true
|
|
|
209
219
|
docstring-code-format = true
|
|
210
220
|
docstring-code-line-length = "dynamic"
|
|
211
221
|
|
|
212
|
-
[tool.ruff.lint.pep8-naming]
|
|
213
|
-
extend-ignore-names = ["X", "C", "X_padded", "Y", "Y_padded"]
|
|
214
|
-
|
|
215
222
|
[tool.ruff.lint.isort]
|
|
216
223
|
known-first-party = ["copulas"]
|
|
224
|
+
lines-between-types = 0
|
|
217
225
|
|
|
218
226
|
[tool.ruff.lint.per-file-ignores]
|
|
219
227
|
"__init__.py" = ["F401", "E402", "F403", "F405", "E501", "I001"]
|
|
228
|
+
"errors.py" = ["D105"]
|
|
229
|
+
"tests/**.py" = ["D"]
|
|
220
230
|
|
|
221
231
|
[tool.ruff.lint.pydocstyle]
|
|
222
232
|
convention = "google"
|
|
233
|
+
|
|
234
|
+
[tool.ruff.lint.pycodestyle]
|
|
235
|
+
max-doc-length = 100
|
|
236
|
+
max-line-length = 100
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|