itertools.pairwise: analiza pares consecutivos

Publicado el: 29/08/2026
Tempo de leitura: 4 minutos
A developer typing code on a laptop with a Python book beside in an office.

Muchos algoritmos necesitan comparar cada valor con el siguiente: calcular diferencias, detectar cambios, validar orden, medir distancias, localizar intervalos y analizar series temporales. itertools.pairwise() transforma un iterable en pares solapados: (a, b), después (b, c) y después (c, d), sin cargar toda la entrada en memoria.

Esta guía explica pairwise con listas, generadores y archivos, cálculos de deltas, transiciones, validación de orden, timestamps, iterables cortos, objetos mutables, alternativas asíncronas y la diferencia entre pares solapados y lotes no solapados.

Primer ejemplo

from itertools import pairwise

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

El resultado es (10, 15), (15, 12) y (12, 20). Cada valor intermedio participa dos veces: como segundo componente de un par y primero del siguiente.

Entradas cortas

Un iterable vacío o con un solo elemento no produce pares ni genera error.

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

Si el dominio exige al menos dos observaciones, valida ese contrato por separado.

Procesamiento perezoso

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

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

El iterador conserva únicamente el valor anterior necesario. Puede procesar streams grandes o infinitos con memoria auxiliar constante.

Calcular diferencias

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

El resultado tiene un elemento menos porque la primera observación no posee precedente.

Tasas de cambio

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

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

Valida timestamps duplicados para evitar división por cero y confirma que las muestras están ordenadas cronológicamente.

Detectar transiciones de estado

estados = ["nuevo", "nuevo", "pagado", "enviado", "enviado"]
transiciones = [
    (antes, despues)
    for antes, despues in pairwise(estados)
    if antes != despues
]

Este patrón es útil en auditorías, máquinas de estado, workflows y recorridos de usuario. Combínalo con enumerate() cuando importe la posición.

Validar orden

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

all() devuelve verdadero para entradas vacías o unitarias porque ningún par viola la regla. Añade una validación de longitud si el dominio requiere varios valores.

Duplicados consecutivos

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

Esto encuentra solo repeticiones adyacentes. Para duplicados en cualquier posición, usa set, Counter, una restricción de base de datos o una estrategia de agrupación.

Huecos en secuencias

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

La misma técnica sirve con fechas, números de secuencia y sensores. Define cómo clasificar valores repetidos y regresiones.

Distancias en una trayectoria

from math import hypot

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

La suma mide la longitud recorrida, no la distancia directa entre el inicio y el final.

Comparar líneas consecutivas

with open("eventos.log", encoding="utf-8") as archivo:
    for linea_anterior, linea_actual in pairwise(archivo):
        comparar(linea_anterior, linea_actual)

El archivo se consume incrementalmente. Evita guardar todos los pares si el objetivo es memoria constante.

Añadir posiciones

for indice, (anterior, actual) in enumerate(pairwise(valores), start=1):
    print(indice - 1, indice, anterior, actual)

El índice representa naturalmente la posición del segundo elemento.

Pairwise frente a zip y slicing

En secuencias, código antiguo usaba:

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

Esta forma crea un slice y no se generaliza directamente a iteradores. Antes de pairwise, las implementaciones genéricas usaban tee(). La función estándar es más clara.

Implementación conceptual

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

La función oficial sigue esta idea de una sola pasada y conserva una referencia anterior.

Pairwise frente a 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 es una ventana de tamaño dos con paso uno. batched crea bloques no solapados.

Ventanas mayores

pairwise está especializado en dos valores. Para ventanas de tres o más, usa un recipe con deque:

from collections import deque

def sliding_window(iterable, n):
    ventana = deque(maxlen=n)
    for item in iterable:
        ventana.append(item)
        if len(ventana) == n:
            yield tuple(ventana)

Series temporales irregulares

No supongas un intervalo fijo. Calcula la diferencia temporal para cada par. Maneja zonas horarias, registros fuera de orden, timestamps duplicados, ajustes de reloj y observaciones ausentes.

Valores ausentes

Cuando existe None, decide si el par se omite, se imputa o se marca como inválido.

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

Valores NaN

NaN se propaga en cálculos y no es igual a sí mismo. Detecta ese valor antes de validar monotonía o calcular cambios significativos.

Objetos mutables reutilizados

pairwise guarda una referencia al objeto anterior. Si un generador modifica y vuelve a producir la misma instancia, ambos componentes pueden mostrar el estado más reciente. Genera snapshots independientes cuando necesites historial.

Excepciones de la fuente

Si el iterador fuente genera una excepción, pairwise la propaga. La referencia previa se libera al descartar el iterador.

Pairwise asíncrono

La función estándar acepta iterables síncronos. Una fuente async necesita un helper:

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

Encontrar el mayor cambio

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

Usa default porque una entrada con menos de dos valores no produce candidatos.

Transiciones frente a grupos

pairwise ayuda a localizar fronteras. Si necesitas producir grupos completos de claves iguales, itertools.groupby() suele ser más directo. Usa pairwise cuando la transición sea el evento principal.

Errores comunes

  • Esperar pares no solapados: usa batched.
  • Olvidar que salen n-1 pares: el primer valor no tiene anterior.
  • Reutilizar un iterador consumido: muchas fuentes son de una sola pasada.
  • Ignorar timestamps duplicados: las tasas pueden dividir por cero.
  • Confundir duplicados adyacentes con globales: pairwise solo ve vecinos.
  • Reutilizar un objeto mutable: las referencias históricas pueden engañar.

Ejemplo completo: auditoría de eventos

from itertools import pairwise

ORDEN = {
    "creado": 0,
    "pagado": 1,
    "preparado": 2,
    "enviado": 3,
    "entregado": 4,
}

def auditar(eventos):
    problemas = []
    for anterior, actual in pairwise(eventos):
        if actual.timestamp < anterior.timestamp:
            problemas.append("timestamp regresivo")
        if ORDEN[actual.estado] < ORDEN[anterior.estado]:
            problemas.append(
                f"transición inválida: {anterior.estado} -> {actual.estado}"
            )
    return problemas

La función valida cronología y progresión de estado en una sola pasada y memoria constante.

Conclusión

itertools.pairwise() expresa claramente algoritmos basados en vecinos consecutivos. Es perezoso, funciona con cualquier iterable síncrono y elimina slices o recetas manuales con tee.

La documentación oficial de itertools.pairwise define la función. Úsala para deltas, transiciones, huecos y validaciones, recordando que los pares se solapan y las entradas cortas no producen resultados.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Estructura de código y XML para canonicalización con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ElementTree canonicalize: normaliza XML en Python

    Aprende ElementTree canonicalize en Python para comparar XML, generar hashes estables y controlar namespaces, espacios y comentarios.

    Ler mais

    Tempo de leitura: 5 minutos
    22/10/2026
    Código Python en una pantalla
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    T-strings y templatelib: interpolación estructurada

    Aprende t-strings y string.templatelib en Python para interpolación estructurada, renderizado seguro, logs y procesamiento personalizado.

    Ler mais

    Tempo de leitura: 6 minutos
    22/10/2026
    Base SQLite y código Python para exportación selectiva con iterdump
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    SQLite iterdump: crea dumps selectivos en Python

    Aprende sqlite3.Connection.iterdump con filtro para crear dumps SQLite selectivos, validar dependencias y restaurar con seguridad.

    Ler mais

    Tempo de leitura: 4 minutos
    21/10/2026
    Código y archivos que representan compresión de datos con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: Zstandard rápido en Python

    Aprende compression.zstd en Python para comprimir archivos y datos con velocidad, streaming, integridad y seguridad.

    Ler mais

    Tempo de leitura: 7 minutos
    21/10/2026
    Archivo de configuración y estructura de código para ConfigParser en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ConfigParser: lee INI sin sección en Python

    Aprende ConfigParser sin sección en Python con validación, tipos, compatibilidad, capas de configuración y escritura segura.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026
    Datos binarios y memoria de ordenador representando memoryview en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview count e index: busca sin copiar

    Aprende memoryview count e index en Python para buscar valores en buffers sin copias y validar datos binarios con eficiencia.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026