Open-source project
Diyago/Tabular-data-generation avatar
Diyago/Tabular-data-generation

TabGAN: One Python Interface for CTGAN, ForestDiffusion, Copulas and LLM Tabular Synthesis

We well know GANs for success in the realistic image generation. However, they can be applied in tabular data generation. We will review and examine some recent papers about tabular GANs in action.

570 stars82 forksPythonApache-2.0

At a glance

What is it?
TabGAN wraps four generative backends behind a single generate_data_pipe call, then runs a LightGBM adversarial filter over the output. The unified API is the selling point; the shared post-processing pipeline is the part that decides whether the synthetic rows are usable.
Who is it for?
Adopt TabGAN if you need to compare several synthetic tabular generators under one API without writing four separate training harnesses, or if you want the LightGBM adversarial filter applied to output from a model you already trust.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 170 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem TabGAN targets: four generative backends, one call site

Synthetic tabular data has no single winning method. CTGAN handles mixed continuous and categorical columns; diffusion over tree-based gradient boosting is documented for structured data; LLMs are listed for semantic dependencies and conditional text; a Gaussian copula is the fast correlation-preserving option. In practice a team that wants to compare those approaches ends up maintaining four training scripts, four preprocessing paths and four output formats. TabGAN's answer is a shared interface: every generator exposes generate_data_pipe and returns the same (new_train, new_target) tuple of pandas DataFrames. That is the whole proposition, and it is narrower than the README's feature list suggests. The project is not a new generative model. It is a coordination layer over existing ones, plus a post-processing stage the README describes as generate, post-process, adversarial filter. The audience is a data scientist who already knows which family of model they want to try and does not want to rewrite the surrounding plumbing each time they switch.

Inside generate_data_pipe: oversampling, quantile trimming and the LightGBM filter

The pipeline is visible in the parameter table even though the README does not walk through the code. pregeneration_frac defaults to 2, so the backend is asked for roughly twice the rows that will survive. is_post_process defaults to True and applies bot_filter_quantile (0.001) and top_filter_quantile (0.999) to trim the tails. Then use_adversarial defaults to True, which routes the surviving rows through the LightGBM-based validation the README calls adversarial filtering. The stated purpose is to keep synthetic samples distribution-consistent with the real data. gen_x_times, default 1.1, scales the requested sample count relative to the training size. A second flag, only_adversarial, skips generation entirely and runs only the filter, which is a useful escape hatch if you generated rows elsewhere. Two details matter here. First, the filter is a learned discriminator, so it can reject legitimate rare rows along with implausible ones. Second, the quantile trimming is applied on a fixed scale, which means a column whose real distribution is genuinely heavy-tailed gets clipped at the 0.999 mark by default. The README gives no guidance on tuning either knob per column.

Installing and calling the generators

Installation is a single command: pip install tabgan. The README's quick start imports GANGenerator from tabgan.sampler, builds a random 150-row DataFrame with columns A to D, a binary target column Y, and a 100-row test frame, then calls GANGenerator().generate_data_pipe(train, target, test) with no further arguments. The test frame is passed for distribution alignment, not for evaluation. Switching backends is a one-line change to the import: the sampler module also exposes OriginalGenerator, ForestDiffusionGenerator, BayesianGenerator and LLMGenerator. Generator-specific settings go through the gen_params dictionary. For GANGenerator the README shows {"batch_size": 500, "patience": 25, "epochs": 500}. For LLMGenerator it shows {"batch_size": 32, "epochs": 4, "llm": "distilgpt2", "max_length": 500}. The LLM backend is documented as working with LM Studio, OpenAI, Ollama or any OpenAI-compatible endpoint, so the llm key can point at a local server instead of a hosted model. generate_data_pipe additionally accepts deep_copy (default True) to copy the input frames, and only_generated_data (default False) to return synthetic rows without the originals mixed in. If you want the originals excluded from the output, that flag is the one to set.

Float-only internals and what that costs you

The data format note is blunt: TabGAN processes values as floating-point internally, and the README tells you to apply rounding after generation for integer-valued outputs. For anyone generating counts, ages or currency amounts, that means the generator's output is not directly usable and the rounding step is yours to write. Rounding after the fact also interacts with the adversarial filter, because the filter sees the float values, not the rounded ones you eventually store. A row that passes the filter at 3.7 may become 4 and land in a bin the filter never evaluated. The README does not address this ordering. Categorical handling is declared through the cat_cols list, which defaults to None; leaving it unset means the library has to infer which columns are discrete. The README lists conditional generation via LLM prompting as a feature, but the only conditioning parameter documented in the common table is cat_cols, and the conditional-generation description is tied to the LLM backend specifically. Treat the conditional path as LLM-only unless the source shows otherwise.

Where TabGAN is the wrong tool, and what to use instead

TabGAN assumes pandas DataFrames that fit in memory. The pipeline builds full frames, oversamples them by a factor of two, filters, and returns the result, so peak memory is several times the training set. If your table does not fit in a single process, this library is not the right layer. The second boundary is determinism. The README documents no random seed parameter in the common table or in the generator-specific gen_params examples, so reproducing an exact synthetic set across runs is not something the documented API promises. A team that needs a frozen synthetic dataset for a regression test should verify reproducibility before adopting. The obvious alternative is to call the underlying libraries directly: train a CTGAN model from the CTGAN package, or run ForestDiffusion from its own repository, and write your own filtering step. That path gives you the full hyperparameter surface of each model and control over the order of rounding and filtering, at the cost of maintaining the glue yourself. TabGAN's value is precisely that glue, so the direct route makes sense when you have settled on one backend and need its parameters, not when you are still choosing between four.

Maintenance, version history and the Apache-2.0 terms

The repository is not archived and the release cadence is uneven. Version 2.0.0 landed in September 2023 with ForestDiffusion support. Version 3.0.1 arrived in March 2026, followed the same month by v3.2.0, which added BayesianGenerator. That gap of roughly two and a half years between major lines is the maintenance signal to weigh: the project is active again, but it has been dormant before, and a dependency you build on should be checked against that history. The licence is Apache-2.0, which permits commercial use and modification and includes an explicit patent grant. It also requires that you preserve copyright and licence notices in redistributed copies and state significant changes. Apache-2.0 does not cover the models you load through the LLM backend: if llm points at a hosted API, that provider's terms govern, and if it points at a local checkpoint such as distilgpt2, that model's own licence applies. This is a description of the licence text, not legal advice; your own counsel should review redistribution plans.

What to verify before you commit to TabGAN

Three checks are worth running on your own data before this becomes a dependency. First, quantify what the adversarial filter removes. Call generate_data_pipe twice on the same inputs, once with use_adversarial=True and once with False, and compare returned row counts. If the filter is discarding a large share of rows, the effective sample size after pregeneration_frac=2 may be smaller than you expect. Second, inspect the quantile trimming against your actual column distributions. With bot_filter_quantile at 0.001 and top_filter_quantile at 0.999, any column with real mass in the extreme 0.1 percent will be clipped; raise or lower those keys deliberately rather than accepting the defaults. Third, confirm the float-to-integer path. Generate a batch, round it, and check whether the rounded values still resemble the training distribution, because the filter validated the unrounded ones. If any of those three checks fails, the fix is usually to bypass the wrapper and call the backend model directly, which is a legitimate outcome given that TabGAN's contribution is the pipeline rather than the model.

Editorial conclusion

Adopt TabGAN if you need to compare several synthetic tabular generators under one API without writing four separate training harnesses, or if you want the LightGBM adversarial filter applied to output from a model you already trust. Skip it if you need exact integer output (the README states values are processed as floats and rounding is your job), if your text columns need real semantic modelling and you cannot host an OpenAI-compatible endpoint, or if your data is small enough that a Gaussian copula alone would do. Before committing, run generate_data_pipe with use_adversarial=False and with it enabled on the same seed and compare the returned row counts, then check the two quantile keys against your own column ranges.

Official sources

  1. Diyago/Tabular-data-generation on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Community notes

Community notes