rdt 1.5.1.dev0__tar.gz → 1.6.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.
- {rdt-1.5.1.dev0 → rdt-1.6.0}/HISTORY.md +24 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/PKG-INFO +26 -3
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/__init__.py +1 -1
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/addons/addons_setup.py +1 -2
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/datetime.py +27 -5
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/pii/anonymizer.py +25 -1
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/PKG-INFO +26 -3
- {rdt-1.5.1.dev0 → rdt-1.6.0}/setup.cfg +1 -1
- {rdt-1.5.1.dev0 → rdt-1.6.0}/setup.py +2 -3
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/pii/test_anonymizer.py +21 -1
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_datetime.py +41 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/pii/test_anonymizer.py +40 -2
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_datetime.py +31 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/AUTHORS.rst +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/CONTRIBUTING.rst +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/LICENSE +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/MANIFEST.in +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/README.md +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/RELEASE.md +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/errors.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/hyper_transformer.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/base.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/boolean.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/categorical.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/datetime.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/numerical.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/pii.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/text.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/utils.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/performance.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/profiling.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/base.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/boolean.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/categorical.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/null.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/numerical.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/pii/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/pii/utils.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/text.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/utils.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/SOURCES.txt +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/dependency_links.txt +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/not-zip-safe +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/requires.txt +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/top_level.txt +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/code_style.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/contributing.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/datasets/tests/test_boolean.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/datasets/tests/test_categorical.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/datasets/tests/test_datetime.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/datasets/tests/test_numerical.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/datasets/tests/test_utils.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/test_hyper_transformer.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/test_transformers.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/pii/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_base.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_boolean.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_categorical.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_numerical.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_text.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/performance/README.md +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/performance/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/performance/test_performance.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/performance/tests/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/performance/tests/test_profiling.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/test___init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/test_hyper_transformer.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/addons/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/addons/identity/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/addons/identity/test_identity.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/pii/__init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/pii/test_utils.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test___init__.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_base.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_boolean.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_categorical.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_null.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_numerical.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_text.py +0 -0
- {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_utils.py +0 -0
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# History
|
|
2
2
|
|
|
3
|
+
## 1.6.0 -2023-07-12
|
|
4
|
+
|
|
5
|
+
This release adds the ability to generate missing values to the `AnonymizedFaker`. Users can now provide the `missing_value_generation` parameter during initialization. They can set it to `None` to not generate any missing values, or `'random'` to generate random missing values in the same proportion as the fitted data.
|
|
6
|
+
|
|
7
|
+
Additionally, this release improves the `NullTransformer` by allowing nulls to be replaced on the forward transform even if `missing_value_generation` is set to None. It also fixes a bug that was causing the `UnixTimestampEncoder` to return a different dtype than the input on `reverse_transform`. This was particularly problematic when datetime columns are represented as ints.
|
|
8
|
+
|
|
9
|
+
### New Features
|
|
10
|
+
|
|
11
|
+
* AnonymizedFaker should be able to model and generate missing values - Issue [#660](https://github.com/sdv-dev/RDT/issues/660) by @R-Palazzo
|
|
12
|
+
|
|
13
|
+
### Bugs
|
|
14
|
+
|
|
15
|
+
* The datetime transformers don't give me back the same dtype sometimes - Issue [#657](https://github.com/sdv-dev/RDT/issues/657) by @frances-h
|
|
16
|
+
* RDT NullTransformer doesn't replace nulls if missing_value_generation is None - Issue [#658](https://github.com/sdv-dev/RDT/issues/658) by @amontanez24
|
|
17
|
+
|
|
18
|
+
### Maintenance
|
|
19
|
+
|
|
20
|
+
* Remove python 3.7 builds - Issue [#663](https://github.com/sdv-dev/RDT/issues/663) by @amontanez24
|
|
21
|
+
* Drop support for Python 3.7 - Issue [#666](https://github.com/sdv-dev/RDT/issues/666) by @amontanez24
|
|
22
|
+
|
|
23
|
+
### Internal
|
|
24
|
+
|
|
25
|
+
* Add add-on modules to sys.modules - Issue [#653](https://github.com/sdv-dev/RDT/issues/653) by @amontanez24
|
|
26
|
+
|
|
3
27
|
## 1.5.0 - 2023-06-01
|
|
4
28
|
|
|
5
29
|
This release adds a new parameter called `missing_value_generation` to the initialization of certain transformers to specify how missing values should be created. The parameter can be used in the `FloatFormatter`, `BinaryEncoder`, `UnixTimestampEncoder`, `OptimizedTimestampEncoder`, `GaussianNormalizer` and `ClusterBasedNormalizer`. Additionally, it fixes a bug that was causing every column that had nulls to generate them in the same place.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: rdt
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0
|
|
4
4
|
Summary: Reversible Data Transforms
|
|
5
5
|
Home-page: https://github.com/sdv-dev/RDT
|
|
6
6
|
Author: DataCebo, Inc.
|
|
@@ -12,13 +12,12 @@ Classifier: Intended Audience :: Developers
|
|
|
12
12
|
Classifier: License :: Free for non-commercial use
|
|
13
13
|
Classifier: Natural Language :: English
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.8
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
19
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.8,<3.12
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
23
22
|
Provides-Extra: copulas
|
|
24
23
|
Provides-Extra: test
|
|
@@ -226,6 +225,30 @@ for specific needs.
|
|
|
226
225
|
|
|
227
226
|
# History
|
|
228
227
|
|
|
228
|
+
## 1.6.0 -2023-07-12
|
|
229
|
+
|
|
230
|
+
This release adds the ability to generate missing values to the `AnonymizedFaker`. Users can now provide the `missing_value_generation` parameter during initialization. They can set it to `None` to not generate any missing values, or `'random'` to generate random missing values in the same proportion as the fitted data.
|
|
231
|
+
|
|
232
|
+
Additionally, this release improves the `NullTransformer` by allowing nulls to be replaced on the forward transform even if `missing_value_generation` is set to None. It also fixes a bug that was causing the `UnixTimestampEncoder` to return a different dtype than the input on `reverse_transform`. This was particularly problematic when datetime columns are represented as ints.
|
|
233
|
+
|
|
234
|
+
### New Features
|
|
235
|
+
|
|
236
|
+
* AnonymizedFaker should be able to model and generate missing values - Issue [#660](https://github.com/sdv-dev/RDT/issues/660) by @R-Palazzo
|
|
237
|
+
|
|
238
|
+
### Bugs
|
|
239
|
+
|
|
240
|
+
* The datetime transformers don't give me back the same dtype sometimes - Issue [#657](https://github.com/sdv-dev/RDT/issues/657) by @frances-h
|
|
241
|
+
* RDT NullTransformer doesn't replace nulls if missing_value_generation is None - Issue [#658](https://github.com/sdv-dev/RDT/issues/658) by @amontanez24
|
|
242
|
+
|
|
243
|
+
### Maintenance
|
|
244
|
+
|
|
245
|
+
* Remove python 3.7 builds - Issue [#663](https://github.com/sdv-dev/RDT/issues/663) by @amontanez24
|
|
246
|
+
* Drop support for Python 3.7 - Issue [#666](https://github.com/sdv-dev/RDT/issues/666) by @amontanez24
|
|
247
|
+
|
|
248
|
+
### Internal
|
|
249
|
+
|
|
250
|
+
* Add add-on modules to sys.modules - Issue [#653](https://github.com/sdv-dev/RDT/issues/653) by @amontanez24
|
|
251
|
+
|
|
229
252
|
## 1.5.0 - 2023-06-01
|
|
230
253
|
|
|
231
254
|
This release adds a new parameter called `missing_value_generation` to the initialization of certain transformers to specify how missing values should be created. The parameter can be used in the `FloatFormatter`, `BinaryEncoder`, `UnixTimestampEncoder`, `OptimizedTimestampEncoder`, `GaussianNormalizer` and `ClusterBasedNormalizer`. Additionally, it fixes a bug that was causing every column that had nulls to generate them in the same place.
|
|
@@ -43,7 +43,6 @@ def _build_setup(addon_json):
|
|
|
43
43
|
'License :: Free for non-commercial use',
|
|
44
44
|
'Natural Language :: English',
|
|
45
45
|
'Programming Language :: Python :: 3',
|
|
46
|
-
'Programming Language :: Python :: 3.7',
|
|
47
46
|
'Programming Language :: Python :: 3.8',
|
|
48
47
|
'Programming Language :: Python :: 3.9',
|
|
49
48
|
'Programming Language :: Python :: 3.10',
|
|
@@ -59,7 +58,7 @@ def _build_setup(addon_json):
|
|
|
59
58
|
long_description_content_type='text/markdown',
|
|
60
59
|
name=addon_name,
|
|
61
60
|
packages=find_namespace_packages(include=[addon_module]),
|
|
62
|
-
python_requires='>=3.
|
|
61
|
+
python_requires='>=3.8,<3.12',
|
|
63
62
|
url='https://github.com/sdv-dev/RDT',
|
|
64
63
|
version=RDT_VERSION,
|
|
65
64
|
zip_safe=False,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Transformer for datetime data."""
|
|
2
2
|
import numpy as np
|
|
3
3
|
import pandas as pd
|
|
4
|
-
from pandas.api.types import is_datetime64_dtype
|
|
4
|
+
from pandas.api.types import is_datetime64_dtype, is_numeric_dtype
|
|
5
5
|
from pandas.core.tools.datetimes import _guess_datetime_format_for_array
|
|
6
6
|
|
|
7
7
|
from rdt.transformers.base import BaseTransformer
|
|
@@ -55,7 +55,26 @@ class UnixTimestampEncoder(BaseTransformer):
|
|
|
55
55
|
self._dtype = None
|
|
56
56
|
|
|
57
57
|
def _convert_to_datetime(self, data):
|
|
58
|
-
|
|
58
|
+
"""Convert datetime column into datetime dtype.
|
|
59
|
+
|
|
60
|
+
Convert the datetime column to datetime dtype using the ``datetime_format``.
|
|
61
|
+
All non-numeric columns will automatically be cast to datetimes. Numeric columns
|
|
62
|
+
with a ``datetime_format`` will be treated as strings and cast to datetime. Numeric
|
|
63
|
+
columns without a ``datetime_format`` will be treated as already converted datetimes.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
data (pandas.Series):
|
|
67
|
+
The datetime column.
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
- ``TypeError`` if data cannot be converted to datetime.
|
|
71
|
+
- ``ValueError`` if data does not match the specified datetime format
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
pandas.Series:
|
|
75
|
+
The datetime column converted to the datetime dtype.
|
|
76
|
+
"""
|
|
77
|
+
if self.datetime_format or not is_numeric_dtype(data):
|
|
59
78
|
try:
|
|
60
79
|
pandas_datetime_format = None
|
|
61
80
|
if self.datetime_format:
|
|
@@ -138,13 +157,16 @@ class UnixTimestampEncoder(BaseTransformer):
|
|
|
138
157
|
data = self._reverse_transform_helper(data)
|
|
139
158
|
datetime_data = pd.to_datetime(data)
|
|
140
159
|
if self.datetime_format:
|
|
141
|
-
if self._dtype
|
|
142
|
-
datetime_data = datetime_data.dt.strftime(self.datetime_format)
|
|
143
|
-
elif is_datetime64_dtype(self._dtype) and '.%f' not in self.datetime_format:
|
|
160
|
+
if is_datetime64_dtype(self._dtype) and '.%f' not in self.datetime_format:
|
|
144
161
|
datetime_data = pd.to_datetime(
|
|
145
162
|
datetime_data.dt.strftime(self.datetime_format),
|
|
146
163
|
format=self.datetime_format,
|
|
147
164
|
)
|
|
165
|
+
else:
|
|
166
|
+
datetime_data = datetime_data.dt.strftime(self.datetime_format).astype(self._dtype)
|
|
167
|
+
elif is_numeric_dtype(self._dtype):
|
|
168
|
+
datetime_data = pd.to_numeric(datetime_data.astype('object'), errors='coerce')
|
|
169
|
+
datetime_data = datetime_data.astype(self._dtype)
|
|
148
170
|
|
|
149
171
|
return datetime_data
|
|
150
172
|
|
|
@@ -36,6 +36,14 @@ class AnonymizedFaker(BaseTransformer):
|
|
|
36
36
|
Whether or not to ensure that the new anonymized data is all unique. If it isn't
|
|
37
37
|
possible to create the requested number of rows, then an error will be raised.
|
|
38
38
|
Defaults to ``False``.
|
|
39
|
+
missing_value_generation (str or None):
|
|
40
|
+
The way missing values are being handled. There are two strategies:
|
|
41
|
+
|
|
42
|
+
* ``random``: Randomly generates missing values based on the percentage of
|
|
43
|
+
missing values.
|
|
44
|
+
* ``None``: Don't learn anything during fit. Then during reverse transform,
|
|
45
|
+
don't create any missing values.
|
|
46
|
+
|
|
39
47
|
"""
|
|
40
48
|
|
|
41
49
|
IS_GENERATOR = True
|
|
@@ -85,7 +93,7 @@ class AnonymizedFaker(BaseTransformer):
|
|
|
85
93
|
)
|
|
86
94
|
|
|
87
95
|
def __init__(self, provider_name=None, function_name=None, function_kwargs=None,
|
|
88
|
-
locales=None, enforce_uniqueness=False):
|
|
96
|
+
locales=None, enforce_uniqueness=False, missing_value_generation='random'):
|
|
89
97
|
super().__init__()
|
|
90
98
|
self.data_length = None
|
|
91
99
|
self.enforce_uniqueness = enforce_uniqueness
|
|
@@ -107,6 +115,15 @@ class AnonymizedFaker(BaseTransformer):
|
|
|
107
115
|
if self.locales:
|
|
108
116
|
self._check_locales()
|
|
109
117
|
|
|
118
|
+
if missing_value_generation not in ['random', None]:
|
|
119
|
+
raise TransformerInputError(
|
|
120
|
+
f"Missing value generation '{missing_value_generation}' is not supported "
|
|
121
|
+
"for AnonymizedFaker. Please use either 'random' or None."
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
self.missing_value_generation = missing_value_generation
|
|
125
|
+
self._nan_frequency = 0.0
|
|
126
|
+
|
|
110
127
|
def reset_randomization(self):
|
|
111
128
|
"""Create a new ``Faker`` instance."""
|
|
112
129
|
super().reset_randomization()
|
|
@@ -138,6 +155,8 @@ class AnonymizedFaker(BaseTransformer):
|
|
|
138
155
|
"""
|
|
139
156
|
self._set_faker_seed(data)
|
|
140
157
|
self.data_length = len(data)
|
|
158
|
+
if self.missing_value_generation == 'random':
|
|
159
|
+
self._nan_frequency = data.isna().sum() / len(data)
|
|
141
160
|
|
|
142
161
|
def _transform(self, _data):
|
|
143
162
|
"""Drop the input column by returning ``None``."""
|
|
@@ -170,6 +189,11 @@ class AnonymizedFaker(BaseTransformer):
|
|
|
170
189
|
f"('{self.get_input_column()}')."
|
|
171
190
|
) from exception
|
|
172
191
|
|
|
192
|
+
if self.missing_value_generation == 'random':
|
|
193
|
+
num_nans = int(self._nan_frequency * sample_size)
|
|
194
|
+
nan_indices = np.random.choice(sample_size, num_nans, replace=False)
|
|
195
|
+
reverse_transformed[nan_indices] = np.nan
|
|
196
|
+
|
|
173
197
|
return reverse_transformed
|
|
174
198
|
|
|
175
199
|
def __repr__(self):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: rdt
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0
|
|
4
4
|
Summary: Reversible Data Transforms
|
|
5
5
|
Home-page: https://github.com/sdv-dev/RDT
|
|
6
6
|
Author: DataCebo, Inc.
|
|
@@ -12,13 +12,12 @@ Classifier: Intended Audience :: Developers
|
|
|
12
12
|
Classifier: License :: Free for non-commercial use
|
|
13
13
|
Classifier: Natural Language :: English
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.8
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
19
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.8,<3.12
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
23
22
|
Provides-Extra: copulas
|
|
24
23
|
Provides-Extra: test
|
|
@@ -226,6 +225,30 @@ for specific needs.
|
|
|
226
225
|
|
|
227
226
|
# History
|
|
228
227
|
|
|
228
|
+
## 1.6.0 -2023-07-12
|
|
229
|
+
|
|
230
|
+
This release adds the ability to generate missing values to the `AnonymizedFaker`. Users can now provide the `missing_value_generation` parameter during initialization. They can set it to `None` to not generate any missing values, or `'random'` to generate random missing values in the same proportion as the fitted data.
|
|
231
|
+
|
|
232
|
+
Additionally, this release improves the `NullTransformer` by allowing nulls to be replaced on the forward transform even if `missing_value_generation` is set to None. It also fixes a bug that was causing the `UnixTimestampEncoder` to return a different dtype than the input on `reverse_transform`. This was particularly problematic when datetime columns are represented as ints.
|
|
233
|
+
|
|
234
|
+
### New Features
|
|
235
|
+
|
|
236
|
+
* AnonymizedFaker should be able to model and generate missing values - Issue [#660](https://github.com/sdv-dev/RDT/issues/660) by @R-Palazzo
|
|
237
|
+
|
|
238
|
+
### Bugs
|
|
239
|
+
|
|
240
|
+
* The datetime transformers don't give me back the same dtype sometimes - Issue [#657](https://github.com/sdv-dev/RDT/issues/657) by @frances-h
|
|
241
|
+
* RDT NullTransformer doesn't replace nulls if missing_value_generation is None - Issue [#658](https://github.com/sdv-dev/RDT/issues/658) by @amontanez24
|
|
242
|
+
|
|
243
|
+
### Maintenance
|
|
244
|
+
|
|
245
|
+
* Remove python 3.7 builds - Issue [#663](https://github.com/sdv-dev/RDT/issues/663) by @amontanez24
|
|
246
|
+
* Drop support for Python 3.7 - Issue [#666](https://github.com/sdv-dev/RDT/issues/666) by @amontanez24
|
|
247
|
+
|
|
248
|
+
### Internal
|
|
249
|
+
|
|
250
|
+
* Add add-on modules to sys.modules - Issue [#653](https://github.com/sdv-dev/RDT/issues/653) by @amontanez24
|
|
251
|
+
|
|
229
252
|
## 1.5.0 - 2023-06-01
|
|
230
253
|
|
|
231
254
|
This release adds a new parameter called `missing_value_generation` to the initialization of certain transformers to specify how missing values should be created. The parameter can be used in the `FloatFormatter`, `BinaryEncoder`, `UnixTimestampEncoder`, `OptimizedTimestampEncoder`, `GaussianNormalizer` and `ClusterBasedNormalizer`. Additionally, it fixes a bug that was causing every column that had nulls to generate them in the same place.
|
|
@@ -111,7 +111,6 @@ setup(
|
|
|
111
111
|
'License :: Free for non-commercial use',
|
|
112
112
|
'Natural Language :: English',
|
|
113
113
|
'Programming Language :: Python :: 3',
|
|
114
|
-
'Programming Language :: Python :: 3.7',
|
|
115
114
|
'Programming Language :: Python :: 3.8',
|
|
116
115
|
'Programming Language :: Python :: 3.9',
|
|
117
116
|
'Programming Language :: Python :: 3.10',
|
|
@@ -135,11 +134,11 @@ setup(
|
|
|
135
134
|
include=['rdt', 'rdt.*'],
|
|
136
135
|
exclude=['rdt.transformers.addons.*']
|
|
137
136
|
),
|
|
138
|
-
python_requires='>=3.
|
|
137
|
+
python_requires='>=3.8,<3.12',
|
|
139
138
|
setup_requires=setup_requires,
|
|
140
139
|
test_suite='tests',
|
|
141
140
|
tests_require=tests_require,
|
|
142
141
|
url='https://github.com/sdv-dev/RDT',
|
|
143
|
-
version='1.
|
|
142
|
+
version='1.6.0',
|
|
144
143
|
zip_safe=False,
|
|
145
144
|
)
|
|
@@ -69,6 +69,26 @@ def test_anonymizedfaker_with_nans():
|
|
|
69
69
|
'id': [1, 2, 3, 4, 5],
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
+
pd.testing.assert_frame_equal(transformed, expected_transformed)
|
|
73
|
+
assert len(reverse_transform['username']) == 5
|
|
74
|
+
assert reverse_transform['username'].isna().sum() == 1
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_anonymizedfaker_with_nans_missing_value_generation_none():
|
|
78
|
+
"""End to end test settings missing_value_generation=None."""
|
|
79
|
+
data = pd.DataFrame({
|
|
80
|
+
'id': [1, 2, 3, 4, 5],
|
|
81
|
+
'username': ['a', np.nan, 'c', 'd', 'e']
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
instance = AnonymizedFaker(missing_value_generation=None)
|
|
85
|
+
transformed = instance.fit_transform(data, 'username')
|
|
86
|
+
reverse_transform = instance.reverse_transform(transformed)
|
|
87
|
+
|
|
88
|
+
expected_transformed = pd.DataFrame({
|
|
89
|
+
'id': [1, 2, 3, 4, 5],
|
|
90
|
+
})
|
|
91
|
+
|
|
72
92
|
pd.testing.assert_frame_equal(transformed, expected_transformed)
|
|
73
93
|
assert len(reverse_transform['username']) == 5
|
|
74
94
|
assert reverse_transform['username'].isna().sum() == 0
|
|
@@ -102,7 +122,7 @@ def test_anonymizedfaker_custom_provider_with_nans():
|
|
|
102
122
|
|
|
103
123
|
pd.testing.assert_frame_equal(transformed, expected_transformed)
|
|
104
124
|
assert len(reverse_transform['cc']) == 5
|
|
105
|
-
assert reverse_transform['cc'].isna().sum() ==
|
|
125
|
+
assert reverse_transform['cc'].isna().sum() == 1
|
|
106
126
|
|
|
107
127
|
|
|
108
128
|
def test_anonymizedfaker_enforce_uniqueness():
|
|
@@ -90,6 +90,47 @@ class TestUnixTimestampEncoder:
|
|
|
90
90
|
pd.testing.assert_frame_equal(expected_transformed, transformed)
|
|
91
91
|
pd.testing.assert_frame_equal(reverted, data)
|
|
92
92
|
|
|
93
|
+
def test_unixtimestampencoder_with_integer_datetimes(self):
|
|
94
|
+
"""Test that the transformer properly handles integer columns."""
|
|
95
|
+
# Setup
|
|
96
|
+
ute = UnixTimestampEncoder('mean', True, datetime_format='%m%d%Y')
|
|
97
|
+
data = pd.DataFrame({'column': [1201992, 11022028, 10011990]})
|
|
98
|
+
|
|
99
|
+
# Run
|
|
100
|
+
ute.fit(data, column='column')
|
|
101
|
+
ute.set_random_state(np.random.RandomState(7), 'reverse_transform')
|
|
102
|
+
transformed = ute.transform(data)
|
|
103
|
+
reverted = ute.reverse_transform(transformed)
|
|
104
|
+
|
|
105
|
+
# Asserts
|
|
106
|
+
expected_transformed = pd.DataFrame({
|
|
107
|
+
'column': [6.958656e+17, 1.856736e+18, 6.547392e+17],
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
pd.testing.assert_frame_equal(expected_transformed, transformed)
|
|
111
|
+
pd.testing.assert_frame_equal(reverted, data)
|
|
112
|
+
|
|
113
|
+
def test_unixtimestampencoder_with_nans(self):
|
|
114
|
+
"""Test that the transformer properly handles null columns."""
|
|
115
|
+
# Setup
|
|
116
|
+
ute = UnixTimestampEncoder('mean', True)
|
|
117
|
+
data = pd.DataFrame({'column': [np.nan, np.nan, np.nan]})
|
|
118
|
+
|
|
119
|
+
# Run
|
|
120
|
+
ute.fit(data, column='column')
|
|
121
|
+
ute.set_random_state(np.random.RandomState(7), 'reverse_transform')
|
|
122
|
+
transformed = ute.transform(data)
|
|
123
|
+
reverted = ute.reverse_transform(transformed)
|
|
124
|
+
|
|
125
|
+
# Asserts
|
|
126
|
+
expected_transformed = pd.DataFrame({
|
|
127
|
+
'column': [0., 0., 0.],
|
|
128
|
+
'column.is_null': [1., 1., 1.]
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
pd.testing.assert_frame_equal(expected_transformed, transformed)
|
|
132
|
+
pd.testing.assert_frame_equal(reverted, data)
|
|
133
|
+
|
|
93
134
|
|
|
94
135
|
class TestOptimizedTimestampEncoder:
|
|
95
136
|
def test_optimizedtimestampencoder(self):
|
|
@@ -220,6 +220,23 @@ class TestAnonymizedFaker:
|
|
|
220
220
|
assert instance.locales is None
|
|
221
221
|
assert mock_faker.Faker.called_once_with(None)
|
|
222
222
|
assert instance.enforce_uniqueness is False
|
|
223
|
+
assert instance.missing_value_generation == 'random'
|
|
224
|
+
|
|
225
|
+
def test___init__error_missing_value_generation(self):
|
|
226
|
+
"""Test that the ``__init__`` raises an error if the missing value generation is invalid.
|
|
227
|
+
|
|
228
|
+
Test that the ``__init__`` raises an error if the missing value generation is invalid.
|
|
229
|
+
|
|
230
|
+
Side effects:
|
|
231
|
+
- ``ValueError`` is raised.
|
|
232
|
+
"""
|
|
233
|
+
expected_message = (
|
|
234
|
+
"Missing value generation 'invalid' is not supported "
|
|
235
|
+
"for AnonymizedFaker. Please use either 'random' or None."
|
|
236
|
+
)
|
|
237
|
+
# Run and Assert
|
|
238
|
+
with pytest.raises(TransformerInputError, match=expected_message):
|
|
239
|
+
AnonymizedFaker(missing_value_generation='invalid')
|
|
223
240
|
|
|
224
241
|
@patch('rdt.transformers.pii.anonymizer.faker')
|
|
225
242
|
@patch('rdt.transformers.pii.anonymizer.AnonymizedFaker.check_provider_function')
|
|
@@ -312,15 +329,16 @@ class TestAnonymizedFaker:
|
|
|
312
329
|
"""
|
|
313
330
|
# Setup
|
|
314
331
|
transformer = AnonymizedFaker()
|
|
315
|
-
columns_data = pd.Series(['1', '2', '3'])
|
|
332
|
+
columns_data = pd.Series(['1', '2', '3', None, np.nan])
|
|
316
333
|
transformer.columns = ['col']
|
|
317
334
|
|
|
318
335
|
# Run
|
|
319
336
|
transformer._fit(columns_data)
|
|
320
337
|
|
|
321
338
|
# Assert
|
|
322
|
-
assert transformer.data_length ==
|
|
339
|
+
assert transformer.data_length == 5
|
|
323
340
|
assert transformer.output_properties == {None: {'next_transformer': None}}
|
|
341
|
+
assert transformer._nan_frequency == 0.4
|
|
324
342
|
|
|
325
343
|
def test__transform(self):
|
|
326
344
|
"""Test the ``_transform`` method.
|
|
@@ -377,6 +395,26 @@ class TestAnonymizedFaker:
|
|
|
377
395
|
assert function.call_args_list == [call(), call(), call()]
|
|
378
396
|
np.testing.assert_array_equal(result, np.array(['a', 'b', 'c']))
|
|
379
397
|
|
|
398
|
+
def test__reverse_transform_with_nans(self):
|
|
399
|
+
"""Test that ``_reverse_transform`` generates NaNs."""
|
|
400
|
+
# Setup
|
|
401
|
+
instance = AnonymizedFaker()
|
|
402
|
+
instance.data_length = 4
|
|
403
|
+
instance._nan_frequency = 0.25
|
|
404
|
+
function = Mock()
|
|
405
|
+
function.side_effect = ['a', 'b', 'c', 'd']
|
|
406
|
+
|
|
407
|
+
instance._function = function
|
|
408
|
+
|
|
409
|
+
# Run
|
|
410
|
+
result = instance._reverse_transform(None)
|
|
411
|
+
result = pd.Series(result)
|
|
412
|
+
|
|
413
|
+
# Assert
|
|
414
|
+
assert function.call_args_list == [call(), call(), call(), call()]
|
|
415
|
+
assert instance.missing_value_generation == 'random'
|
|
416
|
+
assert result.isna().sum() == 1
|
|
417
|
+
|
|
380
418
|
def test__reverse_transform_not_enough_unique_values(self):
|
|
381
419
|
"""Test the ``_reverse_transform`` method.
|
|
382
420
|
|
|
@@ -430,6 +430,37 @@ class TestUnixTimestampEncoder:
|
|
|
430
430
|
if 'windows' not in platform.system().lower():
|
|
431
431
|
pd.testing.assert_series_equal(output, expected)
|
|
432
432
|
|
|
433
|
+
def test__reverse_transform_datetime_format_with_nans(self):
|
|
434
|
+
"""Test the ``_reverse_transform`` method returns the correct datetime format with nans."""
|
|
435
|
+
# Setup
|
|
436
|
+
ute = UnixTimestampEncoder()
|
|
437
|
+
ute.datetime_format = '%b %d, %Y'
|
|
438
|
+
transformed = np.array([1.5778368e+18, 1.5805152e+18, np.nan])
|
|
439
|
+
ute._dtype = 'object'
|
|
440
|
+
ute.null_transformer = NullTransformer('mean')
|
|
441
|
+
|
|
442
|
+
# Run
|
|
443
|
+
output = ute._reverse_transform(transformed)
|
|
444
|
+
|
|
445
|
+
# Assert
|
|
446
|
+
expected = pd.Series(['Jan 01, 2020', 'Feb 01, 2020', np.nan])
|
|
447
|
+
pd.testing.assert_series_equal(output, expected)
|
|
448
|
+
|
|
449
|
+
def test__reverse_transform_only_nans(self):
|
|
450
|
+
"""Test the ``_reverse_transform`` method returns the correct datetime format with nans."""
|
|
451
|
+
# Setup
|
|
452
|
+
ute = UnixTimestampEncoder()
|
|
453
|
+
transformed = np.array([np.nan, np.nan, np.nan])
|
|
454
|
+
ute._dtype = 'float'
|
|
455
|
+
ute.null_transformer = NullTransformer('mean')
|
|
456
|
+
|
|
457
|
+
# Run
|
|
458
|
+
output = ute._reverse_transform(transformed)
|
|
459
|
+
|
|
460
|
+
# Assert
|
|
461
|
+
expected = pd.Series([np.nan, np.nan, np.nan])
|
|
462
|
+
pd.testing.assert_series_equal(output, expected)
|
|
463
|
+
|
|
433
464
|
|
|
434
465
|
class TestOptimizedTimestampEncoder:
|
|
435
466
|
|
|
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
|
|
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
|
|
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
|
|
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
|