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

    Close-up of a detailed South America map showcasing geography and cartography.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ChainMap en Python: mapas por capas

    Aprende ChainMap en Python para combinar configuración y scopes por capas, controlar precedencia, escrituras y snapshots seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    itertools.batched: procesa iterables por lotes

    Aprende itertools.batched en Python para procesar iterables por lotes, controlar memoria, usar strict y crear 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 Avanzado
    Foto de perfil de Leandro Hirt da Academify

    cmp_to_key: adapta comparadores antiguos a sorted

    Aprende cmp_to_key en Python para adaptar comparadores antiguos, ordenar con locale, conservar estabilidad y evitar relaciones incoherentes.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    total_ordering: genera comparaciones consistentes

    Aprende total_ordering en Python para generar comparaciones coherentes, devolver NotImplemented, integrar dataclasses y probar órdenes.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    inspect.signature: inspecciona parámetros de funciones

    Aprende inspect.signature en Python para leer parámetros, vincular argumentos, conservar decorators y generar interfaces dinámicas.

    Ler mais

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

    get_origin y get_args: inspecciona tipos genéricos

    Aprende get_origin y get_args en Python para inspeccionar genéricos, uniones, Annotated, Literal, alias y metadatos de runtime.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026