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

    Código Python que representa functools.Placeholder y argumentos posicionales
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    functools.Placeholder: argumentos flexibles en partial

    Aprende functools.Placeholder en Python 3.14 para crear llamadas parciales flexibles con argumentos posicionales abiertos.

    Ler mais

    Tempo de leitura: 6 minutos
    17/10/2026
    Código en una pantalla que representa t-strings en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    T-strings en Python 3.14: templates estructurados

    Aprende t-strings en Python 3.14, explora Template e Interpolation y crea renderizadores estructurados, extensibles y seguros.

    Ler mais

    Tempo de leitura: 6 minutos
    16/10/2026
    CPU y módulos de memoria que representan shared_memory track en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    shared_memory track: controla el resource tracker

    Aprende shared_memory track en Python para controlar el resource tracker, la propiedad y la limpieza entre procesos.

    Ler mais

    Tempo de leitura: 5 minutos
    16/10/2026
    Unidad de almacenamiento de datos que representa compression.zstd en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: streaming y compresión segura

    Aprende compression.zstd en Python para comprimir archivos y datos con velocidad, streaming, diccionarios y límites seguros.

    Ler mais

    Tempo de leitura: 7 minutos
    15/10/2026
    Código en pantalla para diagnosticar procesos con sys.remote_exec en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sys.remote_exec: diagnostica procesos Python

    Aprende sys.remote_exec en Python para diagnosticar procesos activos con scripts controlados, permisos seguros y buenas prácticas.

    Ler mais

    Tempo de leitura: 7 minutos
    15/10/2026
    Código Python y configuración de variables de entorno con os.reload_environ
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    os.reload_environ: threads y variables de entorno

    Aprende os.reload_environ en Python para actualizar variables de entorno externas con validación, compatibilidad y precauciones de concurrencia.

    Ler mais

    Tempo de leitura: 5 minutos
    14/10/2026