Генерация синтетических данных с помощью SDV: от идеи до готовой таблицы в ClickHouse
Синтетические данные давно перестали быть просто «искусственной картинкой» — сегодня это полноценный инструмент для тестирования, разработки и даже обучения моделей машинного обучения. В этой статье я на примере реального кода покажу, как с помощью библиотеки SDV ( Synthetic Data Vault ) можно сгенерировать синтетическую копию существующей таблицы из ClickHouse, сохранив её статистические свойства и взаимосвязи.
Весь код, который мы разберём, написан на Python и использует актуальную версию SDV. Спойлер: итоговый скрипт занимает меньше 80 строк, а результат — полностью готовая синтетическая таблица в вашей базе данных.
Что такое SDV и зачем он нужен
Synthetic Data Vault (SDV) — это Python-библиотека с открытым исходным кодом, которая стала фактическим стандартом для генерации синтетических табличных данных. Она была создана в MIT, а сегодня развивается компанией DataCebo.
Главная идея SDV проста: вы даёте ей реальные данные, она обучает генеративную модель, а затем эта модель создаёт новые данные, которые статистически неотличимы от оригинала, но при этом не содержат реальных записей.
Ключевые возможности SDV:
- Обучение на одном наборе данных — библиотека анализирует распределения, корреляции и зависимости между колонками.
- Генерация новых записей — синтетические данные повторяют структуру и свойства оригинала.
- Оценка качества — встроенные метрики показывают, насколько хорошо синтетика отражает реальные данные.
- Поддержка разных типов данных — числовые, категориальные, даты, текстовые поля.
SDV умеет работать с одиночными таблицами, связанными таблицами и даже последовательными данными.
Какие синтезаторы предлагает SDV
SDV предлагает несколько алгоритмов (синтезаторов) на выбор. В нашем примере мы используем `GaussianCopulaSynthesizer` — и вот почему.
| Синтезатор | Принцип работы | Скорость | Когда использовать |
| GaussianCopulaSynthesizer | Классический статистический метод, моделирует данные через многомерное распределение | Очень быстрый | Рекомендуется для старта: быстрая работа, хорошее качество, прозрачность |
| CTGANSynthesizer | Генеративно-состязательная сеть (GAN) | Медленнее | Для данных со сложными зависимостями или большим количеством категориальных признаков |
| TVAESynthesizer | Вариационный автокодировщик | Средняя | Альтернатива GAN, требующая больше данных для обучения |
`GaussianCopulaSynthesizer` отлично справляется со смешанными типами данных, которые встречаются в реальных таблицах: числа, строки, даты. При этом он обучается за секунды даже на десятках тысяч строк. Именно поэтому мы выбрали его для нашего примера.
Настройка окружения
Перед запуском кода нам потребуется установить несколько пакетов. В нашем проекте мы использовали `uv` — быстрый менеджер пакетов для Python.
uv pip install torch==2.2.2
uv pip install "numpy<2"
uv pip install "scipy<1.13"
uv pip install sdv
uv pip install clickhouse-connectВажно: версии пакетов подобраны так, чтобы избежать конфликтов. На момент написания статьи `torch` версии 2.13.0 не имеет готовых сборок для macOS на Intel, поэтому мы используем `2.2.2`. Также важно зафиксировать `numpy<2` и `scipy<1.13` — более новые версии несовместимы с текущей версией `torch`.
Полный код генерации синтетических данных
Ниже представлен итоговый скрипт `sdv_py_v5.py`, который мы будем разбирать. Он подключается к ClickHouse, загружает реальные данные, обучает синтезатор, генерирует синтетическую копию и сохраняет результат обратно в базу.
import clickhouse_connect
import pandas as pd
import os
import warnings
from sdv.single_table import GaussianCopulaSynthesizer
from sdv.metadata import SingleTableMetadata
# Подавляем предупреждения (включая депрекацию)
warnings.filterwarnings("ignore")
# Правильный импорт для однотабличных отчётов
from sdmetrics.reports.single_table import QualityReport, DiagnosticReport
# ========== НАСТРОЙКИ ==========
CLICKHOUSE_HOST = 'localhost'
CLICKHOUSE_PORT = 18123
CLICKHOUSE_USER = 'default'
CLICKHOUSE_PASSWORD = 'changeme'
CLICKHOUSE_DATABASE = 'default'
SOURCE_TABLE = 'superstore'
SYNTHETIC_TABLE = f'{SOURCE_TABLE}_synt'
LIMIT_ROWS = 10000
NUM_SYNTHETIC_ROWS = None
# ========== ПОДКЛЮЧЕНИЕ ==========
client = clickhouse_connect.get_client(
host=CLICKHOUSE_HOST,
port=CLICKHOUSE_PORT,
username=CLICKHOUSE_USER,
password=CLICKHOUSE_PASSWORD,
database=CLICKHOUSE_DATABASE
)
# ========== ЗАГРУЗКА ==========
query = f"SELECT * FROM {SOURCE_TABLE} LIMIT {LIMIT_ROWS}"
real_df = client.query_df(query)
print(f"Загружено {len(real_df)} строк")
# Преобразуем числовые колонки (запятая → точка)
numeric_cols = ['sales', 'discount', 'profit']
for col in numeric_cols:
if col in real_df.columns and real_df[col].dtype == 'object':
real_df[col] = real_df[col].str.replace(',', '.').astype(float)
# ========== МЕТАДАННЫЕ ==========
metadata = SingleTableMetadata()
metadata.detect_from_dataframe(real_df)
if os.path.exists('metadata.json'):
os.remove('metadata.json')
metadata.save_to_json('metadata.json')
print("Метаданные сохранены в metadata.json")
# ========== ОБУЧЕНИЕ ==========
print("Начинаем обучение синтезатора...")
synthesizer = GaussianCopulaSynthesizer(metadata)
synthesizer.fit(real_df)
print("Обучение завершено")
# ========== ГЕНЕРАЦИЯ ==========
if NUM_SYNTHETIC_ROWS is None:
NUM_SYNTHETIC_ROWS = len(real_df)
synthetic_df = synthesizer.sample(NUM_SYNTHETIC_ROWS)
print(f"Сгенерировано {len(synthetic_df)} строк")
# ========== ПРЕДПРОСМОТР ==========
print("\nОригинальные данные (первые 5 строк):")
print(real_df.head())
print("\nСинтетические данные (первые 5 строк):")
print(synthetic_df.head())
# ========== ОЦЕНКА КАЧЕСТВА ==========
print("\n--- Диагностика (проверка ошибок) ---")
diagnostic = DiagnosticReport()
diagnostic.generate(real_df, synthetic_df, metadata.to_dict())
print(diagnostic)
print("\n--- Оценка качества (статистическое сходство) ---")
quality = QualityReport()
quality.generate(real_df, synthetic_df, metadata.to_dict())
print(quality)
# ========== СОХРАНЕНИЕ В CLICKHOUSE ==========
client.command(f"CREATE TABLE IF NOT EXISTS {SYNTHETIC_TABLE} AS {SOURCE_TABLE}")
client.insert_df(SYNTHETIC_TABLE, synthetic_df)
print(f"\nСинтетические данные загружены в таблицу {SYNTHETIC_TABLE}")
# ========== СОХРАНЕНИЕ В CSV ==========
synthetic_df.to_csv('synthetic_table.csv', index=False)
print("Синтетические данные также сохранены в synthetic_table.csv")Разбор кода по шагам
1. Подключение к ClickHouse и загрузка данных
client = clickhouse_connect.get_client(
host=CLICKHOUSE_HOST,
port=CLICKHOUSE_PORT,
username=CLICKHOUSE_USER,
password=CLICKHOUSE_PASSWORD,
database=CLICKHOUSE_DATABASE
)
query = f"SELECT * FROM {SOURCE_TABLE} LIMIT {LIMIT_ROWS}"
real_df = client.query_df(query)Мы подключаемся к ClickHouse через библиотеку `clickhouse-connect` и загружаем данные из таблицы-источника в Pandas DataFrame. Лимит в 10 000 строк — разумный компромисс между качеством обучения и скоростью. Для большинства задач этого объёма достаточно, чтобы синтезатор уловил основные закономерности.
2. Предобработка числовых данных
numeric_cols = ['sales', 'discount', 'profit']
for col in numeric_cols:
if col in real_df.columns and real_df[col].dtype == 'object':
real_df[col] = real_df[col].str.replace(',', '.').astype(float)В нашем примере числовые колонки хранятся в ClickHouse как строки с запятой в качестве десятичного разделителя (европейский формат). Перед обучением мы преобразуем их в числа с плавающей точкой — иначе SDV не сможет корректно обработать эти данные.
3. Создание метаданных
metadata = SingleTableMetadata()
metadata.detect_from_dataframe(real_df)
metadata.save_to_json('metadata.json')SDV автоматически определяет типы каждой колонки: числовые, категориальные, даты и т.д.. Метаданные сохраняются в JSON-файл — это полезно для воспроизводимости: в следующий раз вы сможете загрузить их, а не пересоздавать заново.
4. Обучение синтезатора
synthesizer = GaussianCopulaSynthesizer(metadata)
synthesizer.fit(real_df)Синтезатор «изучает» ваши данные: анализирует распределения каждой колонки, корреляции между ними, паттерны пропусков. Этот процесс занимает секунды даже на 10 000 строк.
5. Генерация синтетических данных
synthetic_df = synthesizer.sample(NUM_SYNTHETIC_ROWS)Метод `sample()` создаёт нужное количество новых записей. В нашем случае мы генерируем столько же строк, сколько было в оригинале. При желании можно увеличить или уменьшить это число — синтезатор умеет масштабировать данные.
6. Оценка качества
diagnostic = DiagnosticReport()
diagnostic.generate(real_df, synthetic_df, metadata.to_dict())
quality = QualityReport()
quality.generate(real_df, synthetic_df, metadata.to_dict())Этот этап — одна из сильных сторон SDV. Мы проверяем два аспекта:
- Диагностика (Data Validity + Data Structure) — проверяет, что синтетические данные корректны по типам и структуре. В нашем случае — 100%.
- Quality Report — оценивает статистическое сходство с реальными данными по двум метрикам:
- Column Shapes Score (90.77%) — насколько хорошо совпадают распределения отдельных колонок.
- Column Pair Trends Score (37.71%) — насколько хорошо сохранились попарные зависимости между колонками.
Общий Score 64.24% — это нормальный результат для демонстрационного примера. Для улучшения качества можно переключиться на `CTGANSynthesizer` или увеличить объём обучающих данных.
7. Сохранение в ClickHouse
client.command(f"CREATE TABLE IF NOT EXISTS {SYNTHETIC_TABLE} AS {SOURCE_TABLE}")
client.insert_df(SYNTHETIC_TABLE, synthetic_df)Финальный шаг: создаём новую таблицу с той же структурой, что и исходная, и заполняем её синтетическими данными. Всё готово — вы можете работать с синтетической копией так же, как с реальной таблицей.
Интеграция с ClickHouse: почему это важно
ClickHouse — популярная колоночная СУБД для аналитики. Генерация синтетических данных прямо в экосистему ClickHouse открывает широкие возможности:
- Тестирование производительности — можно генерировать датасеты любого размера и проверять, как ClickHouse справляется с нагрузкой.
- Разработка без доступа к продакшену — разработчики получают рабочую копию данных без риска утечки конфиденциальной информации.
- Обучение моделей — синтетические данные можно использовать для предварительного обучения ML-моделей, не затрагивая реальные данные.
Альтернативный подход — использование встроенных средств ClickHouse, таких как движок `GenerateRandom`. Однако SDV даёт гораздо более качественные данные, потому что он не просто генерирует случайные значения, а воспроизводит реальные статистические паттерны.
Что дальше: улучшение качества синтеза
Если качество синтеза (особенно Column Pair Trends) вас не устраивает, вот несколько способов его улучшить:
- Увеличьте объём обучающих данных — вместо 10 000 строк загрузите 50 000 или 100 000.
- Попробуйте CTGANSynthesizer — нейросетевой синтезатор лучше справляется со сложными зависимостями.
- Настройте метаданные вручную — если автоматическое определение типов сработало неидеально, вы можете отредактировать `metadata.json` и указать типы явно.
- Используйте условную генерацию — SDV позволяет фиксировать значения отдельных колонок и генерировать остальные с учётом этих условий.
Заключение
Мы прошли полный путь: от установки библиотек до готовой синтетической таблицы в ClickHouse. SDV оказался мощным и при этом простым в использовании инструментом — весь процесс уместился в один скрипт на 80 строк.
Главные выводы:
- SDV — это не просто генератор случайных данных, а интеллектуальный инструмент, который сохраняет статистическую структуру оригинала.
- `GaussianCopulaSynthesizer` — отличная точка входа: быстрый, прозрачный, даёт хорошее качество.
- Встроенная оценка качества позволяет объективно понять, насколько синтетика похожа на реальные данные.
- Интеграция с ClickHouse через `clickhouse-connect` делает процесс полностью автоматизированным.
Синтетические данные открывают новые возможности для разработки, тестирования и анализа данных — без рисков, связанных с работой с реальными данными. А SDV даёт в руки инструмент, который превращает эту идею в реальность за считанные минуты.
ПЫСЫ:
Можно еще попробовать так: CTGANSynthesizer
from sdv.single_table import GaussianCopulaSynthesizer, CTGANSynthesizerНо надо будет дольше ждать... и заменить тип в строке на
CTGANSynthesizeruv run sdv_py_v55.py
Загружено 9994 строк
Метаданные сохранены в metadata.json
Начинаем обучение синтезатора...
PerformanceAlert: Using the CTGANSynthesizer on this data is not recommended. To model this data, CTGAN will generate a large number of columns.
Original Column Name Est # of Columns (CTGAN)
Order Date 1236
Ship Date 1334
Ship Mode 4
Customer Name 793
segment 3
Country/Region 1
region 4
category 3
Sub-Category 17
Product Name 1849
sales 5825
quantity 11
discount 12
profit 7287
We recommend preprocessing discrete columns that can have many values, using 'update_transformers'. Or you may drop columns that are not necessary to model. (Exit this script using ctrl-C)Все .. комп полетел)) 5 мин... 7 мин... 15... чем то напоминает майнинг))) .... 20... 30... пойду пожалуй спать ...