On this page

Realism benchmark

Misata's default mode never sees real data, so "realistic" cannot be shown the usual way: fit a model to a dataset, then score it against that dataset. This benchmark asks the question a user actually faces. Knowing only the shape of a business (its tables, its column names and types, roughly how many rows), how close does each tool get to what real data of that kind looks like?

Two public datasets, each split in half:

  • Olist: real orders from a Brazilian marketplace (2016–2018), 30,000 orders sampled, with customers, products and payments.
  • NYC taxis: the seaborn sample of March 2019 trips, 6,389 rides.

Contestants:

Sees real data?What it is given
misata_storynoone sentence naming the business and its row counts
misata_schemanotable and column names and types, the category labels (not their shares), no parameters
faker_scriptnothe script people write: Faker dates, numpy uniform amounts, uniform foreign keys
misata_mimictrain halfmisata.mimic() on the real rows
sdv_copulatrain halfSDV's Gaussian copula on the real rows
real_traintrain halfthe real train half itself: the noise floor

Everything is scored against the held-out test half, on scale-free metrics so a blind generator is not punished for not knowing the currency:

MetricMeasuresBetter
Amount shapeKS distance between log(amount / median) distributionslower
Hour / weekday profiletotal variation distance between the histogramslower
Customer / product fan-outdifference in the Gini of children per parentlower
Category balancedifference in normalised entropy (how even, not which labels)lower
Detection AUCa gradient-boosted classifier telling real from synthetic rows on amount shape, time of day and weekday; 0.5 is indistinguishablelower
Tells scorerealism_report on the synthetic tableshigher

Results#

Misata 0.9.7, seed 7. Reproduce with python -m benchmarks.realism_bench --cache .bench_cache.

Detection AUC over five seeds#

Seeds 7, 11, 23, 42 and 99: mean, with the lowest and highest run. The other metrics move by 0.01 or less between seeds; every one is in benchmarks/results/summary.json.

Datasetreal_trainmisata_storymisata_schemafaker_scriptmisata_mimicsdv_copula
Olist0.500 (0.49–0.51)0.730 (0.73–0.73)0.629 (0.63–0.63)0.783 (0.78–0.79)0.537 (0.53–0.54)0.711 (0.71–0.72)
NYC taxis0.503 (0.50–0.51)0.801 (0.80–0.81)0.750 (0.75–0.75)0.788 (0.78–0.79)0.543 (0.53–0.55)0.675 (0.67–0.68)
Detection AUC, mean of five seeds (0.5 means a classifier cannot tell synthetic from real; lower is better)
  • Olist
  • NYC taxis
real_train (noise floor) Olist: 0.5 NYC taxis: 0.503real_train (noise floor)0.50.503misata_mimic (fitted) Olist: 0.537 NYC taxis: 0.543misata_mimic (fitted)0.5370.543misata_schema (blind) Olist: 0.629 NYC taxis: 0.75misata_schema (blind)0.6290.75sdv_copula (fitted) Olist: 0.711 NYC taxis: 0.675sdv_copula (fitted)0.7110.675misata_story (blind) Olist: 0.73 NYC taxis: 0.801misata_story (blind)0.730.801faker_script (blind) Olist: 0.783 NYC taxis: 0.788faker_script (blind)0.7830.7880.50.85
Show the numbers
GeneratorOlistNYC taxis
real_train (noise floor)0.50.503
misata_mimic (fitted)0.5370.543
misata_schema (blind)0.6290.75
sdv_copula (fitted)0.7110.675
misata_story (blind)0.730.801
faker_script (blind)0.7830.788

Source: benchmarks/results/summary.json, realism.datasets.*.detect_auc.mean

Olist (30,000 orders)#

Metricreal_trainmisata_storymisata_schemafaker_scriptmisata_mimicsdv_copula
Amount shape (KS)0.0080.1690.0510.1910.0240.072
Hour profile (TVD)0.0180.1490.1450.2890.0170.286
Weekday profile (TVD)0.0100.0460.0480.0540.0090.057
Customer fan-out (ΔGini)0.001n/a0.2560.237n/an/a
Product fan-out (ΔGini)0.0030.4400.0280.149n/an/a
Category balance (Δ)0.003n/a0.4810.5000.0010.003
Detection AUC0.5000.7320.6290.7850.5330.708
Tells score ↑1.0000.9421.0000.3331.0000.750

NYC taxis (6,389 trips)#

Metricreal_trainmisata_storymisata_schemafaker_scriptmisata_mimicsdv_copula
Amount shape (KS)0.0150.2250.2510.2170.0250.135
Hour profile (TVD)0.0390.2370.0820.1930.0600.189
Weekday profile (TVD)0.0260.0740.0300.0330.0540.049
Category balance (Δ)0.003n/a0.1300.1400.0090.003
Detection AUC0.4970.8050.7500.7920.5300.676
Tells score ↑1.0001.0000.6670.4001.0000.667

What it says#

On e-commerce, blind Misata beats the script and approaches a model fitted to the data. With table and column names and the domain, misata_schema is harder to tell from real orders than SDV fitted on 15,000 of them (detection AUC 0.63 against 0.71, over five seeds), gets the shape of order amounts closer than SDV (0.05 vs 0.07), and matches real product popularity almost exactly (ΔGini 0.03), which the uniform script misses by five times as much. One qualification: the e-commerce amount prior was set from Olist's public data when Misata's priors were built, so the amount row is not a blind result. Hours, weekdays and fan-out are.

On taxis, the hour rhythm is now right; fares are not. Declaring the domain as transport gives the night-heavy demand curve ride data has (hour TVD 0.08, better than SDV fitted on the data at 0.19), which puts blind Misata ahead of the script (AUC 0.75 vs 0.79) but behind SDV fitted on the real trips (0.68). Fares are still drawn from a generic money shape that is wider than taxi totals (amount KS 0.25, worse than the script's 0.22). That is the remaining miss here, and it is left in rather than fixed with a taxi-specific fare prior fitted to the same public data.

Customer fan-out is better, not solved. In this Olist sample nearly every buyer buys once (30,000 orders from 29,651 customers). Declaring the domain as marketplace makes person-like parents (customers, buyers, guests) mildly weighted while products stay concentrated, which cut the gap from 0.39 to 0.26. Declaring min_children: 1 on the relationship, which says what this sample says (every customer in it has ordered), closes it: ΔGini 0.000. That declaration did not work before 0.9.7 for child tables larger than one 10,000-row batch; it now counts coverage across batches.

mimic is the strongest fitted generator here. It beats SDV on every metric except weekday profile and category balance on taxis, and is close to indistinguishable on both datasets (AUC 0.54).

The tells check calibrates on real data. Both real datasets score 1.0 on realism_report.

Changes between runs, and why#

The first published run (0.9.6.60 defaults) found five problems, and the fixes are in 0.9.7. To keep the benchmark honest about what it tests:

  • Bugs, fixed generally: mimic dropped the time of day; story *_at columns were dates; the fan-out tell flagged real data; product names were not reproducible across processes. None of these fixes look at the benchmark data.
  • Domain knowledge added after the benchmark exposed its absence: a night-heavy hour curve for transport/taxi/mobility/nightlife domains, and mild person fan-out for marketplace/travel/realestate. These are general facts about those businesses, not parameters fitted to these samples, but they were added because these samples showed they were missing. Treat the taxi hour row and the Olist customer row as validation of a fix, not as a blind first measurement.
  • The Olist blind schema now declares marketplace (it declared ecommerce before), because that is what Olist is. With the same declaration as before, every number but customer fan-out is unchanged.

A correction to the detection metric#

The first 0.9.7 runs reported higher detection AUCs (Olist schema 0.74, taxi schema 0.91, taxi mimic 0.66). Running five seeds showed why: the real train half scored as high as 0.89 against the real test half on some seeds, which is impossible for a fair test. Amounts are discrete (prices repeat), and dividing each sample by its own median put the two real halves on slightly different grids that the classifier learned to tell apart. The feature is now rounded to one decimal of log-ratio, real-vs-real scores 0.50 on every seed, and every contestant's AUC above is from the corrected metric. The other metrics did not change.

Caveats#

  • Two datasets, both transactional. This is evidence about orders and trips, not about every domain.
  • The detection classifier sees three scale-free features. A classifier with more features (text, cross-table joins) would separate every contestant more easily.
  • Blind contestants get the row counts and, for the schema variant, the category labels. They never see shares, ranges or any row.
  • Five seeds. A seed changes which 30,000 Olist orders are sampled, the train/test split and every generator's seed; it cannot add datasets.
  • Realism report: the tells check behind the tells score, for any generator
  • Validity benchmark: keys and CHECK rules rather than shapes
  • Mimic: the fitted contestant, which is not anonymous
  • Limitations: the taxi fare miss and other known gaps

Prefer generating data visually?

Misata Studio lets you turn natural language prompts into relational schemas with exact outcome invariants, live preview tables, and one-click database seeding without writing code.

Last updated Edit this page.md