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.
Files changed (88) hide show
  1. {rdt-1.5.1.dev0 → rdt-1.6.0}/HISTORY.md +24 -0
  2. {rdt-1.5.1.dev0 → rdt-1.6.0}/PKG-INFO +26 -3
  3. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/__init__.py +1 -1
  4. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/addons/addons_setup.py +1 -2
  5. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/datetime.py +27 -5
  6. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/pii/anonymizer.py +25 -1
  7. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/PKG-INFO +26 -3
  8. {rdt-1.5.1.dev0 → rdt-1.6.0}/setup.cfg +1 -1
  9. {rdt-1.5.1.dev0 → rdt-1.6.0}/setup.py +2 -3
  10. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/pii/test_anonymizer.py +21 -1
  11. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_datetime.py +41 -0
  12. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/pii/test_anonymizer.py +40 -2
  13. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_datetime.py +31 -0
  14. {rdt-1.5.1.dev0 → rdt-1.6.0}/AUTHORS.rst +0 -0
  15. {rdt-1.5.1.dev0 → rdt-1.6.0}/CONTRIBUTING.rst +0 -0
  16. {rdt-1.5.1.dev0 → rdt-1.6.0}/LICENSE +0 -0
  17. {rdt-1.5.1.dev0 → rdt-1.6.0}/MANIFEST.in +0 -0
  18. {rdt-1.5.1.dev0 → rdt-1.6.0}/README.md +0 -0
  19. {rdt-1.5.1.dev0 → rdt-1.6.0}/RELEASE.md +0 -0
  20. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/errors.py +0 -0
  21. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/hyper_transformer.py +0 -0
  22. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/__init__.py +0 -0
  23. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/__init__.py +0 -0
  24. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/base.py +0 -0
  25. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/boolean.py +0 -0
  26. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/categorical.py +0 -0
  27. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/datetime.py +0 -0
  28. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/numerical.py +0 -0
  29. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/pii.py +0 -0
  30. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/text.py +0 -0
  31. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/datasets/utils.py +0 -0
  32. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/performance.py +0 -0
  33. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/performance/profiling.py +0 -0
  34. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/__init__.py +0 -0
  35. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/base.py +0 -0
  36. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/boolean.py +0 -0
  37. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/categorical.py +0 -0
  38. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/null.py +0 -0
  39. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/numerical.py +0 -0
  40. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/pii/__init__.py +0 -0
  41. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/pii/utils.py +0 -0
  42. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/text.py +0 -0
  43. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt/transformers/utils.py +0 -0
  44. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/SOURCES.txt +0 -0
  45. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/dependency_links.txt +0 -0
  46. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/not-zip-safe +0 -0
  47. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/requires.txt +0 -0
  48. {rdt-1.5.1.dev0 → rdt-1.6.0}/rdt.egg-info/top_level.txt +0 -0
  49. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/__init__.py +0 -0
  50. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/code_style.py +0 -0
  51. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/contributing.py +0 -0
  52. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/datasets/tests/test_boolean.py +0 -0
  53. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/datasets/tests/test_categorical.py +0 -0
  54. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/datasets/tests/test_datetime.py +0 -0
  55. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/datasets/tests/test_numerical.py +0 -0
  56. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/datasets/tests/test_utils.py +0 -0
  57. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/__init__.py +0 -0
  58. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/test_hyper_transformer.py +0 -0
  59. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/test_transformers.py +0 -0
  60. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/__init__.py +0 -0
  61. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/pii/__init__.py +0 -0
  62. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_base.py +0 -0
  63. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_boolean.py +0 -0
  64. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_categorical.py +0 -0
  65. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_numerical.py +0 -0
  66. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/integration/transformers/test_text.py +0 -0
  67. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/performance/README.md +0 -0
  68. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/performance/__init__.py +0 -0
  69. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/performance/test_performance.py +0 -0
  70. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/performance/tests/__init__.py +0 -0
  71. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/performance/tests/test_profiling.py +0 -0
  72. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/__init__.py +0 -0
  73. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/test___init__.py +0 -0
  74. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/test_hyper_transformer.py +0 -0
  75. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/__init__.py +0 -0
  76. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/addons/__init__.py +0 -0
  77. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/addons/identity/__init__.py +0 -0
  78. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/addons/identity/test_identity.py +0 -0
  79. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/pii/__init__.py +0 -0
  80. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/pii/test_utils.py +0 -0
  81. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test___init__.py +0 -0
  82. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_base.py +0 -0
  83. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_boolean.py +0 -0
  84. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_categorical.py +0 -0
  85. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_null.py +0 -0
  86. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_numerical.py +0 -0
  87. {rdt-1.5.1.dev0 → rdt-1.6.0}/tests/unit/transformers/test_text.py +0 -0
  88. {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.5.1.dev0
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.7,<3.12
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.
@@ -5,7 +5,7 @@
5
5
 
6
6
  __author__ = 'DataCebo, Inc.'
7
7
  __email__ = 'info@sdv.dev'
8
- __version__ = '1.5.1.dev0'
8
+ __version__ = '1.6.0'
9
9
 
10
10
 
11
11
  import sys
@@ -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.7,<3.12',
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
- if data.dtype == 'object':
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 == 'object':
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.5.1.dev0
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.7,<3.12
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.
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 1.5.1.dev0
2
+ current_version = 1.6.0
3
3
  commit = True
4
4
  tag = True
5
5
  parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\.(?P<release>[a-z]+)(?P<candidate>\d+))?
@@ -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.7,<3.12',
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.5.1.dev0',
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() == 0
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 == 3
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