itertools.pairwise: analise pares consecutivos

Publicado em: 29/08/2026
Tempo de leitura: 5 minutos
Close-up of a python snake curled up, showcasing its detailed scales.

Muitos algoritmos precisam comparar cada elemento com o próximo: calcular diferenças, detectar mudanças, validar ordem, medir distâncias, encontrar intervalos e analisar séries temporais. itertools.pairwise() transforma um iterável em pares sobrepostos, produzindo (a, b), depois (b, c), depois (c, d), sem carregar toda a fonte na memória.

Neste guia, você aprenderá a usar pairwise com listas, geradores e arquivos, calcular deltas, detectar transições, validar sequências, trabalhar com timestamps, lidar com iteráveis curtos e distinguir pares sobrepostos de lotes não sobrepostos.

Primeiro exemplo

from itertools import pairwise

valores = [10, 15, 12, 20]
for anterior, atual in pairwise(valores):
    print(anterior, atual)

O resultado é (10, 15), (15, 12) e (12, 20). Cada elemento intermediário participa de dois pares: como segundo valor de um par e primeiro do próximo.

Iteráveis curtos

Uma fonte vazia ou com apenas um elemento produz zero pares. Não ocorre erro.

list(pairwise([]))       # []
list(pairwise([42]))     # []
list(pairwise([1, 2]))   # [(1, 2)]

Se o algoritmo exige pelo menos dois elementos, valide antes ou detecte a ausência do primeiro par.

Consumo preguiçoso

def contador():
    numero = 0
    while True:
        yield numero
        numero += 1

pares = pairwise(contador())
print(next(pares))
print(next(pares))

A função mantém apenas o valor anterior necessário para formar o próximo par. Isso permite trabalhar com streams grandes ou infinitos.

Calculando diferenças

temperaturas = [20.0, 21.5, 19.0, 23.0]
deltas = [atual - anterior for anterior, atual in pairwise(temperaturas)]

O resultado possui um elemento a menos que a entrada. Essa diferença de comprimento é natural, pois não existe valor anterior para o primeiro item.

Taxas de variação

amostras = [(0.0, 10.0), (2.0, 14.0), (5.0, 20.0)]

taxas = []
for (t1, v1), (t2, v2) in pairwise(amostras):
    taxas.append((v2 - v1) / (t2 - t1))

Valide timestamps repetidos para evitar divisão por zero. Também confirme que as amostras estão ordenadas pelo tempo.

Detectando mudanças de estado

estados = ["novo", "novo", "pago", "enviado", "enviado"]
transicoes = [
    (antes, depois)
    for antes, depois in pairwise(estados)
    if antes != depois
]

Esse padrão é útil em auditorias, máquinas de estado, logs e jornadas de usuário. Para preservar o índice da mudança, combine com enumerate().

Validando ordem crescente

def esta_ordenado(valores):
    return all(a <= b for a, b in pairwise(valores))

Para listas vazias ou unitárias, all() devolve True. Isso segue a lógica de que não existe par fora de ordem. Caso o domínio exija pelo menos dois valores, acrescente uma regra separada.

Detectando duplicatas consecutivas

duplicadas = [a for a, b in pairwise(valores) if a == b]

Isso encontra apenas repetições adjacentes. Para duplicatas em qualquer posição, use um conjunto, Counter ou ordenação adequada.

Intervalos e lacunas

datas = [1, 2, 5, 6, 10]
lacunas = [(a, b) for a, b in pairwise(datas) if b - a > 1]

Em calendários, IDs sequenciais ou amostras, o padrão identifica buracos. Defina se valores repetidos e regressões também devem ser sinalizados.

Distâncias em trajetórias

from math import hypot

pontos = [(0, 0), (3, 4), (6, 4)]
total = sum(
    hypot(x2 - x1, y2 - y1)
    for (x1, y1), (x2, y2) in pairwise(pontos)
)

A soma mede o comprimento da trajetória, não a distância direta entre início e fim.

Lendo linhas consecutivas

with open("eventos.log", encoding="utf-8") as arquivo:
    for linha_anterior, linha_atual in pairwise(arquivo):
        comparar(linha_anterior, linha_atual)

O arquivo é consumido de forma incremental. Não guarde referências aos pares se o objetivo for manter memória constante.

Pairwise e enumerate

for indice, (anterior, atual) in enumerate(pairwise(valores), start=1):
    print(f"transição {indice - 1}->{indice}: {anterior} para {atual}")

O índice representa a posição do segundo elemento. Ajuste a mensagem conforme a convenção do domínio.

Pairwise e zip

Antes da função oficial, era comum escrever:

pares = zip(valores, valores[1:])

Essa forma funciona com sequências e cria um slice. Para iteradores genéricos, usava-se tee(). pairwise é mais direto e evita boilerplate.

Implementação conceitual

def pairwise_manual(iterable):
    iterator = iter(iterable)
    try:
        anterior = next(iterator)
    except StopIteration:
        return
    for atual in iterator:
        yield anterior, atual
        anterior = atual

A função oficial segue essa lógica e mantém apenas uma referência anterior.

Diferença para batched

from itertools import batched, pairwise

list(pairwise([1, 2, 3, 4]))
# [(1, 2), (2, 3), (3, 4)]

list(batched([1, 2, 3, 4], 2))
# [(1, 2), (3, 4)]

pairwise cria uma janela de tamanho dois com passo um. batched cria blocos não sobrepostos.

Janelas maiores

pairwise é específico para dois elementos. Para janelas de tamanho três ou mais, use um recipe com deque ou uma biblioteca apropriada.

from collections import deque

def sliding_window(iterable, n):
    iterator = iter(iterable)
    janela = deque([], maxlen=n)
    for item in iterator:
        janela.append(item)
        if len(janela) == n:
            yield tuple(janela)

Séries temporais irregulares

Ao comparar amostras temporais, não suponha intervalos constantes. Calcule o delta de tempo para cada par. Também trate fuso horário, dados fora de ordem e registros duplicados.

Valores ausentes

Se a série contém None, decida se o par deve ser ignorado, imputado ou marcado como inválido.

for a, b in pairwise(valores):
    if a is None or b is None:
        continue
    processar(b - a)

NaN

Em dados numéricos, NaN se propaga e não é igual a si mesmo. Use funções específicas para detectá-lo antes de comparar mudanças ou ordem.

Objetos mutáveis

pairwise guarda uma referência ao elemento anterior. Se a fonte reutiliza e modifica o mesmo objeto a cada yield, ambos os componentes podem apontar para o mesmo objeto mutado. Geradores devem produzir snapshots independentes quando isso importar.

Exceções na fonte

Se o iterável lança uma exceção, pairwise a propaga. O último valor anterior permanece apenas dentro do iterador e será liberado quando ele for descartado.

Async iterators

pairwise aceita iteráveis síncronos. Para fontes assíncronas, implemente um helper com async for:

async def async_pairwise(source):
    iterator = aiter(source)
    try:
        anterior = await anext(iterator)
    except StopAsyncIteration:
        return
    async for atual in iterator:
        yield anterior, atual
        anterior = atual

Encontrando máximas mudanças

maior = max(
    ((abs(b - a), a, b) for a, b in pairwise(valores)),
    default=None,
)

Use default porque uma entrada com menos de dois elementos não produz candidatos.

Segmentando por mudanças

pairwise ajuda a localizar fronteiras, mas para produzir grupos completos por chave, itertools.groupby() costuma ser mais simples. Use pairwise quando a transição em si é a informação principal.

Erros comuns

  • Esperar pares não sobrepostos: use batched para isso.
  • Esquecer que o resultado é menor: existem n-1 pares.
  • Consumir o iterador duas vezes: a fonte pode ser de passagem única.
  • Ignorar timestamps repetidos: taxas podem dividir por zero.
  • Confundir duplicata adjacente com global: pairwise vê apenas vizinhos.
  • Usar com objetos reutilizados: referências mutáveis podem surpreender.

Exemplo completo: auditoria de eventos

from itertools import pairwise

ORDEM = {
    "criado": 0,
    "pago": 1,
    "separado": 2,
    "enviado": 3,
    "entregue": 4,
}

def auditar(eventos):
    problemas = []
    for anterior, atual in pairwise(eventos):
        if atual.timestamp < anterior.timestamp:
            problemas.append("timestamp regressivo")
        if ORDEM[atual.estado] < ORDEM[anterior.estado]:
            problemas.append(
                f"transição inválida: {anterior.estado} -> {atual.estado}"
            )
    return problemas

O exemplo valida cronologia e progressão de estado em uma única passagem e memória constante.

Conclusão

itertools.pairwise() expressa de forma clara algoritmos baseados em vizinhos consecutivos. Ele é preguiçoso, funciona com qualquer iterável síncrono e elimina slices ou combinações manuais com tee.

A documentação oficial de itertools.pairwise define a função. Use-a para deltas, transições, lacunas e validações, lembrando que pares se sobrepõem e que entradas curtas produzem nenhum resultado.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    itertools.batched: processe iteráveis em lotes

    Aprenda itertools.batched no Python para processar iteráveis em lotes, controlar memória, usar strict e criar pipelines resilientes.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    cmp_to_key: adapte comparadores antigos ao sorted

    Aprenda cmp_to_key no Python para adaptar comparadores antigos, ordenar com locale, preservar estabilidade e evitar regras inconsistentes.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    total_ordering: gere comparações consistentes

    Aprenda total_ordering no Python para gerar comparações consistentes, usar NotImplemented, integrar dataclasses e testar ordens.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect.signature: leia parâmetros de funções

    Aprenda inspect.signature no Python para ler parâmetros, vincular argumentos, preservar decorators e gerar interfaces dinâmicas.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    get_origin e get_args: inspecione tipos genéricos

    Aprenda get_origin e get_args no Python para inspecionar genéricos, uniões, Annotated, Literal e aliases com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    LiteralString no Python: strings confiáveis

    Aprenda LiteralString no Python para restringir SQL, templates e comandos a strings confiáveis e reduzir injeções com análise estática.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026