Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Large and wide tables

Column summaries and integrity checks examine every row. Relationship discovery screens broadly, then limits follow-up work by budget. This lets the profiler handle tables with millions of rows and hundreds of columns, while the amount of relationship evidence depends on the configured budget.

What stays exact

PassAt any size
Column cardsevery row: exact cardinality, null counts, statistics, quantiles, top values, concentration
Duplicate rowsfull-row hash plus exact verification
Single-column keysexact distinct counts over the full column
Sortednessexact scan of adjacent values
Datesexact calendar scan of distinct days
Arithmetic identitiesfitted on a sample, residuals verified on every pair-complete row
Functional dependenciesscreened broadly; reported candidates verified over up to 200,000 rows (standard) or 500,000 (deep) — full data when the table is smaller

Dense and nullable Int64/Float64 columns borrow Arrow buffers directly; other dtypes can require a normalized copy. This is an in-memory profiler, so input size and exact statistics still drive memory and runtime.

Bounded relationship work

Screening is broad and cheap; follow-up is bounded. The budget table in Coverage, budgets, and deadlines gives the per-tier numbers: at standard, the encoded relationship sample is 20,000 rows over all usable columns, each target is screened against all columns and fitted against at most 48 shortlisted predictors over 5,000 rows, all numeric pairs are screened and the 496 strongest get rank statistics, and FD candidates are verified over 200,000 rows. deep raises each of those. Coverage reports the limits used for each run.

Here is a wide synthetic table with relationships planted in the last columns — deliberately at the far end of the column order, because a screener that only looked at the first few dozen columns would miss them:

(100000, 59)
100000 rows × 59 columns (standard budget); key candidate: id; 3 functional dependencies, 6 predictive dependencies, 0 conditional rules

The report recovers the planted affine dependency, nonlinear response, and code→label lookup in the final columns, and ranks the integer id as the key candidate. Screening considers every usable column before shortlisting.

The coverage block records the work performed:

Loading...

The scale benchmark

The reproducible scale benchmark is the pre-publish gate for this contract. It generates a fixed-seed table — independent noise, an integer primary key, a key with one repeated value, and relationships planted in the final columns: an affine identity, a nonlinear response, and a category-to-label dependency — then fails unless every planted structure is recovered and the exact cardinality checks hold:

pip install 'onceover[benchmark]'
python benchmarks/scale.py --rows 2000000 --columns 200 --threads 4 \
  --output /tmp/onceover-scale.json

The recorded run — 2,000,000 rows × 200 columns on macOS arm64, 24 GiB system memory, four Rayon threads:

Recorded measurementValue
Profiling time5.68 s
Peak process RSS during profiling3.22 GiB
Increase from pre-profile RSS0.15 GiB

All eight checks passed, including recovery of the late-column relationships, and no independent noise target was flagged as predictable. Peak RSS includes the input DataFrame and the Python process itself; the RSS increase measures the change from the pre-profile process baseline. These are reproducible synthetic checks, not guarantees for every data distribution — see the benchmark notes for methodology and the recorded JSON.

What bounded search can miss

Samples can miss rare patterns: a relationship visible in 0.01% of rows may not appear in a 20,000-row sample. Shortlisting can miss weak signals and interaction-only signals: a pair of columns that predict a target only together may not survive per-column screening. Capped searches stop early: beyond 496 pairs (standard), the profiler still screens every numeric pair but computes detailed statistics only for the strongest.

Coverage records sample sizes and search caps, and each finding includes its validation scope. A run that reports no relationship may still have missed one. Increase the budget (deep, sample_rows, max_pairs) or test a specific hypothesis directly.

Tuning for a big table

The overrides from Coverage, budgets, and deadlines apply directly:

# wider sampling when the standard caps feel tight
report = onceover.profile(df, sample_rows=50_000, max_predictors=96,
                          max_pairs=2_000)

# or a wall-clock budget; coverage lists passes skipped after the deadline
report = onceover.profile(df, deadline_seconds=30.0)

Sample and pair overrides change the amount of relationship work. A deadline can skip later passes. Neither reduces the rows examined by full-data passes that complete.