Pandas & NumPy Academy · Aula

Estruturando etapas de transformação como funções

Divida seu notebook em funções de extração, transformação e carregamento, cada uma recebendo e retornando um DataFrame para facilitar os testes.

Aula 1 de 413 etapas

Estruturando etapas de transformação como funções é uma aula grátis de Pandas & NumPy Academy no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Pandas & NumPy Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Pandas & NumPy Academy inclui 4 aulas no total.

Indo além dos notebooks

Os notebooks do Jupyter são excelentes para exploração, mas inadequados para fluxos de dados de produção. Código espalhado por 50 células, com estado global e sem testes, é frágil: alterar uma célula pode quebrar outra silenciosamente. A alternativa profissional é extrair cada transformação para uma função nomeada que aceita um DataFrame e retorna um DataFrame. Essa separação de responsabilidades é a base de um código de engenharia de dados fácil de manter.

# Notebook-style (fragile)
df = pd.read_csv('orders.csv')
df = df.dropna(subset=['revenue'])
df = df[df['quantity'] > 0]
df['revenue_per_unit'] = df['revenue'] / df['quantity']

# Function-style (robust)
def extract(path):
    return pd.read_csv(path)

def transform(df):
    return (
        df.dropna(subset=['revenue'])
        .query('quantity > 0')
        .assign(revenue_per_unit=lambda d: d['revenue'] / d['quantity'])
    )

df = transform(extract('orders.csv'))

O padrão ETL: extrair, transformar, carregar

O padrão ETL divide um fluxo de dados em três fases: Extrair (ler da origem), Transformar (limpar e enriquecer) e Carregar (gravar no destino). Cada fase é uma função separada. Essa separação facilita a troca das fontes de dados (CSV ou banco de dados), a alteração da lógica de limpeza ou a mudança do formato de saída sem modificar as outras duas fases. Todo fluxo de produção deve seguir essa estrutura.

def extract(config):
    return pd.read_csv(config['input_path'], parse_dates=['order_date'])

def transform(df, config):
    return (
        df
        .dropna(subset=config['required_cols'])
        .query('quantity > 0')
        .assign(revenue=lambda d: d['quantity'] * d['unit_price'])
    )

def load(df, config):
    df.to_parquet(config['output_path'], index=False)
    print(f'Saved {len(df)} rows.')

Princípio da responsabilidade única

Cada função de transformação deve fazer exatamente uma coisa. Uma função chamada clean_data() que remove valores nulos, limita valores atípicos, interpreta datas e codifica categorias é difícil de testar e depurar. Em vez disso, escreva drop_nulls(), cap_outliers(), parse_dates() e encode_categories() como funções separadas. Essa granularidade facilita ignorar, substituir ou reordenar qualquer etapa individual.

def drop_null_rows(df, required_cols):
    return df.dropna(subset=required_cols)

def remove_returns(df):
    return df[df['quantity'] > 0]

def compute_revenue(df):
    return df.assign(revenue=lambda d: d['quantity'] * d['unit_price'])

def add_date_features(df):
    return df.assign(
        year=lambda d: d['order_date'].dt.year,
        month=lambda d: d['order_date'].dt.month
    )

Encadeando etapas com pipe()

Conecte funções com responsabilidade única usando pipe() para criar toda a fase de transformação como uma cadeia legível. A cadeia se lê como uma receita: cada linha é uma etapa, e o fluxo dos dados segue de cima para baixo. Qualquer etapa pode ser comentada ou reordenada sem renomear variáveis. O resultado final é o DataFrame limpo e enriquecido, pronto para a fase de carregamento.

import pandas as pd

REQUIRED = ['order_id', 'revenue', 'order_date']

def transform(raw_df):
    return (
        raw_df
        .pipe(drop_null_rows, required_cols=REQUIRED)
        .pipe(remove_returns)
        .pipe(compute_revenue)
        .pipe(add_date_features)
    )

df_clean = transform(pd.read_csv('orders.csv', parse_dates=['order_date']))
print(df_clean.shape)

Retornando contagens de linhas para auditoria

Cada função de transformação deve registrar opcionalmente quantas linhas recebeu e quantas retornou. Envolver o corpo da função com um registro da contagem de linhas antes e depois cria uma trilha de auditoria simples, que permite identificar rapidamente onde as linhas estão sendo removidas durante uma execução do fluxo. Armazene essas contagens em uma lista e imprima-as como um relatório ao final de cada execução.

audit_log = []

def audited(func):
    def wrapper(df, *args, **kwargs):
        before = len(df)
        result = func(df, *args, **kwargs)
        after = len(result)
        audit_log.append({'step': func.__name__, 'in': before, 'out': after, 'dropped': before - after})
        return result
    return wrapper

@audited
def drop_null_rows(df, required_cols):
    return df.dropna(subset=required_cols)

Funções testáveis com DataFrames pequenos

A maior vantagem das funções nomeadas é a testabilidade. Escreva um pequeno DataFrame de teste que represente um caso-limite realista e verifique a saída da função. Teste a função drop_null_rows com uma linha que contenha um valor nulo e confirme que ela foi removida; depois, use outra linha sem valor nulo e confirme que ela foi mantida. Testes unitários em funções de transformação detectam regressões quando o código do fluxo é alterado.

import pandas as pd

def test_drop_null_rows():
    test_df = pd.DataFrame({
        'order_id': [1, 2, 3],
        'revenue': [100.0, None, 200.0]
    })
    result = drop_null_rows(test_df, required_cols=['revenue'])
    assert len(result) == 2, 'Should have 2 non-null rows'
    assert result['revenue'].isna().sum() == 0, 'No nulls in revenue'
    print('test_drop_null_rows PASSED')

test_drop_null_rows()

Organizando funções em módulos

À medida que o fluxo cresce, divida as funções em arquivos de módulos Python: extract.py, transform.py, load.py e validate.py. O script principal pipeline.py faz a importação e a orquestração dessas funções. Essa estrutura de arquivos é legível, testável com pytest e pode ser implantada como um pacote Python. Ela reproduz a organização padrão usada por equipes de engenharia de dados com ferramentas como dbt ou Airflow.

# pipeline.py
# from extract import extract_orders
# from transform import transform
# from load import load_to_parquet
# from validate import validate_sales_df

# def run_pipeline(config):
#     raw = extract_orders(config)
#     clean = transform(raw, config)
#     validate_sales_df(clean)
#     load_to_parquet(clean, config)

print('Module-based pipeline structure shown above (imports commented for demo)')

Idempotência: executando várias vezes com segurança

Uma função de fluxo bem projetada é idempotente: executá-la duas vezes com a mesma entrada produz a mesma saída e não causa efeitos colaterais. Evite mutações no próprio objeto (df.drop(..., inplace=True)) e sempre use df.copy() no início das funções que modificam colunas. Funções idempotentes podem ser executadas novamente após falhas sem corromper os dados de saída.

def add_revenue_flag(df, threshold=500):
    # Use copy to avoid mutating the input
    df = df.copy()
    df['is_large_order'] = df['revenue'] >= threshold
    return df

# Running twice gives the same result
df1 = add_revenue_flag(df_clean)
df2 = add_revenue_flag(df_clean)
assert df1.equals(df2), 'Function is not idempotent!'
print('Idempotency check passed.')

Anotações de tipo para autodocumentação

Adicione anotações de tipo do Python às assinaturas das funções de transformação para tornar o contrato explícito: def transform(df: pd.DataFrame) -> pd.DataFrame. As anotações de tipo são autodocumentadas — qualquer pessoa desenvolvedora que leia a função saberá exatamente o que ela espera e retorna sem precisar ler o corpo. Elas também permitem que ferramentas de análise estática, como mypy, e o preenchimento automático da IDE detectem erros de tipo antes da execução.

from typing import List

def drop_null_rows(df: pd.DataFrame, required_cols: List[str]) -> pd.DataFrame:
    return df.dropna(subset=required_cols)

def compute_revenue(df: pd.DataFrame) -> pd.DataFrame:
    return df.assign(revenue=lambda d: d['quantity'] * d['unit_price'])

print('Type-annotated functions ready for production use.')

Documentando cada etapa com docstrings

Cada função de transformação deve ter uma docstring de uma linha informando o que ela faz, quais colunas exige e quais colunas adiciona ou remove. Boas docstrings tornam a função fácil de localizar por meio de help() e nas dicas de ferramentas da IDE. Elas também servem como documentos de especificação: uma asserção que falha por contradizer a docstring é um erro; uma docstring que diverge do código é um erro de documentação.

def compute_revenue(df: pd.DataFrame) -> pd.DataFrame:
    """Add 'revenue' column as quantity * unit_price.

    Requires: 'quantity' (numeric), 'unit_price' (numeric) columns.
    Returns: df with new 'revenue' float column appended.
    """
    return df.assign(revenue=lambda d: d['quantity'] * d['unit_price'])

help(compute_revenue)

Executando o fluxo completo

Orquestre o ETL completo chamando as três fases em sequência: extração, transformação e carregamento. Adicione medição de tempo em cada fase para identificar onde o tempo é gasto. Capture as exceções de cada fase separadamente para que as mensagens de erro identifiquem qual fase falhou. Registre a hora de início e de término da execução completa e se ela foi bem-sucedida ou falhou, para fins de monitoramento.

import time

CONFIG = {
    'input_path': 'orders.csv',
    'output_path': 'orders_clean.parquet',
    'required_cols': ['order_id', 'revenue']
}

t0 = time.time()
raw = extract(CONFIG)
clean = transform(raw, CONFIG)
load(clean, CONFIG)

print(f'Pipeline completed in {time.time()-t0:.1f}s')
print(f'Audit log: {audit_log}')

Verificação rápida

Teste sua compreensão dos conceitos de análise de dados desta lição.

Resumo da lição

Nesta lição, você aprendeu: a estruturar fluxos como funções ETL com responsabilidade única, a tornar as funções testáveis, idempotentes e autodocumentadas com anotações de tipo e docstrings e a orquestrar o fluxo completo com medição de tempo e registros de auditoria. A seguir, exploraremos como parametrizar fluxos com dicionários de configuração para reutilizá-los em diferentes conjuntos de dados.

Grátis para começar

Aprenda Python com um tutor de IA — grátis

Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.

Cursos
30
Aulas
120

Perguntas Frequentes

A aula “Estruturando etapas de transformação como funções” é grátis?

Sim — o texto completo de “Estruturando etapas de transformação como funções” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Pandas & NumPy Academy, atualize para CoddyKit PRO. O curso de Pandas & NumPy Academy inclui 4 aulas no total.

O que vou aprender em “Estruturando etapas de transformação como funções”?

Divida seu notebook em funções de extração, transformação e carregamento, cada uma recebendo e retornando um DataFrame para facilitar os testes. Você pratica Pandas & NumPy Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Pandas & NumPy Academy?

Nenhuma experiência prévia é necessária. Pandas & NumPy Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “Estruturando etapas de transformação como funções”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Pandas & NumPy Academy?

Sim. Cada aula de Pandas & NumPy Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Estruturando etapas de transformação como funções
  2. Parametrizando pipelines com dicionários de configuração
  3. Testando etapas do pipeline com asserções
  4. Agendando e registrando execuções do pipeline
← Voltar para Pandas & NumPy Academy