Tengo una lista de longitud arbitraria, y necesito dividirla en trozos de igual tamaño y operarla. Hay algunas formas obvias de hacer esto, como mantener un contador y dos listas, y cuando se llene la segunda lista, agréguela a la primera lista y vacíe la segunda lista para la siguiente ronda de datos, pero esto es potencialmente extremadamente costoso.
Me preguntaba si alguien tenía una buena solución para listas de cualquier longitud, por ejemplo, usando generadores.
Estaba buscando algo útil itertools
pero no pude encontrar nada obviamente útil. Sin embargo, podría haberlo perdido.
Pregunta relacionada: ¿Cuál es la forma más "pitónica" de iterar sobre una lista en fragmentos?
Respuestas:
Aquí hay un generador que produce los fragmentos que desea:
Si usa Python 2, debe usar en
xrange()
lugar derange()
:También puede simplemente usar la comprensión de la lista en lugar de escribir una función, aunque es una buena idea encapsular operaciones como esta en funciones con nombre para que su código sea más fácil de entender. Python 3:
Versión de Python 2:
fuente
Si quieres algo super simple:
Usar en
xrange()
lugar derange()
en el caso de Python 2.xfuente
max()
.Directamente de la (antigua) documentación de Python (recetas para itertools):
La versión actual, según lo sugerido por JFSebastian:
Supongo que la máquina del tiempo de Guido funciona, funcionó, funcionará, habrá funcionado, estaba funcionando de nuevo.
Estas soluciones funcionan porque
[iter(iterable)]*n
(o el equivalente en la versión anterior) crea un iterador, repetidon
veces en la lista.izip_longest
luego realiza de manera efectiva un round robin de "cada" iterador; Debido a que este es el mismo iterador, cada una de esas llamadas avanza, lo que da como resultado que cada zip-roundrobin genere una tupla den
elementos.fuente
list(grouper(3, range(10)))
regresa[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
, y todas las tuplas son de longitud 3. Explique su comentario porque no puedo entenderlo; ¿cómo se llama una cosa y cómo se define como un múltiplo de 3 en "esperar que su cosa sea un múltiplo de 3"? Gracias de antemano.itertools
enfoque funcional sofisticado quel==[1, 2, 3]
entoncesf(*l)
es equivalente af(1, 2, 3)
. Ver esa pregunta y la documentación oficial .Sé que esto es un poco viejo, pero nadie ha mencionado aún
numpy.array_split
:fuente
Me sorprende que nadie haya pensado en usar
iter
la forma de dos argumentos :Manifestación:
Esto funciona con cualquier iterable y produce una salida perezosa. Devuelve tuplas en lugar de iteradores, pero creo que tiene una cierta elegancia. Tampoco rellena; Si desea relleno, una variación simple de lo anterior será suficiente:
Manifestación:
Al igual que las
izip_longest
soluciones basadas en lo anterior, lo anterior siempre es perfecto. Hasta donde sé, no hay una receta de itertools de una o dos líneas para una función que opcionalmente rellena. Al combinar los dos enfoques anteriores, este se acerca bastante:Manifestación:
Creo que este es el chunker más corto propuesto que ofrece relleno opcional.
Como observó Tomasz Gandor , los dos fragmentos de relleno se detendrán inesperadamente si encuentran una larga secuencia de valores de relleno. Aquí hay una variación final que resuelve ese problema de manera razonable:
Manifestación:
fuente
islice(it, size)
expresión básica y la incrustaron (como lo había hecho yo) en una construcción de bucle. Solo que pensaste en la versión de dos argumentos deiter()
(que desconocía por completo), lo que la hace súper elegante (y probablemente la más efectiva para el rendimiento). No tenía idea de que el primer argumento paraiter
cambiar a una función de argumento 0 cuando se le dio el centinela. Devuelve un iterador (pot. Infinito) de trozos, puede usar un iterador (pot. Infinito) como entrada, no tiene nilen()
ningún segmento de matriz. ¡Increíble!it
iterador. En segundo lugar, y lo más importante: terminará prematuramente sipadval
realmente existe un fragmento de su iterable, y debe procesarse.izip_longest
enfoque, por ejemplo, sospecho que podría ser una compensación compleja. Pero ... ¿no es elpadval
problema compartido por cada respuesta aquí que ofrece unpadval
parámetro?()
como el centinela, que hace el trabajo correctamente Esto es debido.tuple(islice(it, size))
Rendimientos()
cuandoit
está vacío.)Aquí hay un generador que funciona en iterables arbitrarios:
Ejemplo:
fuente
fuente
map(None, iter)
es igualizip_longest(iter)
.*
tupla de tu iterador frente a ti? Posiblemente en su texto de respuesta, pero he notado que se*
usaba de esa manera en Python antes. ¡Gracias!Simple pero elegante
o si lo prefieres:
fuente
1
yl
son indistinguibles. Como son0
yO
. Y a veces inclusoI
y1
.print [l[x:x+10] for x in xrange(1, len(l), 10)]
range
.Crítica de otras respuestas aquí:
Ninguna de estas respuestas son trozos de tamaño uniforme, todos dejan un trozo runt al final, por lo que no están completamente equilibrados. Si estaba utilizando estas funciones para distribuir el trabajo, ha incorporado la posibilidad de que uno termine mucho antes que los demás, por lo que se quedaría sin hacer nada mientras los demás continuaron trabajando duro.
Por ejemplo, la respuesta principal actual termina con:
¡Solo odio a esa runa al final!
Otros, como
list(grouper(3, xrange(7)))
, ychunk(xrange(7), 3)
tanto la rentabilidad:[(0, 1, 2), (3, 4, 5), (6, None, None)]
. LosNone
's son solo relleno, y en mi opinión, bastante poco elegantes. NO están fragmentando uniformemente los iterables.¿Por qué no podemos dividir esto mejor?
Mis soluciones
Aquí hay una solución equilibrada, adaptada de una función que he usado en producción (Nota en Python 3 para reemplazar
xrange
arange
):Y creé un generador que hace lo mismo si lo pones en una lista:
Y finalmente, ya que veo que todas las funciones anteriores devuelven elementos en un orden contiguo (como se les dio):
Salida
Para probarlos:
Que imprime:
Tenga en cuenta que el generador contiguo proporciona fragmentos en los mismos patrones de longitud que los otros dos, pero los elementos están todos en orden y están divididos de manera tan uniforme como uno puede dividir una lista de elementos discretos.
fuente
list(grouper(3, xrange(7)))
y el segundo,chunk(xrange(7), 3)
ambos vuelven:[(0, 1, 2), (3, 4, 5), (6, None, None)]
. LosNone
's son solo relleno, y en mi opinión, bastante poco elegantes. NO están fragmentando uniformemente los iterables. Gracias por tu voto!import pandas as pd; [pd.DataFrame(np.arange(7))[i::3] for i in xrange(3)]
Vi la respuesta más impresionante de Python-ish en un duplicado de esta pregunta:
Puede crear n-tuplas para cualquier n. Si
a = range(1, 15)
, entonces el resultado será:Si la lista se divide por igual, entonces se puede sustituir
zip_longest
conzip
, de lo contrario el triplete(13, 14, None)
se perdería. Python 3 se usa arriba. Para Python 2, useizip_longest
.fuente
zip(i, i, i, ... i)
con argumentos "chunk_size" para zip () se puede escribir comozip(*[i]*chunk_size)
si eso es una buena idea o no es discutible, por supuesto.zip_longest
debe usarse, como se hace en: stackoverflow.com/a/434411/1959808range(1, 15)
ya falta elementos, porque hay 14 elementos enrange(1, 15)
, no 15.Si conoce el tamaño de la lista:
Si no lo hace (un iterador):
En el último caso, puede reformularse de una manera más bella si puede estar seguro de que la secuencia siempre contiene un número entero de fragmentos de un tamaño determinado (es decir, no hay un último fragmento incompleto).
fuente
La biblioteca toolz tiene la
partition
función para esto:fuente
Si tenía un tamaño de fragmento de 3, por ejemplo, podría hacer:
fuente: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/
Lo usaría cuando mi tamaño de fragmento es un número fijo que puedo escribir, por ejemplo, '3', y nunca cambiaría.
fuente
Me gusta mucho la versión del documento de Python propuesta por tzot y JFSebastian, pero tiene dos deficiencias:
Estoy usando este mucho en mi código:
ACTUALIZACIÓN: Una versión de trozos perezosos:
fuente
while True
bucle?StopIteration
eleva cuandotuple
está vacío yiterable.next()
se ejecuta. Sin embargo, no funciona correctamente en Python moderno, donde se debe salir de un generadorreturn
, no subirStopIteration
. Atry/except StopIteration: return
alrededor de todo el ciclo (y cambiandoiterable.next()
anext(iterable)
compatibilidad con versiones cruzadas) corrige esto con una sobrecarga mínima al menos.Donde AA es matriz, SS es tamaño de fragmento. Por ejemplo:
fuente
Tenía curiosidad sobre el rendimiento de diferentes enfoques y aquí está:
Probado en Python 3.5.1
Resultados:
fuente
time
biblioteca no es una gran idea cuando tenemos untimeit
módulocódigo:
resultado:
fuente
También puede usar la
get_chunks
función deutilspie
biblioteca como:Puede instalar a
utilspie
través de pip:Descargo de responsabilidad: soy el creador de la biblioteca utilspie .
fuente
En este punto, creo que necesitamos un generador recursivo , por si acaso ...
En python 2:
En python 3:
Además, en caso de una invasión alienígena masiva, un generador recursivo decorado podría ser útil:
fuente
Con las expresiones de asignación en Python 3.8 se vuelve bastante agradable:
Esto funciona en un iterativo arbitrario, no solo en una lista.
fuente
je, una versión de línea
fuente
def chunk
lugar dechunk=lambda
tiene .__ name__ atributo 'chunk' en lugar de '<lambda>'. El nombre específico es más útil en las trazas.<lamba>
o no es, al menos, una diferencia notable.uso:
fuente
Otra versión más explícita.
fuente
Sin llamar a len (), lo cual es bueno para listas grandes:
Y esto es para iterables:
El sabor funcional de lo anterior:
O:
O:
fuente
len()
en listas grandes; Es una operación de tiempo constante.Aquí hay una lista de enfoques adicionales:
Dado
Código
La biblioteca estándar
more_itertools
+Referencias
zip_longest
( publicación relacionada , publicación relacionada )setdefault
(los resultados ordenados requieren Python 3.6+)collections.defaultdict
(los resultados ordenados requieren Python 3.6+)more_itertools.chunked
( relacionado publicado )more_itertools.sliced
more_itertools.grouper
( publicación relacionada )more_itertools.windowed
(véase tambiénstagger
,zip_offset
)+ Una biblioteca de terceros que implementa recetas de itertools y más.
> pip install more_itertools
fuente
Ver esta referencia
Python3
fuente
zip(*[iter(range(7))]*3)
solo devuelve[(0, 1, 2), (3, 4, 5)]
y olvida el6
de la entrada.Ya que todos aquí están hablando de iteradores.
boltons
tiene un método perfecto para eso, llamadoiterutils.chunked_iter
.Salida:
Pero si no desea ser misericordioso con la memoria, puede usar el método antiguo y almacenarlo completo
list
en primer lugar coniterutils.chunked
.fuente
Una solución mas
fuente
fuente
Considere usar piezas matplotlib.cbook
por ejemplo:
fuente