---
title: "Great Expectations com Python: Testes de Qualidade de Dados"
url: "https://python.dev.br/blog/great-expectations-qualidade-dados-python/"
markdown_url: "https://python.dev.br/blog/great-expectations-qualidade-dados-python.MD"
description: "Aprenda a usar Great Expectations com Python para testar qualidade de dados em CSV, Pandas e pipelines, com exemplos práticos para projetos no Brasil."
date: "2026-05-31"
author: "Equipe Python Brasil"
---

# Great Expectations com Python: Testes de Qualidade de Dados

Aprenda a usar Great Expectations com Python para testar qualidade de dados em CSV, Pandas e pipelines, com exemplos práticos para projetos no Brasil.


Qualidade de dados é uma das diferenças mais claras entre um script que roda uma vez e um projeto Python pronto para trabalho real. Em uma planilha pequena, talvez você perceba manualmente que uma coluna veio vazia, que uma data está no formato errado ou que um valor de venda ficou negativo. Em um pipeline recorrente, esse tipo de erro passa silenciosamente para dashboards, modelos, relatórios financeiros, automações de CRM e decisões de negócio.

O problema não é apenas técnico. Quem busca vaga de análise, engenharia de dados, QA de dados ou automação com Python precisa mostrar que sabe transformar dados em confiança. Um projeto que só lê CSV e gera gráfico é útil; um projeto que valida contrato de entrada, bloqueia dados suspeitos e deixa evidência do que foi testado comunica maturidade profissional.

O **Great Expectations** é uma das ferramentas mais conhecidas para esse trabalho. Ele permite declarar expectativas sobre dados: colunas obrigatórias, tipos, faixas, valores permitidos, unicidade, ausência de nulos, formatos de texto e regras customizadas. Em vez de espalhar `if` solto pelo pipeline, você cria uma suíte de validações que pode rodar localmente, no CI ou antes de carregar dados em produção.

Neste guia, vamos usar Great Expectations com [Pandas](/glossario/pandas/) para validar uma base simples, conectar o fluxo com [pytest](/guias/testes-com-pytest/), comparar com [Pandera](/blog/pandera-validacao-dados-python/) e [dbt para analytics engineering](/blog/dbt-python-analytics-engineering/) e mostrar como transformar o resultado em um projeto de portfólio voltado ao mercado brasileiro.

## Quando usar Great Expectations

Use Great Expectations quando os dados têm um contrato que precisa ser verificado de forma repetível. Alguns exemplos comuns:

| Cenário | Expectativas úteis |
|---|---|
| CSV de vendas | pedido único, valor positivo, data válida, status permitido |
| Base de clientes | e-mail não nulo, CPF com formato esperado, cidade preenchida |
| Pipeline de vagas | título obrigatório, empresa obrigatória, URL única, salário coerente |
| Dados públicos | código IBGE válido, UF permitida, ano dentro do intervalo esperado |
| Analytics | eventos com nome conhecido, usuário anônimo presente, timestamp válido |

Se você só precisa validar um `DataFrame` pequeno dentro do próprio código Python, Pandera pode ser mais direto. Se quer documentação de qualidade de dados, suítes reutilizáveis, relatórios de validação e uma ferramenta conhecida por times de dados, Great Expectations faz sentido.

O ponto principal é não tratar qualidade como etapa manual. Dados mudam. APIs quebram. Exportações de sistema ganham coluna nova. Alguém altera separador decimal. Uma validação automatizada transforma esse risco em sinal visível.

## Instalando o ambiente

Crie um projeto simples:

```bash
uv init qualidade-dados-python
cd qualidade-dados-python
uv add great-expectations pandas pytest
```

Com `pip`, o equivalente seria:

```bash
python -m venv .venv
source .venv/bin/activate
pip install great-expectations pandas pytest
```

Monte esta estrutura:

```text
qualidade-dados-python/
  data/
    vendas.csv
  src/
    validar_vendas.py
  tests/
    test_qualidade_vendas.py
```

O arquivo `data/vendas.csv` pode começar assim:

```csv
pedido_id,cliente_email,uf,valor,status,data_pedido
1001,ana@example.com,SP,129.90,pago,2026-05-20
1002,bruno@example.com,MG,89.50,enviado,2026-05-21
1003,carla@example.com,PE,240.00,cancelado,2026-05-22
```

Essa base parece simples, mas já tem regras de negócio: `pedido_id` não deve repetir, `uf` precisa estar em uma lista conhecida, `valor` não pode ser negativo e `status` deve ficar dentro dos valores aceitos.

## Validando um DataFrame com expectativas

Crie `src/validar_vendas.py`:

```python
from pathlib import Path

import great_expectations as gx
import pandas as pd


UFS_BRASIL = [
    "AC", "AL", "AP", "AM", "BA", "CE", "DF", "ES", "GO",
    "MA", "MT", "MS", "MG", "PA", "PB", "PR", "PE", "PI",
    "RJ", "RN", "RS", "RO", "RR", "SC", "SP", "SE", "TO",
]


def carregar_vendas(caminho: str | Path) -> pd.DataFrame:
    return pd.read_csv(caminho, parse_dates=["data_pedido"])


def validar_vendas(df: pd.DataFrame) -> bool:
    batch = gx.from_pandas(df)

    batch.expect_table_columns_to_match_ordered_list(
        ["pedido_id", "cliente_email", "uf", "valor", "status", "data_pedido"]
    )
    batch.expect_column_values_to_be_unique("pedido_id")
    batch.expect_column_values_to_not_be_null("cliente_email")
    batch.expect_column_values_to_match_regex(
        "cliente_email",
        r"^[^@\s]+@[^@\s]+\.[^@\s]+$",
    )
    batch.expect_column_values_to_be_in_set("uf", UFS_BRASIL)
    batch.expect_column_values_to_be_between("valor", min_value=0.01)
    batch.expect_column_values_to_be_in_set("status", ["pago", "enviado", "cancelado"])
    batch.expect_column_values_to_not_be_null("data_pedido")

    resultado = batch.validate()
    return bool(resultado.success)


if __name__ == "__main__":
    vendas = carregar_vendas("data/vendas.csv")
    if not validar_vendas(vendas):
        raise SystemExit("Falha na validação de qualidade de dados")
    print("Dados de vendas validados com sucesso")
```

O exemplo usa `gx.from_pandas()` para ficar didático. Em projetos maiores, você pode evoluir para Data Context, fontes de dados configuradas e checkpoints. Para começar, porém, o valor já aparece: as regras ficam explícitas e qualquer pessoa consegue entender o contrato da base.

## O que cada expectativa protege

As expectativas não devem ser escolhidas por volume, e sim por risco. No exemplo acima:

- `expect_table_columns_to_match_ordered_list` protege o contrato de entrada. Se a exportação mudar nome ou ordem de coluna, o pipeline para cedo.
- `expect_column_values_to_be_unique` evita duplicar pedidos em métricas de receita.
- `expect_column_values_to_not_be_null` impede registros sem e-mail quando a comunicação com cliente depende desse campo.
- `expect_column_values_to_match_regex` captura e-mails claramente inválidos.
- `expect_column_values_to_be_in_set` em `uf` impede valores como `Sao Paulo`, `SP ` ou `XX`.
- `expect_column_values_to_be_between` em `valor` evita vendas negativas ou zeradas entrando em indicadores.
- `expect_column_values_to_be_in_set` em `status` impede que um status novo passe sem decisão explícita.

Essa leitura é importante para carreira. Em entrevistas, não basta dizer "usei Great Expectations". Melhor é explicar qual risco cada validação reduz e qual decisão o time deve tomar quando ela falha.

## Integrando com pytest

Para um projeto de portfólio, uma boa prática é colocar a validação no mesmo fluxo de testes. Crie `tests/test_qualidade_vendas.py`:

```python
import pandas as pd

from src.validar_vendas import validar_vendas


def test_vendas_validas_passam_na_validacao():
    df = pd.DataFrame(
        {
            "pedido_id": [1001, 1002],
            "cliente_email": ["ana@example.com", "bruno@example.com"],
            "uf": ["SP", "MG"],
            "valor": [129.90, 89.50],
            "status": ["pago", "enviado"],
            "data_pedido": pd.to_datetime(["2026-05-20", "2026-05-21"]),
        }
    )

    assert validar_vendas(df) is True


def test_venda_com_valor_negativo_falha():
    df = pd.DataFrame(
        {
            "pedido_id": [1001],
            "cliente_email": ["ana@example.com"],
            "uf": ["SP"],
            "valor": [-10.0],
            "status": ["pago"],
            "data_pedido": pd.to_datetime(["2026-05-20"]),
        }
    )

    assert validar_vendas(df) is False
```

Agora rode:

```bash
pytest
```

Esse teste não substitui testes unitários de transformação, mas adiciona um nível de contrato para dados. Em um pipeline real, você pode rodar testes de função, validações de schema e validações de amostra antes de publicar a tabela final.

## Usando em um pipeline simples

Um fluxo mínimo de ETL pode ficar assim:

```python
from pathlib import Path

from src.validar_vendas import carregar_vendas, validar_vendas


def transformar(caminho_entrada: Path, caminho_saida: Path) -> None:
    vendas = carregar_vendas(caminho_entrada)

    if not validar_vendas(vendas):
        raise ValueError("Base de vendas rejeitada pela validação")

    resumo = (
        vendas.groupby("uf", as_index=False)
        .agg(receita=("valor", "sum"), pedidos=("pedido_id", "count"))
        .sort_values("receita", ascending=False)
    )

    resumo.to_csv(caminho_saida, index=False)
```

O detalhe é a ordem: valide antes de transformar. Se dados ruins entram no agrupamento, você pode gerar relatório bonito com número errado. Em dados, erro silencioso é pior do que falha explícita.

Também vale registrar falhas em um arquivo separado. Por exemplo, se o pipeline recebe CSV de fornecedores, você pode salvar o lote rejeitado em `data/rejeitados/` com timestamp e motivo. Isso ajuda a conversar com a área de negócio sem perder evidência.

## Great Expectations ou Pandera?

A comparação mais útil é por contexto:

| Ferramenta | Melhor uso |
|---|---|
| Pandera | validação tipada dentro do código Python, especialmente com Pandas |
| Great Expectations | suítes de expectativas, documentação de qualidade e validações compartilháveis |
| pytest puro | regras muito específicas de função ou transformação |
| Pydantic | validação de objetos, configurações, APIs e dados de entrada estruturados |

Em um projeto real, elas podem conviver. Você pode usar [Pydantic](/glossario/pydantic/) para validar payloads de API, Pandera para validar `DataFrame` intermediário, Great Expectations para validar contrato de tabela e [pytest](/blog/testes-unitarios-python/) para garantir comportamento de funções.

Evite a armadilha de instalar ferramenta demais sem clareza. Para um primeiro projeto, escolha uma base de dados, escreva 8 a 12 expectativas relevantes, rode no CI e documente o que acontece quando a validação falha.

## Exemplo com dados públicos brasileiros

Um bom projeto para currículo é validar um fluxo com dados públicos brasileiros. Você pode usar uma base de municípios, indicadores educacionais, dados de saúde, despesas públicas ou séries temporais. O README poderia explicar:

- fonte dos dados;
- frequência esperada de atualização;
- colunas obrigatórias;
- expectativas de qualidade;
- exemplos de falha;
- comando para rodar validação;
- saída gerada após aprovação.

Imagine uma base mensal de municípios:

```csv
codigo_ibge,municipio,uf,ano,mes,valor
3550308,São Paulo,SP,2026,5,1024.50
2611606,Recife,PE,2026,5,310.40
```

As expectativas poderiam validar que `codigo_ibge` tem sete dígitos, `uf` está no conjunto de estados, `ano` está entre 2000 e 2030, `mes` fica entre 1 e 12 e `valor` não é negativo. Esse tipo de projeto conversa bem com [APIs públicas brasileiras com Python](/blog/apis-publicas-brasileiras-python/), [ETL com Python](/blog/etl-python-2026/), [DuckDB](/blog/python-e-duckdb-analytics/) e [GeoPandas](/blog/geopandas-dados-geoespaciais-python/).

Para deixar o portfólio mais forte, inclua um caso de falha proposital. Um diretório `examples/dados_invalidos.csv` mostra que você pensou no caminho ruim, não apenas no demo perfeito.

## Rodando no CI

Em GitHub Actions ou Gitea Actions, a validação pode rodar junto com os testes:

```yaml
name: quality

on:
  push:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v5
      - run: uv sync
      - run: uv run pytest
      - run: uv run python src/validar_vendas.py
```

Esse fluxo evita que uma mudança de dados ou código seja aceita sem passar pelo contrato mínimo. Em times maiores, você pode separar validações rápidas de PR e validações completas de agenda diária, porque bases grandes podem demorar.

## Cuidados em produção

Algumas práticas evitam frustração:

- Comece por expectativas críticas. Não tente modelar 100% da realidade no primeiro dia.
- Defina severidade. Uma coluna opcional ausente pode gerar alerta; CPF inválido em base de cobrança talvez bloqueie carga.
- Versione expectativas junto com o código. Mudança de regra deve passar por revisão.
- Não valide apenas schema. Distribuição, faixas e cardinalidade também importam.
- Separe dado rejeitado de dado aprovado. Isso facilita auditoria.
- Monitore tendência de falhas. Se a mesma expectativa quebra toda semana, o contrato ou a fonte precisam ser revistos.

Também cuidado com dados pessoais. Se o projeto usa e-mail, CPF, telefone ou endereço, trabalhe com dados fictícios ou anonimizados. Para portfólio público, mostre estrutura e validação sem expor informação real.

## Como apresentar no portfólio

Um bom README para esse tipo de projeto pode ter esta estrutura:

```text
# Validação de qualidade de dados com Python

## Problema
Base de vendas mensal pode chegar com duplicidade, valores inválidos e estados incorretos.

## Solução
Pipeline em Python com Pandas, Great Expectations e pytest.

## Regras de qualidade
- pedido_id único
- valor maior que zero
- UF dentro da lista oficial
- status dentro do contrato
- e-mail com formato válido

## Como rodar
uv run pytest
uv run python src/validar_vendas.py

## Evidência
Inclui exemplo de base válida, base inválida e saída de validação.
```

Esse formato é mais convincente do que apenas listar tecnologias. Ele mostra problema, decisão, validação e evidência. Para vagas de dados, QA, engenharia analítica e automação, essa clareza pesa bastante.

## Próximos passos

Depois do primeiro fluxo funcionando, evolua por etapas:

1. Adicione validações para dados públicos brasileiros.
2. Gere documentação HTML das expectativas.
3. Rode validação em CI.
4. Salve lotes rejeitados com timestamp.
5. Publique um dashboard simples com apenas dados aprovados.
6. Compare Great Expectations com Pandera no README.

Se o volume crescer, combine a validação com Parquet, DuckDB, Polars ou banco analítico. Quando a rotina precisar de agendamento, retries e histórico por etapa, conecte a validação a um orquestrador como [Airflow com Python](/blog/airflow-python-orquestracao-pipelines/). Se o gargalo for ingestão em alta concorrência, uma arquitetura mista pode usar <a href="https://golang.com.br/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go</a> para coletar eventos e Python para validação, análise e ciência de dados.

Great Expectations não resolve qualidade de dados sozinho. A ferramenta só funciona quando você transforma conhecimento de negócio em contrato verificável. Esse é o ganho real: menos confiança cega em planilha, mais evidência automatizada antes de tomar decisão.
