Iterando sobre cada dos elementos en una lista


Respuestas:


231

Necesitas un pairwise()(ogrouped() ) implementación.

Para Python 2:

from itertools import izip

def pairwise(iterable):
    "s -> (s0, s1), (s2, s3), (s4, s5), ..."
    a = iter(iterable)
    return izip(a, a)

for x, y in pairwise(l):
   print "%d + %d = %d" % (x, y, x + y)

O, más generalmente:

from itertools import izip

def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return izip(*[iter(iterable)]*n)

for x, y in grouped(l, 2):
   print "%d + %d = %d" % (x, y, x + y)

En Python 3, puede reemplazar izipcon la zip()función incorporada y soltar el import.

Gracias a Martineau por su respuesta a mi pregunta , he encontrado que esto es muy eficiente, ya que solo se repite una vez sobre la lista y no crea listas innecesarias en el proceso.

NB : Esto no debe confundirse con la pairwisereceta en la propia itertoolsdocumentación de Python , que produce s -> (s0, s1), (s1, s2), (s2, s3), ..., como lo señala @lazyr en los comentarios.

Pequeña adición para aquellos que deseen hacer una verificación de tipo con mypy en Python 3:

from typing import Iterable, Tuple, TypeVar

T = TypeVar("T")

def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
    """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
    return zip(*[iter(iterable)] * n)

16
No debe confundirse con la función por pares sugerida en la sección de recetas de itertools , que produces -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow

1
Hace algo diferente. Su versión solo produce la mitad del número de pares en comparación con la itertoolsfunción de receta con el mismo nombre. Por supuesto, el tuyo es más rápido ...
Sven Marnach

¿Eh? Su función y la función a la que me referí hacen cosas diferentes, y ese fue el punto de mi comentario.
Lauritz V. Thaulow

55
¡TEN CUIDADO! El uso de estas funciones lo pone en riesgo de no iterar sobre los últimos elementos de un iterable. Ejemplo: lista (agrupada ([1,2,3], 2)) >>> [(1, 2)] .. cuando esperarías [(1,2), (3,)]
egafni

44
@ Erik49: en el caso especificado en la pregunta, no tendría sentido tener una tupla 'incompleta'. Si desea incluir una tupla incompleta, puede usar en izip_longest()lugar de izip(). Por ejemplo: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. Espero que esto ayude.
Johnsyweb

191

Bueno, necesitas una tupla de 2 elementos, así que

data = [1,2,3,4,5,6]
for i,k in zip(data[0::2], data[1::2]):
    print str(i), '+', str(k), '=', str(i+k)

Dónde:

  • data[0::2] significa crear una colección de elementos que (index % 2 == 0)
  • zip(x,y) crea una colección de tuplas a partir de colecciones x e y mismos elementos de índice.

8
Esto también se puede extender en caso de que se requieran más de dos elementos. Por ejemplofor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
balance de vida

19
¡Mucho más limpio que extraer una importación y definir una función!
kmarsh

77
@kmarsh: Pero esto solo funciona en secuencias, la función funciona en cualquier iterable; y esto usa O (N) espacio extra, la función no; Por otro lado, esto es generalmente más rápido. Hay buenas razones para elegir uno u otro; tener miedo importno es uno de ellos.
abarnert

77
>>> l = [1,2,3,4,5,6]

>>> zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

>>> zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]

>>> [a+b for a,b in zip(l,l[1:])[::2]]
[3, 7, 11]

>>> ["%d + %d = %d" % (a,b,a+b) for a,b in zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']

1
Esto no funciona en Python-3.6.0 pero aún funciona en Python-2.7.10
Hamid Rohani

66
@HamidRohani zipdevuelve un zipobjeto en Python 3, que no es subcriptable . Se necesita ser convertida a una secuencia de ( list, tuple, etc.) primero, pero "no de trabajo" es un poco de un tramo.
vaultah

58

Una solución simple

l = [1, 2, 3, 4, 5, 6]

para i en rango (0, len (l), 2):
    print str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])

1
¿Qué pasa si su lista no es par y desea mostrar el último número tal como está?
Hans de Jong

@HansdeJong no te entendió. Por favor explique un poco más.
taskinoor

2
Gracias. Ya descubrí cómo hacerlo. El problema era que si tuviera una lista que ni siquiera tuviera una cantidad de números, obtendría un error de índice. Lo resolvió con una prueba: excepto:
Hans de Jong

O ((l[i], l[i+1])for i in range(0, len(l), 2))para un generador, puede modificarse fácilmente para tuplas más largas.
Basel Shishani

44

Si bien todas las respuestas que usan zipson correctas, encuentro que implementar la funcionalidad usted mismo conduce a un código más legible:

def pairwise(it):
    it = iter(it)
    while True:
        try:
            yield next(it), next(it)
        except StopIteration:
            # no more elements in the iterator
            return

La it = iter(it)parte asegura que en itrealidad es un iterador, no solo un iterable. Si itya es un iterador, esta línea es no operativa.

Uso:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)

2
Esta solución permite la generalización al tamaño de tuplas> 2
guilloptero

1
Esta solución también funciona si ites solo un iterador y no un iterable. Las otras soluciones parecen depender de la posibilidad de crear dos iteradores independientes para la secuencia.
Skyking

Encontré este enfoque en stackoverflow.com/a/16815056/2480481 antes de ver esta respuesta. Es más limpio, más fácil que lidiar con zip ().
m3nda

2
Me gusta que permite evitar triplicar el uso de memoria como la respuesta aceptada.
Kentzo

Esto no funciona bien con forbucles en Python 3.5+ debido a PEP 479 , que reemplaza cualquier StopIterationgenerado en un generador con a RuntimeError.
sidney el

28

Espero que esta sea una forma aún más elegante de hacerlo.

a = [1,2,3,4,5,6]
zip(a[::2], a[1::2])

[(1, 2), (3, 4), (5, 6)]

18

En caso de que esté interesado en el rendimiento, hice un pequeño punto de referencia (usando mi biblioteca simple_benchmark) para comparar el rendimiento de las soluciones e incluí una función de uno de mis paquetes:iteration_utilities.grouper

from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder

bench = BenchmarkBuilder()

@bench.add_function()
def Johnsyweb(l):
    def pairwise(iterable):
        "s -> (s0, s1), (s2, s3), (s4, s5), ..."
        a = iter(iterable)
        return zip(a, a)

    for x, y in pairwise(l):
        pass

@bench.add_function()
def Margus(data):
    for i, k in zip(data[0::2], data[1::2]):
        pass

@bench.add_function()
def pyanon(l):
    list(zip(l,l[1:]))[::2]

@bench.add_function()
def taskinoor(l):
    for i in range(0, len(l), 2):
        l[i], l[i+1]

@bench.add_function()
def mic_e(it):
    def pairwise(it):
        it = iter(it)
        while True:
            try:
                yield next(it), next(it)
            except StopIteration:
                return

    for a, b in pairwise(it):
        pass

@bench.add_function()
def MSeifert(it):
    for item1, item2 in grouper(it, 2):
        pass

bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)

ingrese la descripción de la imagen aquí

Entonces, si desea la solución más rápida sin dependencias externas, probablemente debería usar el enfoque dado por Johnysweb (en el momento de escribir esta es la respuesta más votada y aceptada).

Si no le importa la dependencia adicional, entonces el grouperde iteration_utilitiesprobablemente será un poco más rápido.

Pensamientos adicionales

Algunos de los enfoques tienen algunas restricciones, que no se han discutido aquí.

Por ejemplo, algunas soluciones solo funcionan para secuencias (es decir, listas, cadenas, etc.), por ejemplo, soluciones de Margus / pyanon / taskinoor que usan indexación, mientras que otras soluciones funcionan en cualquier iterable (es decir, secuencias y generadores, iteradores) como Johnysweb / mic_e / mis soluciones.

Luego, Johnysweb también proporcionó una solución que funciona para otros tamaños que no sean 2, mientras que las otras respuestas no lo hacen (está bien, iteration_utilities.groupertambién permite establecer el número de elementos en "grupo").

Luego también está la pregunta sobre qué debería suceder si hay un número impar de elementos en la lista. ¿Se debe descartar el artículo restante? ¿Se debe rellenar la lista para que tenga el mismo tamaño? ¿Debería devolverse el artículo restante como soltero? La otra respuesta no aborda este punto directamente, sin embargo, si no he pasado por alto nada, todos siguen el enfoque de que el elemento restante debe descartarse (a excepción de la respuesta de los taskinoors, lo que en realidad generará una excepción).

Con grouperusted puede decidir lo que quiere hacer:

>>> from iteration_utilities import grouper

>>> list(grouper([1, 2, 3], 2))  # as single
[(1, 2), (3,)]

>>> list(grouper([1, 2, 3], 2, truncate=True))  # ignored
[(1, 2)]

>>> list(grouper([1, 2, 3], 2, fillvalue=None))  # padded
[(1, 2), (3, None)]

12

Use los comandos zipy iterjuntos:

Me parece que esta solución iteres bastante elegante:

it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]

Que encontré en la documentación zip de Python 3 .

it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='\n')

# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11

Para generalizar a Nelementos a la vez:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]

10
for (i, k) in zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

zip(*iterable) devuelve una tupla con el siguiente elemento de cada iterable.

l[::2] devuelve el 1er, 3er, 5to elemento, etc. de la lista: el primer colon indica que el corte comienza al principio porque no hay un número detrás, el segundo colon solo es necesario si desea un 'paso en el corte '(en este caso 2).

l[1::2]hace lo mismo pero comienza en el segundo elemento de las listas, por lo que devuelve el segundo, cuarto, sexto, etc. elemento de la lista original .


44
Esta respuesta ya la dio Margus hace dos años. stackoverflow.com/questions/5389507/…
cababunga

1
1 para explicar cómo [number::number]funciona la sintaxis. útil para quienes no usan Python a menudo
Alby

2

puedes usar el paquete more_itertools .

import more_itertools

lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
    print(f'{i} + {j} = {i+j}')

2

Con desembalaje:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(str(i), '+', str(k), '=', str(i+k))

¡Guauu! Por qué no pude pensar en esto :) Solo necesita manejar el caso donde no hay un par absoluto (entradas impares)
Saurav Kumar

1

Para cualquiera que pueda ayudar, aquí hay una solución a un problema similar pero con pares superpuestos (en lugar de pares mutuamente excluyentes).

De la documentación de itertools de Python :

from itertools import izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

O, más generalmente:

from itertools import izip

def groupwise(iterable, n=2):
    "s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
    t = tee(iterable, n)
    for i in range(1, n):
        for j in range(0, i):
            next(t[i], None)
    return izip(*t)

1

Necesito dividir una lista por un número y arreglarlo así.

l = [1,2,3,4,5,6]

def divideByN(data, n):
        return [data[i*n : (i+1)*n] for i in range(len(data)//n)]  

>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]

>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]

1

Hay muchas formas de hacer eso. Por ejemplo:

lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]    
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

[i for i in zip(*[iter(lst)]*2)]    
>>>[(1, 2), (3, 4), (5, 6)]

0

Pensé que este es un buen lugar para compartir mi generalización de esto para n> 2, que es solo una ventana deslizante sobre un iterable:

def sliding_window(iterable, n):
    its = [ itertools.islice(iter, i, None) 
            for i, iter
            in enumerate(itertools.tee(iterable, n)) ]                               

    return itertools.izip(*its)

0

El título de esta pregunta es engañoso, parece que estás buscando pares consecutivos, pero si quieres iterar sobre el conjunto de todos los pares posibles, esto funcionará:

for i,v in enumerate(items[:-1]):
        for u in items[i+1:]:

0

Usando la escritura para poder verificar los datos con la herramienta de análisis estático mypy :

from typing import Iterator, Any, Iterable, TypeVar, Tuple

T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]

def legs(iterable: Iterator[T_]) -> Pairs_Iter:
    begin = next(iterable)
    for end in iterable:
        yield begin, end
        begin = end

0

Un enfoque simplista:

[(a[i],a[i+1]) for i in range(0,len(a),2)]

Esto es útil si su matriz es una y desea iterar en ella por pares. Para iterar en trillizos o más, simplemente cambie el comando de paso "rango", por ejemplo:

[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]

(tiene que lidiar con los valores en exceso si la longitud de su matriz y el paso no se ajustan)


0
from itertools import tee
def pairwise(iterable):
    a = iter(iterable)
    for i in a:
        try:
            yield (i, next(a))
        except StopIteration:
            yield(i, None)
       
    

for i in pairwise([3, 7, 8, 9, 90, 900]):
    print(i)

Salida:

(3, 7)
(8, 9)
(90, 900)
> 

puedes elegir cualquier relleno para Ninguno
Vishesh Mangla hace

-1

Aquí podemos tener un alt_elemmétodo que se ajuste a su bucle for.

def alt_elem(list, index=2):
    for i, elem in enumerate(list, start=1):
        if not i % index:
           yield tuple(list[i-index:i])


a = range(10)
for index in [2, 3, 4]:
    print("With index: {0}".format(index))
    for i in alt_elem(a, index):
       print(i)

Salida:

With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)

Nota: La solución anterior podría no ser eficiente considerando las operaciones realizadas en func.


-1
a_list = [1,2,3,4,5,6]
empty_list = [] 
for i in range(0,len(a_list),2):
   empty_list.append(a_list[i]+a_list[i+1])   
print(empty_list)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.