Skip to main content

TCF (Tabular Compact Format) — string compression via OBAT (Online Bidirectional Affix Tokenizer) + HCC (Hierarchical Compositional Coding)

Project description

TCF · Tabular Compact Format

CI Python License Version Format

E se desse pra transmitir a mesma tabela com bem menos bytes, sem virar um arquivo binário que ninguém mais consegue abrir e ler?

Um cadastro pequeno, nos três formatos (bytes reais, saída de verdade):

JSON (480 B): repete o nome de cada campo em toda linha.

[ { "nome": "Ana Souza",  "email": "ana@acme.com.br",
    "cidade": "Sao Paulo", "plano": "Premium" },
  { "nome": "Bruno Lima", "email": "bruno@acme.com.br",
    "cidade": "Sao Paulo", "plano": "Premium" },  ]

CSV (213 B): tira os nomes repetidos, uma linha por registro.

nome,email,cidade,plano
Ana Souza,ana@acme.com.br,Sao Paulo,Premium
Bruno Lima,bruno@acme.com.br,Sao Paulo,Premium
Carla Nunes,carla@acme.com.br,Sao Paulo,Basic
Diego Rocha,diego@acme.com.br,Rio de Janeiro,Premium

TCF (177 B, formato 0.7, saída real do encode): o que se repete vira referência.

#TCF.7 M
!44=nome,42=email,28=cidade,plano
Ana Souza
Bruno Lima
Carla Nunes
Diego Rochaan*a*@acme.com.br
brun*o3
carl2,3
dieg5,3
*3|Sao Paulo
Rio de Janeiro
*2|Premium
Basic
^1

Como ler:

  • Linha 1, shebang: #TCF.7 M é o formato 0.7, multi-coluna.
  • Linha 2, meta das colunas (tamanho=nome). O ! marca uma coluna guardada crua (quando o raw fica menor que o TCF). A última (plano) não leva tamanho: vai até o fim.
  • Os corpos vêm concatenados, delimitados por tamanho, não por quebra de linha. Por isso a coluna crua nome (…Diego Rocha) emenda direto no e-mail (an*a*…).
  • No corpo: *3|Sao Paulo é "Sao Paulo, 3×" (repetição). ^1 é "igual à linha 1" (substituição).
  • Na coluna de e-mail o TCF vai mais fundo (prefixo único + domínio comum referenciado). É onde mais economiza, e onde o texto fica mais denso.

JSON repete a estrutura inteira. CSV repete os valores. O TCF fatora o que se repete e referencia o resto, continuando texto ASCII que você abre e lê.

Mas quanto mais fundo ele fatora (veja o e-mail), mais denso o texto fica. Legível não quer dizer óbvio à primeira vista.

Em tabelas grandes a diferença cresce: ver Resultados.

O que é o TCF

Um formato textual e sem perdas (decode(encode(x)) == x) para tabelas de strings.

Comprime parecido com um zip/gzip, mas com uma diferença: o resultado continua texto ASCII que você abre e inspeciona, sem descomprimir. Não fica tão óbvio quanto o original (quanto mais o TCF fatora, mais denso o texto), mas nunca vira um blob opaco. Cada coluna passa por um pipeline próprio.

É essa a faixa que o TCF ocupa: compacto como um compressor, inspecionável como texto. (Precisa de ratio máximo? Dá pra rodar gzip/brotli por cima: eles se compõem.)

Como ele faz isso: OBAT + HCC

Duas camadas, explicadas pelo propósito (specs: docs/algorithms/).

OBAT (Online Bidirectional Affix Tokenizer) acha o que as strings têm em comum. Para cada valor, procura o maior prefixo e sufixo compartilhado com os anteriores (domínios de e-mail, raízes de URL, códigos da mesma família). Escreve o trecho uma vez e referencia o resto.

É um front-coding bidirecional: generaliza o front-coding clássico de dicionários de strings (Witten et al.; HTFC/RPDac, Brisaboa et al.). O "bidirecional" é o que captura o sufixo comum (@acme.com.br), não só o prefixo.

A busca por afixos é da família das árvores de prefixo/sufixo: tries, Patricia/radix tree (Morrison 1968), suffix trees. Na prática o OBAT acelera essa busca com um índice de trigramas, que derruba o custo de O(N²) ingênuo para ~O(N^1.42) (sub-quadrático, quase-linear). (Trocar o índice por uma Patricia trie é candidato futuro: exploração.)

HCC (Hierarchical Compositional Coding) decide o que vale a pena nomear e agrupa repetições. Pega os tokens do OBAT, fatora composições recorrentes em referências nomeadas reutilizáveis (operador ~) e colapsa repetidos (RLE, inclusive sequências quase-iguais, tipo IDs que só mudam no fim).

Como referência aponta para referência, o resultado é um grafo acíclico (DAG) de fragmentos: na prática uma gramática / straight-line program do conteúdo. É o espírito do Re-Pair (Larsson & Moffat 1999) e do Sequitur (Nevill-Manning & Witten 1997), mas operando sobre os tokens do OBAT (não sobre bytes) e com operadores próprios (~ cria nó nomeado, , só concatena).

É o que mantém a saída pequena e inspecionável: os grupos *N|... ficam à vista.

Velocidade. O lado caro é o encode (a busca de afixos do OBAT), trazido a quase-linear pelo índice de trigramas (mais o acelerador Cython opcional). O decode é uma passada linear única: só expande as referências (lookups O(1)) e os grupos RLE, sem nenhuma busca. Rápido e previsível.

Getting started (1 minuto)

from tcf import encode, decode

# Single-column: lista de strings
text = encode(["joao@gmail.com", "maria@gmail.com", "pedro@gmail.com"])
assert decode(text) == ["joao@gmail.com", "maria@gmail.com", "pedro@gmail.com"]

# Multi-column: dict de colunas
table = {
    "id":    ["1", "2", "3"],
    "email": ["joao@gmail.com", "maria@gmail.com", "pedro@gmail.com"],
}
text = encode(table)
assert decode(text) == table  # round-trip lossless

# Naturezas (opt-in): CPF/CNPJ/IP comprimidos sem digito verificador/padding
from tcf import SPEC_CPF
text = encode(["111.444.777-35", "529.982.247-25"], nature=SPEC_CPF)

encode dispatcha por tipo (list → single-column, dict → multi-column). decode roteia pelo shebang.

Tutorial passo-a-passo: docs/tutorials/getting-started.md. Guias praticos: docs/how-to/.

Formato 0.7 (default): onde os bytes vão

O encode multi-coluna sai em 0.7 / #TCF.7 por default (ADR-0024). Quatro coisas, todas automáticas (sem flag), cada coluna escolhendo a menor representação:

  • Fallback por coluna. Guarda a coluna em raw quando o raw fica menor que o TCF ("nunca pior que raw"). Marcada com ! no meta (ADR-0022).
  • Dicionário low-card. Coluna com poucos valores distintos vira tabela de únicos + índices compactos, em vez de um ref por linha. Marcada com @ no meta (ADR-0025).
  • Split estrutural. Valor estruturado (decimal, data, datetime, CPF) com template uniforme vira campos separados (o template guardado uma vez), e cada campo low-card cai no dicionário. Marcada com % no meta (ADR-0026).
  • Header mínimo. O flag M no shebang já declara que vêm colunas, então o meta dispensa o prefixo # . E a última coluna não leva tamanho, vai até o fim (ADR-0023).
text = encode(table)        # 0.7 / #TCF.7, é o default, sem flags

# knobs opt-out (default True) — pra modificar o comportamento / inspecionar:
text = encode(table, fallback=False, min_header=False)  # força o legado #TCF.6
text = encode(table, min_header=False)                  # #TCF.7 com header verboso
text = encode(table, min_len=5)                         # override do min_len do OBAT (default: auto)
text = encode(table, sort_by="cidade")                  # ordena linhas pela coluna (order-free, +compressão)

sort_by reordena as linhas pela coluna (agrupa similares → menos bytes, 5-15% com chave low-card). É order-free: o decode devolve a ordem ordenada, não a original. Use só quando a ordem das linhas não importa.

No cadastro de 4 colunas do topo, comparado ao formato legado #TCF.6:

formato meta line bytes
0.7 / #TCF.7 (default) !44=nome,42=email,28=cidade,plano 177
#TCF.6 (legado) # 45=nome,42=email,28=cidade,20=plano 182

O ganho é proporcionalmente maior em payloads pequenos (o header de tamanho fixo domina).

Pré-1.0, o encoder só escreve o formato mais novo. O #TCF.6 legado ainda é lido pelo decoder, e git checkout reproduz a era 0.6 (ADR-0024). O dicionário low-card (V2-B) e o split estrutural já estão no default; a compressão lossy fica no roadmap.

Estado (pré-1.0)

  • Pré-1.0 (ADR-0024). Os minors do formato (#TCF.4/.5/.6/.7) são iterações de desenvolvimento rumo a um 1.0 sólido, sem compat rígida entre eles (git reproduz versões antigas). v2.0 fica pra depois.
  • Implementação canônica em src/tcf/. Round-trip sempre lossless (decode(encode(x)) == x).
  • Default 0.7 / #TCF.7: fallback (ADR-0022) + header mínimo (ADR-0023), ver seção acima. O #TCF.6 legado é lido pelo decoder.
  • Suíte: 398 passed, 1 xfailed. Baselines de byte = guardas de regressão, re-pináveis em mudança intencional (ADR-0024).
  • Mudanças: CHANGELOG.md. História M0-M14: experiments/lab/dirty/notas/historia-dirty-lab.md.

O ciclo v0.5 (formato columnar para LLM benchmark) é acessório e vive separado. Ver a seção "Benchmark LLM v0.5" mais abaixo.

Resultados

Sem nenhum compressor, o TCF é o formato de texto mais compacto do conjunto. Nos 15 datasets sintéticos do EXP-008:

formato (texto puro, sem compressor) bytes
TCF 3131
CSV 4872
JSON 5409
JSONL 7001

~36% menor que CSV e ~42% menor que JSON, continuando legível.

Núcleo pinado em testes: D1-D9 = 1523 B (51.1% do raw, single-col); D17a multi-col = 303 B (0.7 com V2-B; legado #TCF.6 = 322 B). Real-world multi-coluna (9 tabelas Adult + TPC-H, 136k linhas): −33.02% weighted vs CSV raw.

E contra gzip / brotli / zstd? Outra categoria: são compressores binários opacos (precisa descomprimir pra ler qualquer coisa). No ratio puro eles ganham (no EXP-008, csv+brotli = 1742 B contra tcf+brotli = 2141 B). O TCF troca um pouco de ratio por legibilidade e se compõe com eles (rodar gzip por cima do TCF funciona).

Tabelas completas: reports do EXP-008.

First-time setup (dev)

# Clone + install dev deps
git clone https://github.com/LeoPR/TCF.git && cd TCF
pip install -e ".[dev]"

# (recomendado) instalar pre-commit hooks
pre-commit install

# Rodar hooks em todos arquivos (opcional, baseline)
pre-commit run --all-files

Hooks configurados (ver .pre-commit-config.yaml):

  • ruff lint + format
  • detect-secrets (scan)
  • basicos: trailing-whitespace, end-of-file-fixer, check-merge-conflict, check-added-large-files
  • custom: bloqueia cache dirs (__pycache__/, .pytest_cache/, etc.) acidentalmente staged

How to cite

Ver CITATION.cff. GitHub renderiza badge "Cite this repository" na pagina do repo automaticamente.


Benchmark LLM v0.5 (acessorio, projeto paralelo)

Esta secao resume o ciclo v0.5 (formato columnar para consumo por LLMs). NAO e' o algoritmo TCF v0.6 acima. Todo o material vive separado.

O ciclo v0.5 mediu compreensao de tabelas por LLMs (CSV/JSON/TOON/TCF, Linha A "LLM le e computa" + Linha B "LLM gera SQL"): 7 modelos comerciais

  • 13 locais, 2 datasets, 2256 registros, 38 findings. Usava o motor de niveis (EncodeConfig(level=N)) em old/tcf/. Ver old/tcf/LEVELS-REVIEW.md para a semantica L0–L3.

Candidato a spin-off (tcf-llm-tools) no futuro. Pode re-validar contra v0.6 se Phase 2 for revivida.


Repository layout

TCF/
├── src/tcf/                 ← CANONICAL v0.6 API (OBAT+HCC, encode/decode, #TCF.6)
├── old/tcf/                 ← motor v0.5 (niveis L0–L3), congelado-historico (ver LEVELS-REVIEW.md)
├── scripts/                 ← Shaper (stratified sampling), CSV→SQLite, setup_* datasets
├── experiments/lab/         ← labs v0.6 (dirty + clean): compressao composicional
├── llm-benchmark/           ← benchmark LLM v0.5 (harness: runners + llm_eval), acessorio
├── tests/                   ← pytest suite (v0.6)
├── datasets/                ← canonical metadata + samples (dados reais em Z:)
├── tickets/                 ← planejamento markdown (YAML frontmatter)
├── docs/
│   ├── algorithms/          ← specs canonicos v0.6 (OBAT, HCC, TCF-format) [reference]
│   ├── adr/                 ← decisoes numeradas, imutaveis
│   ├── theory/              ← fundamentos teoricos [explanation]
│   ├── how-to/, tutorials/  ← Diataxis
│   ├── findings/            ← catalogo cientifico v0.5 LLM (F-Q01..Q38) [historico]
│   ├── workbench/           ← dev timeline, research notes (partes em _archive/)
│   └── archive/             ← material v0.5/v0.1 congelado (manual_v05, article_v05, etc.)
├── config/                  ← storage.json (aponta Z:), api_keys (gitignored)
├── README.md                ← you are here
└── CHANGELOG.md             ← release history

Para o mapa detalhado, ver MAP.md. Os diretorios docs/manual/ e docs/article/ NAO existem; o material v0.5 correspondente esta em docs/archive/manual_v05/ e docs/archive/article_v05/.


Tools shipped (v0.6)

O encoder e' a ferramenta principal; auxiliares de suporte (NAO TCF-core):

  • Shaper (scripts/shaper/): stratified, FK-preserving sampling framework. Standalone-able as a separate library; see shaper-as-standalone-tool note
  • DatasetReader (scripts/dataset_reader.py): uniform interface over SQLite hubs (rows, columns, query, column_stats)
  • setup_*.py (scripts/): download/geracao dos datasets canonicos (Adult, TPC-H, IBGE, CNPJ, etc.); ver datasets/README.md

Pré-1.0: library-only (sem CLI; ver pyproject.toml). O benchmark LLM v0.5 (CommercialClient, M-series runners) vive em llm-benchmark/, com instrucoes de reproducao no README de la'.


Where to go next


License

MIT. See LICENSE.

Acknowledgements

Project conceived as part of an academic dissertation (TCC). Datasets: UCI Adult Census and TPC-H (via DuckDB tpch extension). (Ciclo v0.5) Commercial LLM testing supported by personal credits; total spend $9.46 USD for 1968 records (75% cache savings).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tcf_format-0.7.1.tar.gz (68.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tcf_format-0.7.1-py3-none-any.whl (62.1 kB view details)

Uploaded Python 3

File details

Details for the file tcf_format-0.7.1.tar.gz.

File metadata

  • Download URL: tcf_format-0.7.1.tar.gz
  • Upload date:
  • Size: 68.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tcf_format-0.7.1.tar.gz
Algorithm Hash digest
SHA256 ebe95aa9ee711067542f562a8fb6726939e37071edf14b799cb518786815336d
MD5 1de24ad8dcb14f22c3881db421e06398
BLAKE2b-256 a21646b0c5a152d4e4052538283a6d4d562cec5ed43d3340352e2aad17600241

See more details on using hashes here.

File details

Details for the file tcf_format-0.7.1-py3-none-any.whl.

File metadata

  • Download URL: tcf_format-0.7.1-py3-none-any.whl
  • Upload date:
  • Size: 62.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tcf_format-0.7.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3c71471a457a0c5f071171ed53c853bd129e0fc0154579bf8613de322a70516f
MD5 3b6103d1797f05cd91ff892cef72315c
BLAKE2b-256 67a89235ab59959752bd57493c8b0f3d1ac2f661312817a6e7a08de765079cb2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page