sdgym 0.16.1.dev0__tar.gz → 0.16.2.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 (62) hide show
  1. {sdgym-0.16.1.dev0/sdgym.egg-info → sdgym-0.16.2.dev0}/PKG-INFO +4 -5
  2. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/pyproject.toml +4 -5
  3. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/__init__.py +1 -1
  4. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark/credentials_utils.py +1 -1
  5. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_dataset_utils.py +8 -8
  6. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/benchmark.py +18 -31
  7. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/datasets.py +20 -11
  8. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/base.py +2 -3
  9. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/column.py +9 -7
  10. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/data_designer.py +7 -5
  11. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/realtabformer.py +5 -2
  12. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/sdv.py +1 -1
  13. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/tabddpm.py +5 -2
  14. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/uniform.py +14 -10
  15. sdgym-0.16.2.dev0/sdgym/synthesizers/utils.py +84 -0
  16. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/utils.py +17 -0
  17. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0/sdgym.egg-info}/PKG-INFO +4 -5
  18. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym.egg-info/requires.txt +3 -4
  19. sdgym-0.16.1.dev0/sdgym/synthesizers/utils.py +0 -37
  20. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/LICENSE +0 -0
  21. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/README.md +0 -0
  22. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark/__init__.py +0 -0
  23. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark/benchmark.py +0 -0
  24. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark_launcher/__init__.py +0 -0
  25. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark_launcher/_instance_manager.py +0 -0
  26. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark_launcher/_storage_manager.py +0 -0
  27. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark_launcher/_validation.py +0 -0
  28. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark_launcher/benchmark_base.yaml +0 -0
  29. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark_launcher/benchmark_config.py +0 -0
  30. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark_launcher/benchmark_launcher.py +0 -0
  31. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark_launcher/script.py +0 -0
  32. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/_benchmark_launcher/utils.py +0 -0
  33. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/cli/__init__.py +0 -0
  34. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/cli/__main__.py +0 -0
  35. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/cli/collect.py +0 -0
  36. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/cli/summary.py +0 -0
  37. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/cli/utils.py +0 -0
  38. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/dataset_explorer.py +0 -0
  39. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/errors.py +0 -0
  40. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/metrics.py +0 -0
  41. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/progress.py +0 -0
  42. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/result_explorer/__init__.py +0 -0
  43. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/result_explorer/result_explorer.py +0 -0
  44. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/result_explorer/result_handler.py +0 -0
  45. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/result_writer.py +0 -0
  46. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/run_benchmark/__init__.py +0 -0
  47. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/run_benchmark/run_benchmark.py +0 -0
  48. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/run_benchmark/upload_benchmark_results.py +0 -0
  49. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/run_benchmark/utils.py +0 -0
  50. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/s3.py +0 -0
  51. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizer_descriptions.yaml +0 -0
  52. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/__init__.py +0 -0
  53. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/clavaddpm.py +0 -0
  54. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/generate.py +0 -0
  55. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym/synthesizers/identity.py +0 -0
  56. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym.egg-info/SOURCES.txt +0 -0
  57. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym.egg-info/dependency_links.txt +0 -0
  58. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym.egg-info/entry_points.txt +0 -0
  59. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/sdgym.egg-info/top_level.txt +0 -0
  60. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/setup.cfg +0 -0
  61. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/tests/test_scripts.py +0 -0
  62. {sdgym-0.16.1.dev0 → sdgym-0.16.2.dev0}/tests/test_tasks.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sdgym
3
- Version: 0.16.1.dev0
3
+ Version: 0.16.2.dev0
4
4
  Summary: Benchmark tabular synthetic data generators using a variety of datasets
5
5
  Author-email: "DataCebo, Inc." <info@sdv.dev>
6
6
  License-Expression: BUSL-1.1
@@ -63,10 +63,9 @@ Requires-Dist: torch>=2.6.0; python_version >= "3.13" and python_version < "3.14
63
63
  Requires-Dist: torch>=2.9.0; python_version >= "3.14"
64
64
  Requires-Dist: tqdm>=4.66.3
65
65
  Requires-Dist: XlsxWriter>=1.2.8
66
- Requires-Dist: rdt<2,>=1.18.2; python_version < "3.14"
67
- Requires-Dist: rdt<2,>=1.20.0; python_version >= "3.14"
68
- Requires-Dist: sdmetrics>=0.29.0
69
- Requires-Dist: sdv<2,>=1.37.0
66
+ Requires-Dist: sdmetrics>=0.32.0
67
+ Requires-Dist: rdt<3,>=2.0.0
68
+ Requires-Dist: sdv<3,>=2.0.0
70
69
  Provides-Extra: dask
71
70
  Requires-Dist: dask; extra == "dask"
72
71
  Requires-Dist: distributed; extra == "dask"
@@ -61,10 +61,9 @@ dependencies = [
61
61
  "torch>=2.9.0;python_version>='3.14'",
62
62
  'tqdm>=4.66.3',
63
63
  'XlsxWriter>=1.2.8',
64
- "rdt>=1.18.2,<2;python_version<'3.14'",
65
- "rdt>=1.20.0,<2;python_version>='3.14'",
66
- "sdmetrics>=0.29.0",
67
- "sdv>=1.37.0,<2",
64
+ "sdmetrics>=0.32.0",
65
+ "rdt>=2.0.0,<3",
66
+ "sdv>=2.0.0,<3",
68
67
  ]
69
68
 
70
69
  [project.urls]
@@ -177,7 +176,7 @@ namespaces = false
177
176
  version = {attr = 'sdgym.__version__'}
178
177
 
179
178
  [tool.bumpversion]
180
- current_version = "0.16.1.dev0"
179
+ current_version = "0.16.2.dev0"
181
180
  parse = '(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\.(?P<release>[a-z]+)(?P<candidate>\d+))?'
182
181
  serialize = [
183
182
  '{major}.{minor}.{patch}.{release}{candidate}',
@@ -8,7 +8,7 @@ __author__ = 'DataCebo, Inc.'
8
8
  __copyright__ = 'Copyright (c) 2022 DataCebo, Inc.'
9
9
  __email__ = 'info@sdv.dev'
10
10
  __license__ = 'BSL-1.1'
11
- __version__ = '0.16.1.dev0'
11
+ __version__ = '0.16.2.dev0'
12
12
 
13
13
  import logging
14
14
 
@@ -13,5 +13,5 @@ def sdv_install_cmd(credentials):
13
13
  pip install sdv-installer
14
14
 
15
15
  python -c "from sdv_installer.installation.installer import install_packages; \\
16
- install_packages(username='{username}', license_key='{license_key}', version='0.53')"
16
+ install_packages(username='{username}', license_key='{license_key}')"
17
17
  """)
@@ -8,7 +8,7 @@ from zipfile import ZipFile
8
8
  import numpy as np
9
9
  import pandas as pd
10
10
  from sdv.metadata import Metadata
11
- from sdv.utils import poc
11
+ from sdv.utils import get_random_subset
12
12
 
13
13
  LOGGER = logging.getLogger(__name__)
14
14
 
@@ -77,7 +77,7 @@ def _get_multi_table_dataset_subset(data, metadata_dict):
77
77
  data[table_name] = data[table_name][list(table.columns)]
78
78
 
79
79
  # Subsample the data mantaining the referential integrity
80
- data = poc.get_random_subset(
80
+ data = get_random_subset(
81
81
  data=data,
82
82
  metadata=metadata,
83
83
  main_table_name=largest_table_name,
@@ -122,9 +122,12 @@ def _get_dataset_subset(data, metadata_dict, modality):
122
122
  filtered = _filter_columns(columns=columns, mandatory_columns=mandatory_columns)
123
123
 
124
124
  table_info['columns'] = filtered
125
- data = data[list(filtered)]
126
- max_rows = min(MAX_NUM_ROWS, len(data))
127
- data = data.sample(max_rows)
125
+ is_dict = isinstance(data, dict)
126
+ table_data = data[table_name] if is_dict else data
127
+ table_data = table_data[list(filtered)]
128
+ max_rows = min(MAX_NUM_ROWS, len(table_data))
129
+ table_data = table_data.sample(max_rows)
130
+ data = {table_name: table_data} if is_dict else table_data
128
131
 
129
132
  return data, metadata_dict
130
133
 
@@ -137,9 +140,6 @@ def _read_zipped_data(zip_file_path, modality):
137
140
  key = Path(file_name).stem
138
141
  data[key] = _read_csv_from_zip(zf, csv_file_name=file_name)
139
142
 
140
- if modality != 'multi_table':
141
- data = next(iter(data.values()))
142
-
143
143
  return data
144
144
 
145
145
 
@@ -25,18 +25,7 @@ import pandas as pd
25
25
  import tqdm
26
26
  import yaml
27
27
  from botocore.config import Config
28
- from sdmetrics.reports.multi_table import (
29
- DiagnosticReport as MultiTableDiagnosticReport,
30
- )
31
- from sdmetrics.reports.multi_table import (
32
- QualityReport as MultiTableQualityReport,
33
- )
34
- from sdmetrics.reports.single_table import (
35
- DiagnosticReport as SingleTableDiagnosticReport,
36
- )
37
- from sdmetrics.reports.single_table import (
38
- QualityReport as SingleTableQualityReport,
39
- )
28
+ from sdmetrics.reports import DiagnosticReport, QualityReport
40
29
  from sdmetrics.single_table import DCRBaselineProtection
41
30
 
42
31
  from sdgym.datasets import (
@@ -60,6 +49,7 @@ from sdgym.s3 import (
60
49
  from sdgym.synthesizers import MultiTableUniformSynthesizer, UniformSynthesizer
61
50
  from sdgym.synthesizers.base import BaselineSynthesizer
62
51
  from sdgym.utils import (
52
+ _get_single_table_dataframe,
63
53
  calculate_score_time,
64
54
  convert_metadata_to_sdmetrics,
65
55
  format_exception,
@@ -515,7 +505,8 @@ def _synthesize(
515
505
  if modality == 'multi_table':
516
506
  synthetic_data = sample_from_synthesizer(fitted_synthesizer, 1.0)
517
507
  else:
518
- synthetic_data = sample_from_synthesizer(fitted_synthesizer, n_samples=len(data))
508
+ num_rows = len(_get_single_table_dataframe(data))
509
+ synthetic_data = sample_from_synthesizer(fitted_synthesizer, n_samples=num_rows)
519
510
 
520
511
  sample_end = get_utc_now()
521
512
  sample_time = sample_end - train_end
@@ -530,9 +521,9 @@ def _synthesize(
530
521
  result_writer.write_zipped_dataframes(
531
522
  synthetic_data, synthesizer_path['synthetic_data']
532
523
  )
533
-
534
524
  else:
535
- result_writer.write_dataframe(synthetic_data, synthesizer_path['synthetic_data'])
525
+ synthetic_table = _get_single_table_dataframe(synthetic_data)
526
+ result_writer.write_dataframe(synthetic_table, synthesizer_path['synthetic_data'])
536
527
 
537
528
  return synthetic_data, train_time, sample_time, synthesizer_size, peak_memory
538
529
 
@@ -579,8 +570,12 @@ def _compute_scores(
579
570
  metrics = metrics or []
580
571
  if modality == 'single_table':
581
572
  sdmetrics_metadata = convert_metadata_to_sdmetrics(metadata)
573
+ sdmetrics_real_data = _get_single_table_dataframe(real_data)
574
+ sdmetrics_synthetic_data = _get_single_table_dataframe(synthetic_data)
582
575
  else:
583
576
  sdmetrics_metadata = metadata
577
+ sdmetrics_real_data = real_data
578
+ sdmetrics_synthetic_data = synthetic_data
584
579
 
585
580
  if len(metrics) > 0:
586
581
  metrics, metric_kwargs = get_metrics(metrics, modality=modality)
@@ -600,7 +595,7 @@ def _compute_scores(
600
595
  start = get_utc_now()
601
596
  try:
602
597
  LOGGER.info('Computing %s on dataset %s', metric_name, dataset_name)
603
- metric_args = (real_data, synthetic_data, sdmetrics_metadata)
598
+ metric_args = (sdmetrics_real_data, sdmetrics_synthetic_data, sdmetrics_metadata)
604
599
  score = metric.compute(*metric_args, **metric_kwargs.get(metric_name, {}))
605
600
  normalized_score = metric.normalize(score)
606
601
  except Exception:
@@ -620,23 +615,15 @@ def _compute_scores(
620
615
 
621
616
  if compute_diagnostic_score:
622
617
  start = get_utc_now()
623
- if modality == 'single_table':
624
- diagnostic_report = SingleTableDiagnosticReport()
625
- else:
626
- diagnostic_report = MultiTableDiagnosticReport()
627
-
628
- diagnostic_report.generate(real_data, synthetic_data, sdmetrics_metadata, verbose=False)
618
+ diagnostic_report = DiagnosticReport()
619
+ diagnostic_report.generate(real_data, synthetic_data, metadata, verbose=False)
629
620
  output['diagnostic_score_time'] = calculate_score_time(start)
630
621
  output['diagnostic_score'] = diagnostic_report.get_score()
631
622
 
632
623
  if compute_quality_score:
633
624
  start = get_utc_now()
634
- if modality == 'single_table':
635
- quality_report = SingleTableQualityReport()
636
- else:
637
- quality_report = MultiTableQualityReport()
638
-
639
- quality_report.generate(real_data, synthetic_data, sdmetrics_metadata, verbose=False)
625
+ quality_report = QualityReport()
626
+ quality_report.generate(real_data, synthetic_data, metadata, verbose=False)
640
627
  output['quality_score_time'] = calculate_score_time(start)
641
628
  output['quality_score'] = quality_report.get_score()
642
629
  if synthesizer_path is not None and result_writer is not None:
@@ -644,7 +631,7 @@ def _compute_scores(
644
631
 
645
632
  if compute_privacy_score:
646
633
  start = get_utc_now()
647
- num_rows = len(synthetic_data)
634
+ num_rows = len(sdmetrics_synthetic_data)
648
635
  # parameters were determined by running experiments with all datasets
649
636
  # and tweaking num_rows_subsample and num_iterations until
650
637
  # absolute difference from real score was close (while keeping runtime low)
@@ -652,8 +639,8 @@ def _compute_scores(
652
639
  num_rows_subsample = math.floor(num_rows * 0.60)
653
640
  num_iterations = 2
654
641
  score = DCRBaselineProtection.compute_breakdown(
655
- real_data=real_data,
656
- synthetic_data=synthetic_data,
642
+ real_data=sdmetrics_real_data,
643
+ synthetic_data=sdmetrics_synthetic_data,
657
644
  table_name=dataset_name,
658
645
  metadata=sdmetrics_metadata,
659
646
  num_rows_subsample=num_rows_subsample,
@@ -12,12 +12,13 @@ import pandas as pd
12
12
  from sdv.datasets.demo import (
13
13
  _find_data_zip_key,
14
14
  _get_data_from_bucket,
15
- _get_first_v1_metadata_bytes,
15
+ _get_first_v2_metadata_bytes,
16
16
  _get_metadata,
17
17
  _list_objects,
18
18
  _load_data_from_zip,
19
19
  download_demo,
20
20
  )
21
+ from sdv.metadata import Metadata
21
22
 
22
23
  from sdgym._dataset_utils import (
23
24
  _get_dataset_subset,
@@ -178,24 +179,35 @@ def get_data_and_metadata_from_path(dataset_path, modality):
178
179
  interpret and load the dataset (e.g., ``'single_table'``, ``'multi_table'``).
179
180
 
180
181
  Returns:
181
- tuple[pd.DataFrame | dict, dict | None]:
182
+ tuple[dict | None, dict | None]:
182
183
  A tuple containing:
183
- - The loaded dataset as a ``pandas.DataFrame`` or a dictionary with
184
- ``pandas.DataFrame`` if ``multi_table``.
185
- - The dataset metadata as a dictionary.
184
+ - The loaded dataset as a dictionary mapping table name to
185
+ ``pandas.DataFrame``.
186
+ - The dataset metadata as a dictionary.
186
187
  """
187
188
  metadata_dict = None
188
189
  data = None
189
190
  for file_name in dataset_path.iterdir():
190
191
  is_metadata = 'metadata' in file_name.stem and file_name.suffix == '.json'
191
- if (is_metadata and metadata_dict is None) or file_name.name == 'metadata.json':
192
+ if (is_metadata and metadata_dict is None) or file_name.name == 'metadata_v2.json':
192
193
  metadata_dict = _read_metadata_json(file_name)
193
194
  elif 'data' in file_name.stem and file_name.suffix == '.zip':
194
195
  data = _read_zipped_data(zip_file_path=(file_name), modality=modality)
195
196
 
197
+ if modality == 'single_table' and data is not None:
198
+ data = _set_single_table_as_dict(data, metadata_dict)
199
+
196
200
  return data, metadata_dict
197
201
 
198
202
 
203
+ def _set_single_table_as_dict(data, metadata_dict):
204
+ if isinstance(data, dict):
205
+ return data
206
+
207
+ table_name = Metadata.load_from_dict(metadata_dict)._get_single_table_name()
208
+ return {table_name: data}
209
+
210
+
199
211
  def _genereate_dataset_info(s3_client, bucket_name, contents):
200
212
  """Generate summarized dataset information from S3 bucket.
201
213
 
@@ -302,13 +314,10 @@ def _load_private_sdv_demo_dataset(modality, dataset_name, bucket, s3_client=Non
302
314
  contents = _list_objects(dataset_prefix, bucket=bucket_name, client=s3_client)
303
315
  data_key = _find_data_zip_key(contents, dataset_prefix, bucket_name)
304
316
  data_bytes = io.BytesIO(_get_data_from_bucket(data_key, bucket=bucket_name, client=s3_client))
305
- metadata_bytes = _get_first_v1_metadata_bytes(
317
+ metadata_bytes = _get_first_v2_metadata_bytes(
306
318
  contents, dataset_prefix, bucket=bucket_name, client=s3_client
307
319
  )
308
320
  data = _load_data_from_zip(data_bytes, bucket_name, dataset_name)
309
- if modality != 'multi_table':
310
- data = data.popitem()[1]
311
-
312
321
  metadata = _get_metadata(metadata_bytes, dataset_name)
313
322
  return data, metadata.to_dict()
314
323
 
@@ -378,7 +387,7 @@ def load_dataset(
378
387
  columns. Defauts to ``False``.
379
388
 
380
389
  Returns:
381
- pd.DataFrame | dict, dict:
390
+ tuple (dict[str, pd.DataFrame], dict):
382
391
  The data and medatata for a dataset.
383
392
  """
384
393
  s3_client = get_s3_client(
@@ -129,9 +129,8 @@ class BaselineSynthesizer(abc.ABC):
129
129
  The number of samples to create.
130
130
 
131
131
  Returns:
132
- pandas.DataFrame or dict:
133
- The sampled data. If single-table, should be a DataFrame. If multi-table,
134
- should be a dict mapping table name to DataFrame.
132
+ dict[str, pandas.DataFrame]:
133
+ The sampled data mapping table name to DataFrame.
135
134
  """
136
135
  return self._sample_from_synthesizer(synthesizer, n_samples)
137
136
 
@@ -8,6 +8,7 @@ from sdv.metadata import Metadata
8
8
  from sklearn.mixture import GaussianMixture
9
9
 
10
10
  from sdgym.synthesizers.base import BaselineSynthesizer
11
+ from sdgym.synthesizers.utils import _get_single_table_name_and_dataframe
11
12
 
12
13
  LOGGER = logging.getLogger(__name__)
13
14
 
@@ -22,12 +23,12 @@ class ColumnSynthesizer(BaselineSynthesizer):
22
23
  _MODALITY_FLAG = 'single_table'
23
24
 
24
25
  def _fit(self, data, metadata):
26
+ table_name, table_data = _get_single_table_name_and_dataframe(data, metadata)
25
27
  hyper_transformer = HyperTransformer()
26
- hyper_transformer.detect_initial_config(data)
28
+ hyper_transformer.detect_initial_config(table_data)
27
29
  supported_sdtypes = hyper_transformer._get_supported_sdtypes()
28
30
  config = {}
29
31
  if isinstance(metadata, Metadata):
30
- table_name = metadata._get_single_table_name()
31
32
  columns = metadata.tables[table_name].columns
32
33
  else:
33
34
  columns = metadata.columns
@@ -48,14 +49,14 @@ class ColumnSynthesizer(BaselineSynthesizer):
48
49
 
49
50
  # This is done to match the behavior of the synthesizer for SDGym <= 0.6.0
50
51
  columns_to_remove = [
51
- column_name for column_name, data in data.items() if data.dtype.kind in {'O', 'i'}
52
+ column_name for column_name, data in table_data.items() if data.dtype.kind in {'O', 'i'}
52
53
  ]
53
54
  hyper_transformer.remove_transformers(columns_to_remove)
54
55
 
55
- hyper_transformer.fit(data)
56
- transformed = hyper_transformer.transform(data)
56
+ hyper_transformer.fit(table_data)
57
+ transformed = hyper_transformer.transform(table_data)
57
58
 
58
- self.length = len(data)
59
+ self.length = len(table_data)
59
60
  gm_models = {}
60
61
  for name, column in transformed.items():
61
62
  kind = column.dtype.kind
@@ -68,6 +69,7 @@ class ColumnSynthesizer(BaselineSynthesizer):
68
69
  self.hyper_transformer = hyper_transformer
69
70
  self.transformed_data = transformed
70
71
  self.gm_models = gm_models
72
+ self._table_name = table_name
71
73
 
72
74
  def _sample_from_synthesizer(self, synthesizer, n_samples):
73
75
  hyper_transformer = synthesizer.hyper_transformer
@@ -84,4 +86,4 @@ class ColumnSynthesizer(BaselineSynthesizer):
84
86
 
85
87
  sampled[name] = values
86
88
 
87
- return hyper_transformer.reverse_transform(sampled)
89
+ return {synthesizer._table_name: hyper_transformer.reverse_transform(sampled)}
@@ -306,14 +306,16 @@ class DataDesignerSynthesizer(BaselineSynthesizer):
306
306
  _MODALITY_FLAG = 'single_table'
307
307
 
308
308
  def _fit(self, data, metadata):
309
- table_name = metadata._get_single_table_name()
310
309
  metadata.validate()
311
- metadata.validate_data({table_name: data})
310
+ metadata.validate_data(data)
312
311
 
312
+ table_name = metadata._get_single_table_name()
313
+ table_data = data[table_name]
313
314
  model_kwargs = self._MODEL_KWARGS.copy() if self._MODEL_KWARGS else {}
314
315
  self._artifact_path = model_kwargs.pop('artifact_path', None)
315
316
  self._cleanup_artifacts = model_kwargs.pop('cleanup_artifacts', True)
316
- self._config_builder = create_data_designer_config(data, metadata, **model_kwargs)
317
+ self._config_builder = create_data_designer_config(table_data, metadata, **model_kwargs)
318
+ self._table_name = table_name
317
319
 
318
320
  def _sample_from_synthesizer(self, synthesizer, n_sample):
319
321
  """Sample synthetic data with specified sample count."""
@@ -332,12 +334,12 @@ class DataDesignerSynthesizer(BaselineSynthesizer):
332
334
  artifact_path = Path(synthesizer._artifact_path)
333
335
 
334
336
  # A unique name keeps concurrent or repeated sample calls from sharing a folder.
335
- dataset_name = f'dataset-{uuid.uuid4().hex}'
337
+ dataset_name = f'dataset-{synthesizer._table_name}-{uuid.uuid4().hex}'
336
338
  try:
337
339
  output = DataDesigner(artifact_path=artifact_path).create(
338
340
  synthesizer._config_builder, num_records=n_sample, dataset_name=dataset_name
339
341
  )
340
- return output.load_dataset()
342
+ return {synthesizer._table_name: output.load_dataset()}
341
343
  finally:
342
344
  if synthesizer._cleanup_artifacts:
343
345
  to_remove = artifact_path if is_temporary else artifact_path / dataset_name
@@ -8,6 +8,7 @@ from functools import partialmethod
8
8
  import tqdm
9
9
 
10
10
  from sdgym.synthesizers.base import BaselineSynthesizer
11
+ from sdgym.synthesizers.utils import _get_single_table_name_and_dataframe
11
12
 
12
13
  REMOVED_TRAINING_ARGS = ('overwrite_output_dir',)
13
14
 
@@ -49,14 +50,16 @@ class RealTabFormerSynthesizer(BaselineSynthesizer):
49
50
  " dependencies by running pip install sdgym['realtabformer'] "
50
51
  ) from exception
51
52
 
53
+ table_name, table_data = _get_single_table_name_and_dataframe(data, metadata)
52
54
  with prevent_tqdm_output():
53
55
  model_kwargs = self._MODEL_KWARGS.copy() if self._MODEL_KWARGS else {}
54
56
  model = REaLTabFormer(model_type='tabular', **model_kwargs)
55
57
  _remove_unsupported_training_args(model)
56
- model.fit(data)
58
+ model.fit(table_data)
57
59
 
58
60
  self._internal_synthesizer = model
61
+ self._table_name = table_name
59
62
 
60
63
  def _sample_from_synthesizer(self, synthesizer, n_sample):
61
64
  """Sample synthetic data with specified sample count."""
62
- return synthesizer._internal_synthesizer.sample(n_sample)
65
+ return {synthesizer._table_name: synthesizer._internal_synthesizer.sample(n_sample)}
@@ -50,7 +50,7 @@ def _fit(self, data, metadata):
50
50
  def _sample_from_synthesizer(self, synthesizer, sample_arg):
51
51
  LOGGER.info('Sampling %s', self.__class__.__name__)
52
52
  if self._MODALITY_FLAG == 'multi_table':
53
- return synthesizer._internal_synthesizer.sample(scale=sample_arg)
53
+ return synthesizer._internal_synthesizer.sample_scale(scale=sample_arg)
54
54
 
55
55
  return synthesizer._internal_synthesizer.sample(num_rows=sample_arg)
56
56
 
@@ -23,6 +23,7 @@ from sklearn.preprocessing import OrdinalEncoder, QuantileTransformer
23
23
  from torch import Tensor
24
24
 
25
25
  from sdgym.synthesizers.base import BaselineSynthesizer
26
+ from sdgym.synthesizers.utils import _get_single_table_name_and_dataframe
26
27
 
27
28
  CAT_MISSING_VALUE = '__nan__'
28
29
 
@@ -1196,12 +1197,14 @@ class TabDDPMSynthesizer(BaselineSynthesizer):
1196
1197
  _MODALITY_FLAG = 'single_table'
1197
1198
 
1198
1199
  def _fit(self, data, metadata):
1200
+ table_name, table_data = _get_single_table_name_and_dataframe(data, metadata)
1199
1201
  model_kwargs = self._MODEL_KWARGS.copy() if self._MODEL_KWARGS else {}
1200
1202
  model = TabDDPM(metadata, **model_kwargs)
1201
- model.fit(data)
1203
+ model.fit(table_data)
1202
1204
 
1203
1205
  self._internal_synthesizer = model
1206
+ self._table_name = table_name
1204
1207
 
1205
1208
  def _sample_from_synthesizer(self, synthesizer, n_sample):
1206
1209
  """Sample synthetic data with specified sample count."""
1207
- return synthesizer._internal_synthesizer.sample(n_sample)
1210
+ return {synthesizer._table_name: synthesizer._internal_synthesizer.sample(n_sample)}
@@ -8,6 +8,7 @@ import pandas as pd
8
8
  from rdt.hyper_transformer import HyperTransformer
9
9
 
10
10
  from sdgym.synthesizers.base import BaselineSynthesizer, MultiTableBaselineSynthesizer
11
+ from sdgym.synthesizers.utils import _get_single_table_name_and_dataframe
11
12
 
12
13
  LOGGER = logging.getLogger(__name__)
13
14
 
@@ -21,22 +22,24 @@ class UniformSynthesizer(BaselineSynthesizer):
21
22
  super().__init__()
22
23
  self.hyper_transformer = None
23
24
  self.transformed_data = None
25
+ self._table_name = None
24
26
 
25
27
  def _fit(self, data, metadata):
26
28
  """Fit the synthesizer to the data.
27
29
 
28
30
  Args:
29
- data (pd.DataFrame):
31
+ data (dict[str, pd.DataFrame]):
30
32
  The data to fit the synthesizer to.
31
33
  metadata (sdv.metadata.Metadata):
32
34
  The metadata describing the data.
33
35
  """
36
+ table_name, table_data = _get_single_table_name_and_dataframe(data, metadata)
34
37
  hyper_transformer = HyperTransformer()
35
- hyper_transformer.detect_initial_config(data)
38
+ hyper_transformer.detect_initial_config(table_data)
36
39
  supported_sdtypes = hyper_transformer._get_supported_sdtypes()
37
40
  config = {}
38
- table = next(iter(metadata.tables.values()))
39
- for column_name, column in table.columns.items():
41
+ table_metadata = next(iter(metadata.tables.values()))
42
+ for column_name, column in table_metadata.columns.items():
40
43
  sdtype = column['sdtype']
41
44
  if sdtype in supported_sdtypes:
42
45
  config[column_name] = sdtype
@@ -59,15 +62,16 @@ class UniformSynthesizer(BaselineSynthesizer):
59
62
  # This is done to match the behavior of the synthesizer for SDGym <= 0.6.0
60
63
  columns_to_remove = [
61
64
  column_name
62
- for column_name, column_data in data.items()
65
+ for column_name, column_data in table_data.items()
63
66
  if column_data.dtype.kind in {'O', 'i', 'b'}
64
67
  ]
65
68
  hyper_transformer.remove_transformers(columns_to_remove)
66
69
 
67
- hyper_transformer.fit(data)
68
- transformed = hyper_transformer.transform(data)
70
+ hyper_transformer.fit(table_data)
71
+ transformed = hyper_transformer.transform(table_data)
69
72
  self.hyper_transformer = hyper_transformer
70
73
  self.transformed_data = transformed
74
+ self._table_name = table_name
71
75
 
72
76
  def _sample_from_synthesizer(self, synthesizer, n_samples):
73
77
  hyper_transformer = synthesizer.hyper_transformer
@@ -85,7 +89,7 @@ class UniformSynthesizer(BaselineSynthesizer):
85
89
  values = np.random.uniform(column.min(), column.max(), size=n_samples)
86
90
  sampled[name] = values
87
91
 
88
- return hyper_transformer.reverse_transform(sampled)
92
+ return {synthesizer._table_name: hyper_transformer.reverse_transform(sampled)}
89
93
 
90
94
 
91
95
  class MultiTableUniformSynthesizer(MultiTableBaselineSynthesizer):
@@ -112,7 +116,7 @@ class MultiTableUniformSynthesizer(MultiTableBaselineSynthesizer):
112
116
  for table_name, table_data in data.items():
113
117
  table_metadata = metadata.get_table_metadata(table_name)
114
118
  synthesizer = UniformSynthesizer()
115
- synthesizer._fit(table_data, table_metadata)
119
+ synthesizer._fit({table_name: table_data}, table_metadata)
116
120
  self.num_rows_per_table[table_name] = len(table_data)
117
121
  self.table_synthesizers[table_name] = synthesizer
118
122
 
@@ -135,6 +139,6 @@ class MultiTableUniformSynthesizer(MultiTableBaselineSynthesizer):
135
139
  sampled_table = UniformSynthesizer().sample_from_synthesizer(
136
140
  table_synthesizer, n_samples=n_samples
137
141
  )
138
- sampled_data[table_name] = sampled_table
142
+ sampled_data[table_name] = sampled_table[table_name]
139
143
 
140
144
  return sampled_data
@@ -0,0 +1,84 @@
1
+ """Utility functions for synthesizers in SDGym."""
2
+
3
+ from sdv.metadata import Metadata
4
+
5
+ from sdgym.synthesizers.base import BaselineSynthesizer
6
+
7
+
8
+ def get_available_single_table_synthesizers():
9
+ """List all available single-table synthesizers in SDGym.
10
+
11
+ Returns:
12
+ list:
13
+ A sorted list of available single-table synthesizer names.
14
+ """
15
+ return sorted(BaselineSynthesizer._get_supported_synthesizers('single_table'))
16
+
17
+
18
+ def get_available_multi_table_synthesizers():
19
+ """List all available multi-table synthesizers in SDGym.
20
+
21
+ Returns:
22
+ list:
23
+ A sorted list of available multi-table synthesizer names.
24
+ """
25
+ return sorted(BaselineSynthesizer._get_supported_synthesizers('multi_table'))
26
+
27
+
28
+ def _get_supported_synthesizers():
29
+ """Get SDGym supported synthesizers.
30
+
31
+ Returns:
32
+ list:
33
+ A list of available SDGym supported synthesizer names.
34
+ """
35
+ synthesizers = []
36
+ for modality in ['single_table', 'multi_table']:
37
+ synthesizers.extend(BaselineSynthesizer._get_supported_synthesizers(modality))
38
+
39
+ return sorted(synthesizers)
40
+
41
+
42
+ def _get_single_table_name(data, metadata):
43
+ """Get the table name from metadata or data.
44
+
45
+ Args:
46
+ data (dict[str, pd.DataFrame]):
47
+ Dictionary mapping the name to the dataframe. Expected to
48
+ be a dictionary of one dataframe.
49
+ metadata (sdv.Metadata):
50
+ The metadata.
51
+
52
+ Returns:
53
+ str:
54
+ The name of the table.
55
+ """
56
+ if isinstance(metadata, Metadata):
57
+ table_name = metadata._get_single_table_name()
58
+ else:
59
+ if len(data) != 1:
60
+ raise ValueError(
61
+ "The data contains more than one table. Could not determine 'table name'."
62
+ )
63
+
64
+ table_name = next(iter(data))
65
+
66
+ return table_name
67
+
68
+
69
+ def _get_single_table_name_and_dataframe(data, metadata):
70
+ """Get the table name and its dataframe from single table data.
71
+
72
+ Args:
73
+ data (dict[str, pd.DataFrame]):
74
+ Dictionary mapping the name to the dataframe. Expected to
75
+ be a dictionary of one dataframe.
76
+ metadata (sdv.Metadata):
77
+ The metadata.
78
+
79
+ Returns:
80
+ tuple[str, pd.DataFrame]:
81
+ The name of the table and its dataframe.
82
+ """
83
+ table_name = _get_single_table_name(data, metadata)
84
+ return table_name, data[table_name]
@@ -209,3 +209,20 @@ def _set_column_width(writer, df, sheet_name):
209
209
  def _is_list_of_type(values, type_to_check=str):
210
210
  """Checks that 'values' is a list and all elements are of type 'type_to_check'."""
211
211
  return isinstance(values, list) and all(isinstance(value, type_to_check) for value in values)
212
+
213
+
214
+ def _get_single_table_dataframe(data):
215
+ """Get the only table from a dictionary of one dataframe."""
216
+ if not isinstance(data, dict) or not all(
217
+ isinstance(table, pd.DataFrame) for table in data.values()
218
+ ):
219
+ raise ValueError(
220
+ 'The data must be a dictionary mapping a single table name to a dataframe.'
221
+ )
222
+
223
+ if len(data) != 1:
224
+ raise ValueError(
225
+ f'The data must contain exactly one table, but {len(data)} tables were found.'
226
+ )
227
+
228
+ return next(iter(data.values()))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sdgym
3
- Version: 0.16.1.dev0
3
+ Version: 0.16.2.dev0
4
4
  Summary: Benchmark tabular synthetic data generators using a variety of datasets
5
5
  Author-email: "DataCebo, Inc." <info@sdv.dev>
6
6
  License-Expression: BUSL-1.1
@@ -63,10 +63,9 @@ Requires-Dist: torch>=2.6.0; python_version >= "3.13" and python_version < "3.14
63
63
  Requires-Dist: torch>=2.9.0; python_version >= "3.14"
64
64
  Requires-Dist: tqdm>=4.66.3
65
65
  Requires-Dist: XlsxWriter>=1.2.8
66
- Requires-Dist: rdt<2,>=1.18.2; python_version < "3.14"
67
- Requires-Dist: rdt<2,>=1.20.0; python_version >= "3.14"
68
- Requires-Dist: sdmetrics>=0.29.0
69
- Requires-Dist: sdv<2,>=1.37.0
66
+ Requires-Dist: sdmetrics>=0.32.0
67
+ Requires-Dist: rdt<3,>=2.0.0
68
+ Requires-Dist: sdv<3,>=2.0.0
70
69
  Provides-Extra: dask
71
70
  Requires-Dist: dask; extra == "dask"
72
71
  Requires-Dist: distributed; extra == "dask"
@@ -10,8 +10,9 @@ psutil>=7.0.0
10
10
  tabulate<0.9,>=0.8.3
11
11
  tqdm>=4.66.3
12
12
  XlsxWriter>=1.2.8
13
- sdmetrics>=0.29.0
14
- sdv<2,>=1.37.0
13
+ sdmetrics>=0.32.0
14
+ rdt<3,>=2.0.0
15
+ sdv<3,>=2.0.0
15
16
 
16
17
  [:python_version < "3.10"]
17
18
  numpy>=1.22.2
@@ -24,7 +25,6 @@ torch>=1.13.0
24
25
 
25
26
  [:python_version < "3.14"]
26
27
  cloudpickle>=2.1.0
27
- rdt<2,>=1.18.2
28
28
 
29
29
  [:python_version >= "3.10" and python_version < "3.11"]
30
30
  scikit-learn>=1.1.0
@@ -59,7 +59,6 @@ pandas<3,>=2.3.3
59
59
  scikit-learn>=1.8.0
60
60
  scipy>=1.16.1
61
61
  torch>=2.9.0
62
- rdt<2,>=1.20.0
63
62
 
64
63
  [all]
65
64
  sdgym[dask,dev,test]
@@ -1,37 +0,0 @@
1
- """Utility functions for synthesizers in SDGym."""
2
-
3
- from sdgym.synthesizers.base import BaselineSynthesizer
4
-
5
-
6
- def get_available_single_table_synthesizers():
7
- """List all available single-table synthesizers in SDGym.
8
-
9
- Returns:
10
- list:
11
- A sorted list of available single-table synthesizer names.
12
- """
13
- return sorted(BaselineSynthesizer._get_supported_synthesizers('single_table'))
14
-
15
-
16
- def get_available_multi_table_synthesizers():
17
- """List all available multi-table synthesizers in SDGym.
18
-
19
- Returns:
20
- list:
21
- A sorted list of available multi-table synthesizer names.
22
- """
23
- return sorted(BaselineSynthesizer._get_supported_synthesizers('multi_table'))
24
-
25
-
26
- def _get_supported_synthesizers():
27
- """Get SDGym supported synthesizers.
28
-
29
- Returns:
30
- list:
31
- A list of available SDGym supported synthesizer names.
32
- """
33
- synthesizers = []
34
- for modality in ['single_table', 'multi_table']:
35
- synthesizers.extend(BaselineSynthesizer._get_supported_synthesizers(modality))
36
-
37
- return sorted(synthesizers)
File without changes
File without changes
File without changes
File without changes
File without changes