- Python 99.2%
- Shell 0.5%
- Makefile 0.3%
The v0.x core was an opinionated VALID/INVALID title ruleset. This replaces it with detectors that only emit measured patterns: concentration, outliers, ups/downs, associations, subgroups, clusters Benchmark is planted statistical structure (Pareto, crash, correlation, near-FD, 3× subgroup, outliers, missing-together) — 8/8 recovered. On real SEC 2026 companyfacts the same engine reports asset Gini 0.69, top-3 rows holding 55% of assets, and assets↔equity ρ=+0.87. |
||
|---|---|---|
| datasets | ||
| docs | ||
| fixtures | ||
| gold | ||
| loop | ||
| patternlab | ||
| results | ||
| tests | ||
| .gitignore | ||
| CHANGELOG.md | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| requirements.txt | ||
| run.sh | ||
patternlab
Unsupervised pattern and insight discovery for tabular and JSON data.
It does not know what a job title is, what “valid” means, or what a high-value contact is. It measures the table and writes down what is unusual: concentration, outliers, ups and downs, associations, subgroups, clusters.
./run.sh
Or on any file:
python3 -m patternlab analyze path/to/file.csv
python3 -m patternlab analyze path/to/file.json
python3 -m patternlab analyze path/to/file.jsonl --only concentration,outliers,trends
Python 3.11+, stdlib only. Repo: https://git.binary.ovh/brahm/patternlab
What a finding looks like
The top 10% of rows (12) hold 81% of the sum of 'wealth'.
'units' and 'revenue' move together (Spearman ρ=+0.97, n=400).
'sales' dropped 61% in 2025-10 (118 → 46).
When segment='enterprise', 'spend' is higher than the rest (3.1× the global mean).
8 outliers on 'spend' (values from 401 to 478; strongest robust z=+9.4).
'region' nearly determines 'tax_rate' (purity=97%).
Each line is a number taken from the file. Nothing is emitted because a lexicon said so.
Full map: docs/DISCOVERY.md.
Step by step
1. Clone
git clone https://git.binary.ovh/brahm/patternlab.git
cd patternlab
chmod +x run.sh
2. Run the lab
./run.sh
This:
- Fetches O*NET 30.0 and SEC 2026 lists (cached after the first time).
- Writes a synthetic table with known statistical structure.
- Checks that those structures are recovered (
results/latest/bench.md). - Writes findings for the synthetic table and the public tables.
3. Read
| file | what |
|---|---|
results/latest/planted_stats/insights.md |
did we see the planted Pareto / crash / correlation? |
results/latest/sec_companyfacts_2026/insights.md |
concentration and outliers in 2026 10-K numbers |
results/latest/onet_occupations/insights.md |
subgroups / associations in O*NET 30.0 |
results/latest/bench.md |
planted-pattern recall |
4. Point it at your file
CSV, TSV, JSON array, JSONL, nested JSON (flattened).
python3 -m patternlab analyze contacts.csv --out results/mine
--label some_column is optional. If you pass it, that column is treated
as a grouping field for subgroups. It is not a validity flag.
--only concentration,outliers,trends,association,subgroup,cluster runs a subset.
5. How we know it works
gold/planted_stats.jsonl is built with:
- Pareto
wealth(a few rows hold most of the sum) revenue ≈ 2.1 × unitssalescrash in the last quarterregionnearly determinestax_ratesegment=enterprisehas ~3×spend- 8 injected
spendoutliers statusdominated by one valueemailandphonemissing together
The bench asks whether those structures appear in the report.
Detectors
| detector | looks for |
|---|---|
concentration |
Gini, top-10% share, category dominance, HHI |
outliers |
IQR + MAD robust z; rows extreme on 2+ columns |
trends |
auto date column; overall up/down, biggest jump, biggest drop, recent vs prior |
association |
Spearman, Cramér's V, near functional dependencies, missing-together |
subgroup |
category slices where a numeric mean is ≥0.65σ from global |
cluster |
k-means on standardized numerics, described by distinctive features |
No domain lists. Thresholds live in patternlab/config.py.
Data used for the public demos
- O*NET 30.0 (US DOL, CC BY 4.0, 23 Jul 2026)
- SEC company tickers + a 39-issuer companyfacts sample (2026-preferring 10-K)
- ATS-shaped rows derived from those sources (no private ATS scrape)
The engine does not special-case any of them.
Versions
v0.1–v0.3 were an opinionated title-validation experiment. v1.0.0 replaces that core with the discovery engine above.
License
MIT. O*NET strings remain CC BY 4.0.