sdv 1.38.5.dev1__tar.gz → 2.0.0.dev0__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 (93) hide show
  1. {sdv-1.38.5.dev1/sdv.egg-info → sdv-2.0.0.dev0}/PKG-INFO +4 -5
  2. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/README.md +2 -2
  3. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/pyproject.toml +2 -3
  4. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/__init__.py +1 -5
  5. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/_utils.py +165 -29
  6. sdv-1.38.5.dev1/sdv/constraints/utils.py → sdv-2.0.0.dev0/sdv/cag/_utils.py +280 -69
  7. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/cag/base.py +13 -8
  8. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/cag/fixed_combinations.py +5 -4
  9. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/cag/inequality.py +5 -8
  10. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/cag/one_hot_encoding.py +11 -12
  11. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/cag/range.py +8 -11
  12. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/data_processing/data_processor.py +45 -101
  13. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/data_processing/numerical_formatter.py +8 -21
  14. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/datasets/demo.py +33 -42
  15. sdv-2.0.0.dev0/sdv/evaluation/__init__.py +20 -0
  16. sdv-2.0.0.dev0/sdv/evaluation/_visualization.py +266 -0
  17. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/logging/sdv_logger_config.yml +14 -14
  18. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/metadata/__init__.py +0 -4
  19. sdv-1.38.5.dev1/sdv/metadata/single_table.py → sdv-2.0.0.dev0/sdv/metadata/_single_table.py +386 -105
  20. sdv-1.38.5.dev1/sdv/metadata/multi_table.py → sdv-2.0.0.dev0/sdv/metadata/metadata.py +445 -114
  21. sdv-2.0.0.dev0/sdv/metadata/metadata_upgrader.py +63 -0
  22. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/metadata/utils.py +6 -12
  23. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/metadata/visualization.py +1 -1
  24. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/multi_table/base.py +138 -137
  25. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/multi_table/hma.py +5 -8
  26. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/multi_table/utils.py +13 -13
  27. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/sampling/hierarchical_sampler.py +90 -14
  28. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/sampling/independent_sampler.py +35 -8
  29. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/sequential/par.py +128 -82
  30. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/single_table/base.py +153 -215
  31. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/single_table/copulagan.py +2 -9
  32. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/single_table/copulas.py +1 -2
  33. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/single_table/ctgan.py +8 -21
  34. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/single_table/dayz.py +2 -2
  35. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/single_table/utils.py +40 -1
  36. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/utils/__init__.py +5 -1
  37. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/utils/utils.py +132 -4
  38. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0/sdv.egg-info}/PKG-INFO +4 -5
  39. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv.egg-info/SOURCES.txt +3 -14
  40. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv.egg-info/requires.txt +1 -2
  41. sdv-1.38.5.dev1/sdv/cag/_utils.py +0 -299
  42. sdv-1.38.5.dev1/sdv/constraints/__init__.py +0 -31
  43. sdv-1.38.5.dev1/sdv/constraints/base.py +0 -524
  44. sdv-1.38.5.dev1/sdv/constraints/errors.py +0 -38
  45. sdv-1.38.5.dev1/sdv/constraints/tabular.py +0 -1502
  46. sdv-1.38.5.dev1/sdv/evaluation/__init__.py +0 -12
  47. sdv-1.38.5.dev1/sdv/evaluation/_utils.py +0 -35
  48. sdv-1.38.5.dev1/sdv/evaluation/multi_table.py +0 -212
  49. sdv-1.38.5.dev1/sdv/evaluation/single_table.py +0 -206
  50. sdv-1.38.5.dev1/sdv/lite/__init__.py +0 -5
  51. sdv-1.38.5.dev1/sdv/lite/single_table.py +0 -223
  52. sdv-1.38.5.dev1/sdv/metadata/metadata.py +0 -549
  53. sdv-1.38.5.dev1/sdv/metadata/metadata_upgrader.py +0 -312
  54. sdv-1.38.5.dev1/sdv/utils/poc.py +0 -141
  55. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/LICENSE +0 -0
  56. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/cag/__init__.py +0 -0
  57. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/cag/_errors.py +0 -0
  58. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/cag/fixed_increments.py +0 -0
  59. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/cag/programmable_constraint.py +0 -0
  60. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/data_processing/__init__.py +0 -0
  61. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/data_processing/datetime_formatter.py +0 -0
  62. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/data_processing/errors.py +0 -0
  63. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/data_processing/utils.py +0 -0
  64. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/datasets/__init__.py +0 -0
  65. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/datasets/_utils.py +0 -0
  66. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/datasets/local.py +0 -0
  67. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/errors.py +0 -0
  68. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/evaluation/evaluation.py +0 -0
  69. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/evaluation/utils.py +0 -0
  70. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/io/__init__.py +0 -0
  71. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/io/local/__init__.py +0 -0
  72. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/io/local/local.py +0 -0
  73. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/logging/__init__.py +0 -0
  74. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/logging/logger.py +0 -0
  75. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/logging/utils.py +0 -0
  76. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/metadata/errors.py +0 -0
  77. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/metrics/__init__.py +0 -0
  78. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/metrics/demos.py +0 -0
  79. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/metrics/relational.py +0 -0
  80. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/metrics/tabular.py +0 -0
  81. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/metrics/timeseries.py +0 -0
  82. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/multi_table/__init__.py +0 -0
  83. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/multi_table/dayz.py +0 -0
  84. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/sampling/__init__.py +0 -0
  85. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/sampling/tabular.py +0 -0
  86. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/sequential/__init__.py +0 -0
  87. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/single_table/__init__.py +0 -0
  88. {sdv-1.38.5.dev1/sdv/utils → sdv-2.0.0.dev0/sdv/single_table}/mixins.py +0 -0
  89. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv/version/__init__.py +0 -0
  90. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv.egg-info/dependency_links.txt +0 -0
  91. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv.egg-info/entry_points.txt +0 -0
  92. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/sdv.egg-info/top_level.txt +0 -0
  93. {sdv-1.38.5.dev1 → sdv-2.0.0.dev0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sdv
3
- Version: 1.38.5.dev1
3
+ Version: 2.0.0.dev0
4
4
  Summary: Generate synthetic data for single table, multi table and sequential data
5
5
  Author-email: "DataCebo, Inc." <info@sdv.dev>
6
6
  License-Expression: BUSL-1.1
@@ -46,8 +46,7 @@ Requires-Dist: ctgan>=0.11.1; python_version < "3.14"
46
46
  Requires-Dist: ctgan>=0.12.0; python_version >= "3.14"
47
47
  Requires-Dist: deepecho>=0.7.0; python_version < "3.14"
48
48
  Requires-Dist: deepecho>=0.8.0; python_version >= "3.14"
49
- Requires-Dist: rdt<2,>=1.18.2; python_version < "3.14"
50
- Requires-Dist: rdt<2,>=1.20.0; python_version >= "3.14"
49
+ Requires-Dist: rdt>=2.0.0.dev0
51
50
  Requires-Dist: sdmetrics>=0.32.0
52
51
  Requires-Dist: platformdirs>=4.0
53
52
  Requires-Dist: pyyaml>=6.0.1
@@ -215,7 +214,7 @@ The SDV library allows you to evaluate the synthetic data by comparing it to the
215
214
  started by generating a quality report.
216
215
 
217
216
  ```python
218
- from sdv.evaluation.single_table import evaluate_quality
217
+ from sdv.evaluation import evaluate_quality
219
218
 
220
219
  quality_report = evaluate_quality(real_data, synthetic_data, metadata)
221
220
  ```
@@ -236,7 +235,7 @@ This object computes an overall quality score on a scale of 0 to 100% (100 being
236
235
  as detailed breakdowns. For more insights, you can also visualize the synthetic vs. real data.
237
236
 
238
237
  ```python
239
- from sdv.evaluation.single_table import get_column_plot
238
+ from sdv.evaluation import get_column_plot
240
239
 
241
240
  fig = get_column_plot(
242
241
  real_data=real_data,
@@ -119,7 +119,7 @@ The SDV library allows you to evaluate the synthetic data by comparing it to the
119
119
  started by generating a quality report.
120
120
 
121
121
  ```python
122
- from sdv.evaluation.single_table import evaluate_quality
122
+ from sdv.evaluation import evaluate_quality
123
123
 
124
124
  quality_report = evaluate_quality(real_data, synthetic_data, metadata)
125
125
  ```
@@ -140,7 +140,7 @@ This object computes an overall quality score on a scale of 0 to 100% (100 being
140
140
  as detailed breakdowns. For more insights, you can also visualize the synthetic vs. real data.
141
141
 
142
142
  ```python
143
- from sdv.evaluation.single_table import get_column_plot
143
+ from sdv.evaluation import get_column_plot
144
144
 
145
145
  fig = get_column_plot(
146
146
  real_data=real_data,
@@ -44,8 +44,7 @@ dependencies = [
44
44
  "ctgan>=0.12.0;python_version>='3.14'",
45
45
  "deepecho>=0.7.0;python_version<'3.14'",
46
46
  "deepecho>=0.8.0;python_version>='3.14'",
47
- "rdt>=1.18.2,<2;python_version<'3.14'",
48
- "rdt>=1.20.0,<2;python_version>='3.14'",
47
+ "rdt>=2.0.0.dev0",
49
48
  "sdmetrics>=0.32.0",
50
49
  'platformdirs>=4.0',
51
50
  'pyyaml>=6.0.1',
@@ -149,7 +148,7 @@ namespaces = false
149
148
  version = {attr = 'sdv.__version__'}
150
149
 
151
150
  [tool.bumpversion]
152
- current_version = "1.38.5.dev1"
151
+ current_version = "2.0.0.dev0"
153
152
  parse = '(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\.(?P<release>[a-z]+)(?P<candidate>\d+))?'
154
153
  serialize = [
155
154
  '{major}.{minor}.{patch}.{release}{candidate}',
@@ -6,7 +6,7 @@
6
6
 
7
7
  __author__ = 'DataCebo, Inc.'
8
8
  __email__ = 'info@sdv.dev'
9
- __version__ = '1.38.5.dev1'
9
+ __version__ = '2.0.0.dev0'
10
10
 
11
11
 
12
12
  import sys
@@ -16,12 +16,10 @@ from operator import attrgetter
16
16
  from types import ModuleType
17
17
 
18
18
  from sdv import (
19
- constraints,
20
19
  data_processing,
21
20
  datasets,
22
21
  evaluation,
23
22
  io,
24
- lite,
25
23
  logging,
26
24
  metadata,
27
25
  metrics,
@@ -34,12 +32,10 @@ from sdv import (
34
32
  )
35
33
 
36
34
  __all__ = [
37
- 'constraints',
38
35
  'data_processing',
39
36
  'datasets',
40
37
  'evaluation',
41
38
  'io',
42
- 'lite',
43
39
  'logging',
44
40
  'metadata',
45
41
  'metrics',
@@ -9,13 +9,14 @@ from collections.abc import Iterable
9
9
  from datetime import datetime
10
10
  from pathlib import Path
11
11
 
12
+ import numpy as np
12
13
  import pandas as pd
13
14
  from pandas.api.types import is_float, is_integer
14
15
  from pandas.core.tools.datetimes import _guess_datetime_format_for_array
15
16
  from rdt.transformers.utils import _GENERATORS, strings_from_regex
16
17
 
17
18
  from sdv import version
18
- from sdv.errors import SDVVersionWarning, SynthesizerInputError, VersionError
19
+ from sdv.errors import InvalidDataTypeError, SDVVersionWarning, SynthesizerInputError, VersionError
19
20
 
20
21
  try:
21
22
  from re import _parser as sre_parse
@@ -23,7 +24,7 @@ except ImportError:
23
24
  import sre_parse
24
25
 
25
26
 
26
- MODELABLE_SDTYPES = ['categorical', 'numerical', 'datetime', 'boolean']
27
+ MODELABLE_SDTYPES = ['categorical', 'ordinal', 'numerical', 'datetime', 'boolean']
27
28
 
28
29
 
29
30
  def _cast_to_iterable(value, iterable_type=None):
@@ -161,6 +162,82 @@ def _datetime_string_matches_format(value, datetime_format):
161
162
  return False
162
163
 
163
164
 
165
+ def _parse_datetime(value, datetime_format, ignore_timezone):
166
+ is_series = isinstance(value, pd.Series)
167
+ parsed_value = pd.to_datetime(value, format=datetime_format, errors='coerce')
168
+
169
+ if is_series and ignore_timezone and hasattr(parsed_value, 'dt'):
170
+ if hasattr(parsed_value.dt, 'tz_localize'):
171
+ parsed_value = parsed_value.dt.tz_localize(None)
172
+
173
+ elif ignore_timezone and hasattr(parsed_value, 'tz_localize'):
174
+ if isinstance(parsed_value, (list, tuple, pd.Series, np.ndarray)):
175
+ parsed_value = [
176
+ new_value.replace(tzinfo=None)
177
+ if isinstance(new_value, datetime)
178
+ else new_value.tz_localize(None)
179
+ for new_value in parsed_value
180
+ ]
181
+
182
+ else:
183
+ parsed_value = parsed_value.tz_localize(None)
184
+
185
+ if is_series and not isinstance(parsed_value, pd.Series):
186
+ return pd.Series(parsed_value)
187
+
188
+ return parsed_value
189
+
190
+
191
+ def _parse_datetime64_value(value, datetime_format=None, ignore_timezone=True):
192
+ """Parse a single value into `datetime64`, optionally ignoring timezone."""
193
+ if pd.isna(value):
194
+ return pd.NaT.to_datetime64()
195
+
196
+ return _parse_datetime(value, datetime_format, ignore_timezone).to_datetime64()
197
+
198
+
199
+ def _cast_to_datetime64(value, datetime_format=None, ignore_timezone=True):
200
+ """Cast a given value to a ``numpy.datetime64`` format.
201
+
202
+ Args:
203
+ value (pandas.Series, np.ndarray, list, or str):
204
+ Input data to convert.
205
+ datetime_format (str, optional):
206
+ Datetime format of the `value`.
207
+ ignore_timezone (bool):
208
+ If True, strips `%z` or `%Z` from the format and removes tzinfo.
209
+
210
+ Returns:
211
+ numpy.datetime64, pandas.Series, or numpy.ndarray of datetime64
212
+ """
213
+ if datetime_format:
214
+ datetime_format = datetime_format.replace('%#', '%').replace('%-', '%')
215
+
216
+ if isinstance(value, str):
217
+ return _parse_datetime64_value(value, datetime_format, ignore_timezone)
218
+
219
+ elif isinstance(value, pd.Series):
220
+ return pd.Series(
221
+ [
222
+ _parse_datetime64_value(
223
+ val,
224
+ datetime_format,
225
+ ignore_timezone,
226
+ )
227
+ for val in value
228
+ ],
229
+ index=value.index,
230
+ dtype='datetime64[ns]',
231
+ )
232
+
233
+ elif isinstance(value, (np.ndarray, list)):
234
+ return np.array([
235
+ _parse_datetime64_value(val, datetime_format, ignore_timezone) for val in value
236
+ ])
237
+
238
+ return _parse_datetime64_value(value, datetime_format, ignore_timezone)
239
+
240
+
164
241
  def _convert_to_timedelta(column):
165
242
  """Convert a ``pandas.Series`` to one with dtype ``timedelta``.
166
243
 
@@ -266,7 +343,7 @@ def check_sdv_versions_and_warn(synthesizer):
266
343
  """Check if the current SDV and SDV Enterprise versions mismatch.
267
344
 
268
345
  Args:
269
- synthesizer (BaseSynthesizer or BaseMultiTableSynthesizer):
346
+ synthesizer (BaseSynthesizer):
270
347
  An SDV model instance to check versions against.
271
348
 
272
349
  Raises:
@@ -359,7 +436,7 @@ def check_synthesizer_version(synthesizer, is_fit_method=False, compare_operator
359
436
  """Check if the current synthesizer version is greater than the package version.
360
437
 
361
438
  Args:
362
- synthesizer (BaseSynthesizer or BaseMultiTableSynthesizer):
439
+ synthesizer (BaseSynthesizer):
363
440
  An SDV model instance to check versions against.
364
441
  is_fit_method (bool):
365
442
  Whether or not this function is being called by a ``fit`` function.
@@ -435,7 +512,7 @@ def generate_synthesizer_id(synthesizer):
435
512
  and the last part of a UUID4 composed by 36 random characters.
436
513
 
437
514
  Args:
438
- synthesizer (BaseSynthesizer or BaseMultiTableSynthesizer):
515
+ synthesizer (BaseSynthesizer):
439
516
  An SDV model instance to check versions against.
440
517
 
441
518
  Returns:
@@ -489,11 +566,7 @@ def _is_numerical(value):
489
566
  def _get_transformer_init_kwargs(transformer):
490
567
  """Get the dict of arguments used to instantiate the given transformer."""
491
568
  args = inspect.getfullargspec(transformer.__init__).args[1:]
492
- return {
493
- key: getattr(transformer, key)
494
- for key in args
495
- if key != 'model_missing_values' and hasattr(transformer, key)
496
- }
569
+ return {key: getattr(transformer, key) for key in args if hasattr(transformer, key)}
497
570
 
498
571
 
499
572
  def _check_regex_format(table_name, column_name, regex):
@@ -509,25 +582,6 @@ def _check_regex_format(table_name, column_name, regex):
509
582
  ) from e
510
583
 
511
584
 
512
- def warn_load_deprecated():
513
- """Warn that the `load` function is deprecated."""
514
- warnings.warn(
515
- "The 'load' function will be deprecated in future versions of SDV. Please use"
516
- " 'utils.load_synthesizer' instead.",
517
- FutureWarning,
518
- )
519
-
520
-
521
- def warn_set_constraints_deprecated():
522
- """Warn that the `set_constraints` method on synthesizer is deprecated."""
523
- deprecation_msg = (
524
- 'Warning: The `set_constraints` method is deprecated. '
525
- 'Please use the `load_constraints` utility function to load constraints from a file '
526
- 'and add them to the synthesizer with the `add_constraints` method.'
527
- )
528
- warnings.warn(deprecation_msg, FutureWarning)
529
-
530
-
531
585
  def _validate_correct_synthesizer_loading(synthesizer, cls):
532
586
  """Validate that the loaded synthesizer is of the correct type."""
533
587
  synthesizer_name = synthesizer.__class__.__name__
@@ -574,3 +628,85 @@ def _check_is_dict_of_dataframes(data, arg_name='data'):
574
628
  for table_name, table in data.items():
575
629
  if not isinstance(table, pd.DataFrame):
576
630
  raise ValueError(error_message_data)
631
+
632
+
633
+ def _column_range_exceeds_real(column, col_meta):
634
+ range_is_nullable = col_meta.get('range_is_nullable', False)
635
+ if not any(pd.isna(column)) and range_is_nullable:
636
+ return True
637
+
638
+ column = column.dropna()
639
+ if col_meta['sdtype'] in ['datetime', 'numerical']:
640
+ range_min = col_meta.get('range_min')
641
+ range_max = col_meta.get('range_max')
642
+ if range_min is None and range_max is None:
643
+ return False
644
+ elif column.empty:
645
+ return True
646
+
647
+ if col_meta['sdtype'] == 'datetime':
648
+ datetime_format = col_meta.get('datetime_format')
649
+ column = _cast_to_datetime64(column, datetime_format)
650
+ if range_min is not None:
651
+ range_min = _cast_to_datetime64(range_min, datetime_format)
652
+
653
+ if range_max is not None:
654
+ range_max = _cast_to_datetime64(range_max, datetime_format)
655
+
656
+ min_bound_met = any(column.dropna() <= range_min) if range_min is not None else True
657
+ max_bound_met = any(column.dropna() >= range_max) if range_max is not None else True
658
+ return not (min_bound_met and max_bound_met)
659
+
660
+ elif col_meta['sdtype'] in ['categorical', 'ordinal']:
661
+ if 'range_values' not in col_meta:
662
+ return False
663
+
664
+ if not set(col_meta['range_values']).issubset(set(column.unique())):
665
+ return True
666
+
667
+ return False
668
+
669
+
670
+ def _metadata_range_exceeds_real(data, metadata):
671
+ """Returns whether the metadata range information exceeds the real data range."""
672
+ for table_name, table_data in data.items():
673
+ for column in table_data.columns:
674
+ col_meta = metadata.tables[table_name].columns[column]
675
+ if _column_range_exceeds_real(table_data[column], col_meta):
676
+ return True
677
+
678
+ return False
679
+
680
+
681
+ def _validate_data_single_table(data, table_name=None):
682
+ """Validate that the data is a dictionary with a single table."""
683
+ if len(data) != 1:
684
+ raise InvalidDataTypeError(
685
+ 'The `data` parameter must be a dictionary containing exactly one table name '
686
+ 'mapped to a pandas DataFrame.'
687
+ )
688
+
689
+ if table_name is not None and table_name not in data:
690
+ raise InvalidDataTypeError(
691
+ f"The specified table name '{table_name}' is not present in the data."
692
+ )
693
+
694
+
695
+ def _get_single_table_data(data):
696
+ """Return the single table DataFrame from the data dictionary."""
697
+ _validate_data_single_table(data)
698
+ return next(iter(data.values()))
699
+
700
+
701
+ def _validate_positive_integer(parameter_name, value):
702
+ """Validate that a parameter is a positive integer."""
703
+ if (
704
+ not isinstance(value, (int, float))
705
+ or value <= 0
706
+ or not float(value).is_integer()
707
+ or isinstance(value, bool)
708
+ ):
709
+ raise SynthesizerInputError(
710
+ f"Invalid parameter for '{parameter_name}' ({value}). "
711
+ 'Please provide an integer that is greater than 0.'
712
+ )