Skip to content

Freeze & Thaw — API Core


freeze()

Comprime um pd.DataFrame para o formato .permafrost.

metrics = pf.freeze(
    df,                          # pd.DataFrame
    "arquivo.permafrost",        # caminho de saída
    codec=pf.CODEC_LZMA2,       # codec de compressão
    quant=pf.QUANT_NONE,        # nível de quantização
    partition_by="ano",          # coluna de partição (opcional)
    chunk_rows=10_000,           # linhas por chunk
    comment="meu dataset",       # comentário embutido no arquivo
    retention_days=0,            # 0 = sem data de expiração
)

Codecs disponíveis

Constante Algoritmo Quando usar
CODEC_LZMA2 XZ/LZMA2 extreme Cold storage — melhor ratio, aceita freeze mais lento
CODEC_ZSTD Zstandard L19 Warm storage — decompress 6× mais rápido que LZMA2

Preditores colunares

O Permafrost detecta automaticamente o melhor encoding para cada coluna:

Tipo de dado Preditor Mecanismo Ganho típico
IDs sequenciais delta_zigzag Armazena diferenças entre valores 4–8×
Floats monetários lag1_zigzag Armazena resíduo vs valor anterior 3–6×
Timestamps ts_delta_s Delta em segundos Unix 4–8×
Categorias (≤256) category_u8 Índice de 1 byte por valor 6–12×
Texto livre raw_text UTF-8 com separador \x00 2–4×
# Ver qual preditor foi escolhido para cada coluna
info = pf.audit("arquivo.permafrost")
for col, manifest in info["col_predictors"].items():
    print(f"  {col}: {manifest}")

Retorno: métricas do freeze

{
    "path":           "arquivo.permafrost",
    "rows":           80000,
    "cols":           9,
    "n_chunks":       16,
    "chunk_rows":     5000,
    "original_mb":    5.85,
    "stored_mb":      0.678,
    "ratio":          8.37,
    "reduction_pct":  88.0,
    "freeze_s":       2.23,
    "codec":          "lzma2",
    "partition_by":   "ano",
    "index_entries":  16,
}

unfreeze()

Descomprime um arquivo .permafrost de volta para pd.DataFrame.

# Thaw completo
df = pf.unfreeze("arquivo.permafrost", verify=True)

# Thaw seletivo por partição
df_2023 = pf.unfreeze("arquivo.permafrost", filter={"ano": 2023})

# Thaw por range de linhas
df_sample = pf.unfreeze("arquivo.permafrost", row_range=(0, 9_999))

# Sem verificação SHA-256 (mais rápido, para dados confiáveis)
df = pf.unfreeze("arquivo.permafrost", verify=False)

Como o sparse index funciona

arquivo.permafrost (footer)
    ...
    [SPARSE INDEX JSON]    ← byte_offset de cada chunk
    [INDEX_LEN: 4B]
    [INDEX_SHA256: 32B]
    [EOF: "SMRP" 4B]      ← leitura de trás para frente

thaw(filter={"ano": 2021}):
  1. Lê os últimos ~8KB (sparse index)
  2. Filtra chunks onde part_key contém "2021"
  3. Lê APENAS esses bytes do arquivo
  4. Descomprime e retorna

Benchmark

Para um arquivo com 5 anos de dados, filter={"ano": 2021}12–31% do arquivo em vez de 100%.


audit()

Lê metadados sem descomprimir nenhum chunk.

info = pf.audit("arquivo.permafrost")

Retorna:

{
    "version":         "1.2",
    "codec":           "lzma2",
    "quant":           0,           # 0=lossless
    "freeze_date":     "2026-05-13T14:30:00",
    "orig_rows":       80000,
    "n_chunks":        16,
    "chunk_rows":      5000,
    "file_size_mb":    0.678,
    "columns":         ["id", "data", "ano", ...],
    "partition_col":   "ano",
    "partition_keys":  ["2020", "2021", "2022", ...],
    "comment":         "Vendas 2022-2024",
    "index_entries":   [...],       # sparse index completo
}

Integridade e Bit-rot

O Permafrost verifica SHA-256 em 3 camadas:

[HEADER SHA-256]  ← verificado antes de parsear o header
[CHUNK SHA-256]   ← verificado antes de descomprimir cada chunk
[INDEX SHA-256]   ← verificado antes de usar o sparse index
# Corrupção é detectada ANTES da decompressão:
try:
    df = pf.unfreeze("arquivo.permafrost", verify=True)
except ValueError as e:
    print(e)
    # "Header SHA-256 inválido — arquivo modificado"
    # "Chunk 3 corrompido — SHA-256 não confere"
    # "EOF magic ausente — arquivo truncado"

Cenários detectados

  • Bit-rot em storage (disco com setores ruins)
  • Transferência parcial/corrompida
  • Arquivo modificado acidentalmente
  • Truncamento por falta de espaço em disco