Do pipeline ao relatório com o pacote pointblank
Esta aula foi construída para que você compreenda como validar dados sistematicamente com o
pointblank, identificar problemas antes que contaminem a análise, gerar relatórios de qualidade e integrar validação em pipelines de dados. Os dados usados são um dataset de RH simulado com problemas reais e deliberados.
Que cada gole desperte uma nova ideia.
Que cada script abra uma nova conversa.
Que o Café com R se torne um ponto de encontro nosso.
SITE OFICIAL > https://jenniferlopes.github.io/meu_site/
pointblankUma análise começa com dados. Se os dados têm problemas não detectados, a análise produz resultados incorretos, e você só descobre quando o dano já está feito.
Problemas comuns que passam despercebidos:
"ativo", "ATIVO", "Ativo")Sem validação sistemática, você depende de inspeção manual, que falha conforme os dados crescem.
Limpeza transforma os dados: corrige valores, remove duplicatas, padroniza texto.
Validação verifica se os dados atendem às expectativas definidas e reporta o que falhou, sem alterar os dados.
A sequência correta no pipeline:
Dados brutos
|
| Validação inicial: os dados chegaram como esperado?
v
Limpeza e transformação
|
| Validação final: o resultado da transformação está correto?
v
Análise e modelagem
| Workflow | Nome | Quando usar |
|---|---|---|
| VALID-I | Data Quality Reporting | Relatório periódico de qualidade |
| VALID-II | Pipeline Data Validation | Dentro de ETL com ações automáticas |
| VALID-III | Expectations in Unit Tests | Com testthat em pacotes R |
| VALID-IV | Data Tests for Conditionals | Retornar TRUE/FALSE para condicionais |
| VALID-V | Table Scan | Exploração rápida sem plano definido |
Esta aula cobre os workflows I, II e V, que são os mais usados no dia a dia de análise de dados.
tribble(
~Coluna, ~Problema, ~Qtd,
"id_funcionario", "Valores NA", "2",
"id_funcionario", "IDs duplicados (101, 102, 103)", "3",
"nome", "Valores NA", "5",
"departamento", "Capitalização inconsistente", "3",
"departamento", "Valores fora do conjunto válido", "2",
"salario", "Valores negativos ou zero", "3",
"salario", "Valores extremos (>R$300.000)", "2",
"data_admissao", "Datas no futuro", "2",
"status", "Valores fora do conjunto válido", "5",
"cpf", "Formato inválido ou NA", "5",
"avaliacao_anual", "Valores fora do intervalo [1,10]", "3")# A tibble: 11 × 3
Coluna Problema Qtd
<chr> <chr> <chr>
1 id_funcionario Valores NA 2
2 id_funcionario IDs duplicados (101, 102, 103) 3
3 nome Valores NA 5
4 departamento Capitalização inconsistente 3
5 departamento Valores fora do conjunto válido 2
6 salario Valores negativos ou zero 3
7 salario Valores extremos (>R$300.000) 2
8 data_admissao Datas no futuro 2
9 status Valores fora do conjunto válido 5
10 cpf Formato inválido ou NA 5
11 avaliacao_anual Valores fora do intervalo [1,10] 3
O agent é o objeto central do pointblank. Ele recebe a tabela alvo, acumula as etapas de validação e executa o plano completo quando interrogate() é chamado.
dados |>
create_agent(label = "Descrição") |>
funcao_validacao_1(...) |>
funcao_validacao_2(...) |>
interrogate()O interrogate() retorna o agente enriquecido com os resultados. Chamar o objeto no console abre o relatório interativo.
agente_rh <- rh_dados |>
create_agent(
label = "Validação de Dados de RH",
actions = action_levels(warn_at = 0.05,
stop_at = 0.20)) |>
col_vals_not_null(
columns = c(id_funcionario, nome,
departamento, salario,
data_admissao, status)) |>
rows_distinct(
columns = id_funcionario) |>
col_vals_in_set(
columns = departamento,
set = c("TI", "RH", "Financeiro",
"Marketing", "Operações")) |>
col_vals_in_set(
columns = status,
set = c("Ativo", "Inativo", "Férias")) |>
col_vals_between(
columns = salario,
left = 1320, right = 200000) |>
col_vals_between(
columns = avaliacao_anual,
left = 1, right = 10, na_pass = TRUE) |>
col_vals_lte(
columns = data_admissao,
value = Sys.Date()) |>
col_vals_regex(
columns = cpf,
regex = "^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$",
na_pass = TRUE) |>
interrogate()
agente_rhNote
Ao chamar o objeto agente_rh no console do R, o pointblank abre o relatório HTML interativo com todas as etapas, resultados e proporções de falha.
# Exemplo de colunas retornadas (nomes variam por versão do pointblank):
# i type/assertion_type columns n n_passed n_failed f_failed warn stop
# 1 col_vals_not_null ... 200 193 7 0.035 TRUE FALSE
# 2 rows_distinct ... 200 197 3 0.015 FALSE FALSE
# As linhas com departamento fora do conjunto permitido:
# id_funcionario nome departamento status
# 196 Funcionário 196 Recursos_Humanos ativo
# 198 Funcionário 198 MARKETING ATIVO
# 199 Funcionário 199 operações Demitido
get_data_extracts() retorna exatamente as linhas que violaram aquela etapa. Use para investigar os problemas e decidir como corrigi-los.
Limiares definem a proporção de falhas que aciona um comportamento específico:
warn_at: aviso - a proporção de falhas ultrapassou o limite aceitável, mas o pipeline pode continuar.
stop_at: erro - a proporção de falhas ultrapassou o limite crítico. O pipeline deve ser interrompido.
notify_at: notificação - aciona uma função customizada, como envio de e-mail ou registro em log.
Os limiares podem ser definidos como proporção (0 a 1) ou como contagem absoluta de falhas.
# Limiar global: aplica a todas as etapas do agente
limiares <- action_levels(
warn_at = 0.05,
stop_at = 0.20,
notify_at = 0.10)
# Limiar por etapa: sobrescreve o global
agente_personalizado <- rh_dados |>
create_agent() |>
col_vals_not_null(
columns = id_funcionario,
actions = action_levels(warn_at = 1)) |>
col_vals_between(
columns = salario,
left = 1320,
right = 200000,
actions = action_levels(
warn_at = 0.03,
stop_at = 0.10)) |>
interrogate()registrar_aviso <- function(object) {
msg <- glue::glue(
"[{Sys.time()}] AVISO: etapa '{object$name}' ",
"atingiu {round(object$f_failed * 100, 1)}% de falhas.")
write(msg, file = "validacao_log.txt", append = TRUE)
message(msg)
}
limiares_com_log <- action_levels(
warn_at = 0.05,
fns = list(warn = ~ registrar_aviso(.)))
rh_dados |>
create_agent(actions = limiares_com_log) |>
col_vals_between(
columns = salario,
left = 1320,
right = 200000) |>
interrogate()Forma 1 - Filtro direto: a tabela passa pela validação e lança stop() se qualquer unidade falhar.
Forma 2 - expect_*(): para uso com testthat. Falha com expectation failed.
Forma 3 - test_*(): retorna TRUE ou FALSE para uso em condicionais.
departamentos_validos <- test_col_vals_in_set(
rh_dados,
columns = departamento,
set = c("TI", "RH", "Financeiro",
"Marketing", "Operações"))
if (!departamentos_validos) {
message("Dados com departamentos inválidos.",
" Aplicando padronização.")
rh_dados <- rh_dados |>
mutate(departamento = str_to_title(
str_replace_all(departamento, "_", " ")))
} else {
message("Departamentos validados com sucesso.")
}[1] FALSE
test_col_vals_in_set() retornou FALSE porque existem valores como "Recursos_Humanos", "MARKETING" e "operações" fora do conjunto permitido.
| Função | O que verifica |
|---|---|
col_vals_not_null() |
Ausência de NA |
col_vals_between() |
Valor dentro de intervalo [left, right] |
col_vals_gt() |
Valor maior que |
col_vals_gte() |
Valor maior ou igual a |
col_vals_lt() |
Valor menor que |
col_vals_lte() |
Valor menor ou igual a |
| Função | O que verifica |
|---|---|
col_vals_in_set() |
Valor dentro de um conjunto permitido |
col_vals_not_in_set() |
Valor fora de um conjunto proibido |
col_vals_regex() |
Valor corresponde a expressão regular |
col_vals_ne() |
Valor diferente de |
col_vals_outside() |
Valor fora de um intervalo |
| Função | O que verifica |
|---|---|
rows_distinct() |
Linhas únicas por coluna-chave |
rows_complete() |
Linhas sem nulos em nenhuma coluna |
col_exists() |
Coluna existe na tabela |
col_is_numeric() |
Coluna é numérica |
col_is_character() |
Coluna é texto |
col_is_date() |
Coluna é data |
rh_dados |>
create_agent(label = "Estrutura da tabela") |>
rows_distinct(columns = id_funcionario) |>
col_exists(
columns = c(id_funcionario, nome, salario)) |>
col_is_numeric(
columns = c(salario, avaliacao_anual)) |>
col_is_character(
columns = c(nome, departamento)) |>
col_is_date(columns = data_admissao) |>
interrogate()Note
O relatório interativo do pointblank é gerado ao rodar este código localmente. Ele exibe cada etapa com status verde (passou) ou vermelho (falhou), proporção de falhas e métricas detalhadas.
Note
conjointly() avalia as três condições juntas por linha. Uma linha falha se qualquer uma delas não for atendida. No dataset de RH, as linhas com salário negativo, zero ou acima de R$200.000 são marcadas como falha nessa etapa.
scan_data() gera um relatório completo de análise exploratória sem precisar definir um plano de validação. É o equivalente a um skimr::skim() com relatório HTML interativo.
O que o relatório inclui:
O - Overview: linhas, colunas, nulos totais e tipos.
V - Variables: para cada coluna: valores únicos, proporção de nulos, distribuição numérica e frequência categórica.
M - Missing: padrões de valores ausentes entre colunas.
S - Sample: primeiras e últimas linhas.
Tip
Use scan_data() antes de definir o plano de validação. O relatório revela os problemas e ajuda a calibrar os limiares corretos para cada coluna.
O informant mantém metadados sobre a tabela e os atualiza automaticamente quando os dados mudam. O resultado é um dicionário de dados vivo: um relatório HTML com descrição de cada coluna, estatísticas atualizadas e notas do analista.
informant_rh <- rh_dados |>
create_informant(
label = "Dicionário de Dados de RH") |>
info_tabular(
description = "
Tabela de funcionários com dados de identificação,
cargo, remuneração e avaliação. Atualizada mensalmente.
Fonte: Sistema HRIS.",
`Responsável` = "Equipe de RH",
`Contato` = "rh@empresa.com.br") |>
info_columns(
columns = id_funcionario,
info = "Identificador único. Não nulo, não duplicado.") |>
info_columns(
columns = salario,
info = "Salário bruto mensal em reais (R$1.320 a R$200.000).") |>
info_columns(
columns = avaliacao_anual,
info = "Nota de desempenho anual. Escala de 1 a 10.") |>
incorporate()
informant_rhNote
O informant gera um relatório HTML com o dicionário de dados atualizado. Cada coluna aparece com sua descrição, tipo, valores únicos e proporção de nulos calculados automaticamente por incorporate().
library(duckdb)
con <- dbConnect(duckdb())
duckdb_register(con, "rh", rh_dados)
tbl(con, "rh") |>
create_agent(
label = "Validação RH - DuckDB",
actions = action_levels(warn_at = 0.05,
stop_at = 0.20)) |>
col_vals_not_null(
columns = c(id_funcionario, nome, salario)) |>
rows_distinct(columns = id_funcionario) |>
col_vals_in_set(
columns = departamento,
set = c("TI", "RH", "Financeiro",
"Marketing", "Operações")) |>
col_vals_between(
columns = salario,
left = 1320,
right = 200000) |>
interrogate()
dbDisconnect(con)Note
O código de validação sobre uma tabela DuckDB é idêntico ao usado com um data.frame. O pointblank detecta automaticamente a fonte e traduz as validações para SQL quando necessário.
Ingestão (leitura de CSV, API, banco)
|
| scan_data(): explorar antes de validar
| col_vals_not_null(), col_is_*(): verificar estrutura
v
Transformação (limpeza, join, agregação)
|
| test_*(): decisões baseadas em qualidade
| col_vals_between(): verificar intermediários
v
Saída (escrita em banco, Parquet, relatório)
|
| create_agent() + interrogate(): relatório completo
| yaml_write(): versionar o plano de validação
v
Monitoramento (execução periódica com agendamento)
O arquivo YAML gerado pode ser versionado no Git junto com o projeto, funcionando como um contrato de dados: define formalmente o que a tabela deve conter.
| Situação | Causa | Solução |
|---|---|---|
| Relatório vazio ou sem falhas | interrogate() não foi chamado |
Sempre terminar com interrogate() |
| NA contado como falha | na_pass não configurado |
Adicionar na_pass = TRUE quando NA é válido |
get_data_extracts() vazio |
Índice i incorreto |
Verificar o número da etapa no relatório |
| Limiar muito restritivo | stop_at = 0.01 para coluna com ausência esperada |
Calibrar com scan_data() antes de definir |
| Função | Workflow | O que faz |
|---|---|---|
create_agent() |
I | Cria o agente de validação |
interrogate() |
I | Executa o plano e gera o relatório |
get_agent_report() |
I | Extrai o relatório como tibble |
get_data_extracts() |
I | Retorna as linhas que falharam |
action_levels() |
I, II | Define limiares e ações |
| Função | Workflow | O que faz |
|---|---|---|
scan_data() |
V | Análise exploratória com relatório HTML |
create_informant() |
Info | Cria o dicionário de dados |
incorporate() |
Info | Atualiza estatísticas do informant |
yaml_write() |
I | Exporta o plano para YAML |
test_*() |
IV | Retorna TRUE/FALSE para condicionais |
expect_*() |
III | Para uso com testthat |
Continue praticando e explorando.
Esta aula é parte do projeto Café com R. É open source. Use, compartilhe e adapte.
Que cada gole desperte uma nova ideia.
Que cada script abra uma nova conversa.
Que o Café com R se torne um ponto de encontro nosso.
SITE OFICIAL > https://jenniferlopes.github.io/meu_site/

Jennifer Lopes | Café com R