Qualidade de dados
As regras que impedem o relatório errado — e por que elas rodam junto do pipeline, não depois.
Um pipeline que termina sem erro não prova que os dados estão certos. Ele prova que nada estourou — e a diferença entre as duas coisas é onde vive o relatório errado que ninguém contesta.
As seis perguntas#
| Dimensão | A pergunta | Como se mede |
|---|---|---|
| completude | falta alguma coisa? | quantos vazios por coluna |
| unicidade | há repetido? | contagem de chaves distintas × total |
| validade | o valor faz sentido? | faixa, formato, lista de valores aceitos |
| consistência | as partes concordam? | o total bate com a soma das partes? |
| pontualidade | o dado é de hoje? | a data mais recente × agora |
| volume | veio a quantidade esperada? | linhas de hoje × a média dos últimos dias |
Escrever uma regra#
record Regra:
nome: String
grave: Boolean := yes
action conferir(dados, regra, teste):
falhas := [l cycle l in dados given not teste(l)]
yield {"regra": regra.nome,
"grave": regra.grave,
"falhas": len(falhas),
"total": len(dados),
"exemplos": falhas[0:3]}resultados := [
conferir(vendas, Regra("valor positivo"), lambda l: l["valor"] > 0.0),
conferir(vendas, Regra("regiao conhecida"),
lambda l: l["regiao"] in ["sul", "norte"]),
conferir(vendas, Regra("qtd inteira", no), lambda l: l["qtd"] >= 1)
]
cycle r in resultados:
marca := "x" given r["falhas"] > 0 otherwise " "
out $"[{marca}] {r['regra']}: {r['falhas']} de {r['total']}"Grave interrompe; aviso não#
Toda regra precisa de uma resposta declarada para "e se falhar?". Sem isso, ou o pipeline para por qualquer coisa, ou nunca para por nada:
action decidir(resultados):
graves := [r cycle r in resultados
given r["grave"] and r["falhas"] > 0]
given len(graves) > 0:
trigger $"{len(graves)} regra(s) grave(s) falharam; o carregamento nao aconteceu"
avisos := [r cycle r in resultados given r["falhas"] > 0]
cycle a in avisos:
out $"aviso: {a['regra']} — {a['falhas']} linha(s)"
yield yes| Gravidade | O que fazer |
|---|---|
| grave | não carregar. Um dado errado publicado é pior que um relatório atrasado |
| aviso | carregar, registrar, e olhar amanhã |
| informativo | só o número, para acompanhar a tendência |
Onde as regras rodam#
Junto do pipeline, entre transformar e carregar — não num relatório separado que alguém abre na sexta.
action rodar(caminho, db):
brutas := extrair(caminho)
limpas := transformar(brutas)
resultados := conferir_tudo(limpas)
decidir(resultados) // para aqui se houver grave
yield carregar(db, limpas)A razão é simples: a única hora em que alguém consegue agir sobre um dado ruim é antes de ele virar a fonte de um painel.
Guardar o resultado das regras#
Gravar a saída das verificações a cada execução transforma "os números pareciam estranhos" em uma série temporal:
IO.write_json($"qualidade/{data}.json", {
"data": data,
"linhas": len(limpas),
"regras": resultados
})Com um histórico, a regra de volume deixa de precisar de número mágico: ela compara com a média dos últimos dias.
Testar as regras#
Uma regra de qualidade é código, e código sem teste dá falso negativo em silêncio — a regra que nunca acusa nada parece estar tudo bem:
adopt Arcane.Crucible as C
crucible "regras":
trial "valor negativo e pego":
r := conferir([{"valor": -1.0}], Regra("positivo"),
lambda l: l["valor"] > 0.0)
expect r["falhas"] is 1
trial "valor positivo passa":
r := conferir([{"valor": 5.0}], Regra("positivo"),
lambda l: l["valor"] > 0.0)
expect r["falhas"] is 0
C.run()