2010-10-21 13 views
14

estoy en busca de una función que toma un iterable i y un tamaño de n y produce tuplas de longitud n que son valores secuenciales de i:generador Python que los grupos de otra iterables en grupos de N

x = [1,2,3,4,5,6,7,8,9,0] 
[z for z in TheFunc(x,3)] 

da

[(1,2,3),(4,5,6),(7,8,9),(0)] 

¿Existe una función de este tipo en la biblioteca estándar?

Si existe como parte de la biblioteca estándar, parece que no puedo encontrarla y me he quedado sin términos para buscar. Podría escribir el mío, pero preferiría no hacerlo.

Respuesta

17

Ver la receta grouper en el docs for the itertools package

def grouper(n, iterable, fillvalue=None): 
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" 
    args = [iter(iterable)] * n 
    return izip_longest(fillvalue=fillvalue, *args) 

(Sin embargo, esto es un duplicado de quite a few questions.)

+2

Si supiera buscar "mero" no habría tenido que preguntar en absoluto. Pero no sabía de ese término. – BCS

+0

+1. Olvidé por completo las prácticas recetas en los documentos. – Skurmedel

+2

Terminé usando esto, pero tuve que hackear el filtrado de los valores de sutura en una etapa posterior. – BCS

4

¿Qué tal este? Sin embargo, no tiene un valor de relleno.

>>> def partition(itr, n): 
...  i = iter(itr) 
...  res = None 
...  while True: 
...    res = list(itertools.islice(i, 0, n)) 
...    if res == []: 
...      break 
...    yield res 
... 
>>> list(partition([1, 2, 3, 4, 5, 6, 7, 8, 9], 3)) 
[[1, 2, 3], [4, 5, 6], [7, 8, 9]] 
>>> 

Utiliza una copia del iterable original, que agota para cada empalme sucesivo. La única otra forma en que mi cerebro cansado podía pensar era en generar puntos finales de empalme con rango.

Tal vez debería cambiar list() a tuple() por lo que corresponde mejor a su salida.

+0

LOL. Tienes que estar bromeando. Hay un error aquí en la respuesta, ¿y mi edición fue rechazada? Mi respeto por la comunidad SO ha disminuido mucho. –

+2

btw, itertools.islice (i, 0, 3) -> itertools.islice (i, 0, n) Todavía no puedo creer la comunidad SO. –

+0

No lo rechacé, alguien más lo hizo. Pero estás en lo cierto. El 3 está codificado en forma rígida, anulando el propósito de n como parámetro. Si quieres puedo editarlo pero no obtendrás ningún representante, depende de ti:) – Skurmedel

0
def grouper(iterable, n): 
     while True: 
      yield itertools.chain(iterable.next(),itertools.islice(iterable, n-1)) 
+2

Su respuesta sería mejor si incluyó una breve explicación para ir con ese código. – trooper

16

Cuando se desea agrupar un iterador en trozos de nsin relleno el grupo final con un valor de relleno, utilice iter(lambda: list(IT.islice(iterable, n)), []):

import itertools as IT 

def grouper(n, iterable): 
    """ 
    >>> list(grouper(3, 'ABCDEFG')) 
    [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']] 
    """ 
    iterable = iter(iterable) 
    return iter(lambda: list(IT.islice(iterable, n)), []) 

seq = [1,2,3,4,5,6,7] 
print(list(grouper(3, seq))) 

rendimientos

[[1, 2, 3], [4, 5, 6], [7]] 

Hay una explicación de cómo funciona en la segunda mitad de this answer.


Cuando se desea agrupar un iterador en trozos de ny la almohadilla el grupo final con un valor de relleno, utilice el grouper recipezip_longest(*[iterator]*n):

Por ejemplo, en python2:

>>> list(IT.izip_longest(*[iter(seq)]*3, fillvalue='x')) 
[(1, 2, 3), (4, 5, 6), (7, 'x', 'x')] 

En Python3, lo que era izip_longest ahora se llama zip_longest:

>>> list(IT.zip_longest(*[iter(seq)]*3, fillvalue='x')) 
[(1, 2, 3), (4, 5, 6), (7, 'x', 'x')] 

Cuando se desea agrupar una secuencia en trozos de n puede utilizar el chunks receta:

def chunks(seq, n): 
    # https://stackoverflow.com/a/312464/190597 (Ned Batchelder) 
    """ Yield successive n-sized chunks from seq.""" 
    for i in xrange(0, len(seq), n): 
     yield seq[i:i + n] 

Tenga en cuenta que, a diferencia de los iteradores en general, sequences by definition tienen una longitud (es decir, __len__ se define).

0

Sé que esto ha sido respondido varias veces, pero estoy agregando mi solución que debería mejorar en ambos, aplicabilidad general a secuencias e iteradores, legibilidad (no hay salida de bucle invisible por excepción StopIteration) y el rendimiento en comparación con el mero receta. Es muy similar a la última respuesta de Svein.

def chunkify(iterable, n): 
    iterable = iter(iterable) 
    n_rest = n - 1 

    for item in iterable: 
     rest = itertools.islice(iterable, n_rest) 
     yield itertools.chain((item,), rest) 
0

Aquí es una solución diferente que no hace uso de itertools y, a pesar de que tiene un par de líneas, que aparentemente supera las respuestas dadas cuando trozos son mucho más corta que la longitud iterable. Sin embargo, para grandes porciones, las otras respuestas son mucho más rápidas.

def batchiter(iterable, batch_size): 
    """ 
    >>> list(batchiter('ABCDEFG', 3)) 
    [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']] 
    """ 
    next_batch = [] 
    for element in iterable: 
     next_batch.append(element) 
     if len(next_batch) == batch_size: 
      batch, next_batch = next_batch, [] 
      yield batch 
    if next_batch: 
     yield next_batch 


In [19]: %timeit [b for b in batchiter(range(1000), 3)] 
1000 loops, best of 3: 644 µs per loop 

In [20]: %timeit [b for b in grouper(3, range(1000))] 
1000 loops, best of 3: 897 µs per loop 

In [21]: %timeit [b for b in partition(range(1000), 3)] 
1000 loops, best of 3: 890 µs per loop 

In [22]: %timeit [b for b in batchiter(range(1000), 333)] 
1000 loops, best of 3: 540 µs per loop 

In [23]: %timeit [b for b in grouper(333, range(1000))] 
10000 loops, best of 3: 81.7 µs per loop 

In [24]: %timeit [b for b in partition(range(1000), 333)] 
10000 loops, best of 3: 80.1 µs per loop 
1

Esta es una solicitud muy común en Python. Lo suficientemente común como para incluirlo en el paquete de utilidad unificada boltons. Primero apagado, there are extensive docs here. Además, the module está diseñado y probado para confiar únicamente en la biblioteca estándar (compatible con Python 2 y 3), lo que significa que puede just download the file directly into your project.

# if you downloaded/embedded, try: 
# from iterutils import chunked 

# with `pip install boltons` use: 

from boltons.iterutils import chunked 

print(chunked(range(10), 3)) 
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] 

Hay una forma de iterador/generador de secuencias indefinidas/largas, así:

print(list(chunked_iter(range(10), 3, fill=None))) 
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, None, None]] 

Como se puede ver, también puede rellenar la secuencia con un valor de su elección, también. Finalmente, como mantenedor, puedo asegurarle que, si bien el código ha sido descargado/probado por miles de desarrolladores, si encuentra algún problema, obtendrá el soporte más rápido posible a través del boltons GitHub Issues page. Espero que esto (y/o cualquiera de las otras recetas de 150+ boltons) hayan sido de ayuda.

1

Esta es una pregunta muy antigua, pero creo que es útil mencionar el siguiente enfoque para el caso general. Su principal mérito es que solo necesita iterar sobre los datos una vez, por lo que funcionará con los cursores de la base de datos u otras secuencias que solo se pueden usar una vez. También lo encuentro más legible.

def chunks(n, iterator): 
    out = [] 
    for elem in iterator: 
     out.append(elem) 
     if len(out) == n: 
      yield out 
      out = [] 
    yield out 
Cuestiones relacionadas