python - Iteración sobre listas de sectores
loops iteration (9)
Responda a la última parte de la pregunta:
actualización de la pregunta: ¿Cómo modificar la función que ha proporcionado para almacenar los elementos adicionales y usarlos cuando la próxima lista padre se alimenta a la función?
Si necesita almacenar estado, puede usar un objeto para eso.
class Chunker(object):
"""Split `iterable` on evenly sized chunks.
Leftovers are remembered and yielded at the next call.
"""
def __init__(self, chunksize):
assert chunksize > 0
self.chunksize = chunksize
self.chunk = []
def __call__(self, iterable):
"""Yield items from `iterable` `self.chunksize` at the time."""
assert len(self.chunk) < self.chunksize
for item in iterable:
self.chunk.append(item)
if len(self.chunk) == self.chunksize:
# yield collected full chunk
yield self.chunk
self.chunk = []
Ejemplo:
chunker = Chunker(3)
for s in "abcd", "efgh":
for chunk in chunker(s):
print ''''.join(chunk)
if chunker.chunk: # is there anything left?
print ''''.join(chunker.chunk)
Salida:
abc
def
gh
Quiero un algoritmo para iterar sobre sectores de lista. El tamaño de las rebanadas se establece fuera de la función y puede diferir.
En mi opinión, es algo así como:
for list_of_x_items in fatherList:
foo(list_of_x_items)
¿Hay alguna manera de definir correctamente list_of_x_items
o alguna otra forma de hacerlo con Python 2.5?
edit1: Aclaración Tanto los términos de "partición" como "ventana deslizante" suenan aplicables a mi tarea, pero no soy un experto. Entonces explicaré el problema un poco más y agregaré a la pregunta:
The fatherList es un numpy.array multinivel que obtengo de un archivo. La función tiene que encontrar los promedios de las series (el usuario proporciona la longitud de las series). Para promediar estoy usando la función mean()
. Ahora para la expansión de preguntas:
edit2: ¿Cómo modificar la función que ha proporcionado para almacenar los elementos adicionales y usarlos cuando la próxima lista padre se alimenta a la función?
por ejemplo, si la lista es de longitud 10 y el tamaño de un fragmento es 3, entonces el décimo miembro de la lista se almacena y se agrega al comienzo de la siguiente lista.
Relacionado:
¿Te refieres a algo como:
def callonslices(size, fatherList, foo):
for i in xrange(0, len(fatherList), size):
foo(fatherList[i:i+size])
Si esta es más o menos la funcionalidad que desea, puede, si lo desea, vestirla un poco en un generador:
def sliceup(size, fatherList):
for i in xrange(0, len(fatherList), size):
yield fatherList[i:i+size]
y entonces:
def callonslices(size, fatherList, foo):
for sli in sliceup(size, fatherList):
foo(sli)
Ampliando la respuesta de @Ants Aasma: En Python 3.7 el manejo de la excepción StopIteration
changed (de acuerdo con PEP-479 ). Una versión compatible sería:
from itertools import chain, islice
def ichunked(seq, chunksize):
it = iter(seq)
while True:
try:
yield chain([next(it)], islice(it, chunksize - 1))
except StopIteration:
return
No estoy seguro, pero parece que quieres hacer lo que se llama una media móvil. numpy proporciona facilidades para esto (la función convolve).
>>> x = numpy.array(range(20)) >>> x array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) >>> n = 2 # moving average window >>> numpy.convolve(numpy.ones(n)/n, x)[n-1:-n+1] array([ 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5])
Lo bueno es que numpy.ones(n) / n
muy bien los diferentes esquemas de ponderación (simplemente cambie numpy.ones(n) / n
a otra cosa).
Puede encontrar un material completo aquí: http://www.scipy.org/Cookbook/SignalSmooth
Para un liner near-one (después de la importación de itertools
) en la línea de la respuesta de Nadia que trata con tamaños divisibles sin fragmentos sin relleno:
>>> import itertools as itt
>>> chunksize = 5
>>> myseq = range(18)
>>> cnt = itt.count()
>>> print [ tuple(grp) for k,grp in itt.groupby(myseq, key=lambda x: cnt.next()//chunksize%2)]
[(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14), (15, 16, 17)]
Si lo desea, puede deshacerse del requisito itertools.count()
usando enumerate()
, con un aspecto bastante más feo:
[ [e[1] for e in grp] for k,grp in itt.groupby(enumerate(myseq), key=lambda x: x[0]//chunksize%2) ]
(En este ejemplo, el enumerate()
sería superfluo, pero no todas las secuencias son rangos nítidos como este, obviamente)
No es tan claro como algunas otras respuestas, pero es útil en caso de apuro, especialmente si ya está importando itertools
.
Si desea dividir una lista en sectores, puede usar este truco:
list_of_slices = zip(*(iter(the_list),) * slice_size)
Por ejemplo
>>> zip(*(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]
Si la cantidad de elementos no es divisible por el tamaño del corte y desea rellenar la lista con Ninguno, puede hacer esto:
>>> map(None, *(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
Es un pequeño truco sucio
OK, explicaré cómo funciona. Será complicado de explicar, pero haré lo mejor que pueda.
Primero un poco de historia:
En Python puedes multiplicar una lista por un número como este:
[1, 2, 3] * 3 -> [1, 2, 3, 1, 2, 3, 1, 2, 3]
([1, 2, 3],) * 3 -> ([1, 2, 3], [1, 2, 3], [1, 2, 3])
Y un objeto iterator se puede consumir una vez como este:
>>> l=iter([1, 2, 3])
>>> l.next()
1
>>> l.next()
2
>>> l.next()
3
La función zip devuelve una lista de tuplas, donde la i-ésima tupla contiene el elemento i-ésimo de cada una de las secuencias de argumento o iterables. Por ejemplo:
zip([1, 2, 3], [20, 30, 40]) -> [(1, 20), (2, 30), (3, 40)]
zip(*[(1, 20), (2, 30), (3, 40)]) -> [[1, 2, 3], [20, 30, 40]]
El * delante del código postal utilizado para descomprimir argumentos. Puedes encontrar más detalles here . Asi que
zip(*[(1, 20), (2, 30), (3, 40)])
es en realidad equivalente a
zip((1, 20), (2, 30), (3, 40))
pero trabaja con una cantidad variable de argumentos
Ahora volvamos al truco:
list_of_slices = zip(*(iter(the_list),) * slice_size)
iter(the_list)
-> convierte la lista en un iterador
(iter(the_list),) * N
-> generará una referencia N al iterador_list.
zip(*(iter(the_list),) * N)
-> alimentará esa lista de iteradores en zip. Que a su vez los agrupará en tuplas de tamaño N. Pero dado que todos los N elementos son, de hecho, referencias al mismo iterador iter(the_list)
el resultado serán llamadas repetidas a next()
en el iterador original
Espero que esto lo explique. Te aconsejo que vayas con una solución más fácil de entender. Solo tuve la tentación de mencionar este truco porque me gusta.
Si desea poder consumir cualquier iterable, puede usar estas funciones:
from itertools import chain, islice
def ichunked(seq, chunksize):
"""Yields items from an iterator in iterable chunks."""
it = iter(seq)
while True:
yield chain([it.next()], islice(it, chunksize-1))
def chunked(seq, chunksize):
"""Yields items from an iterator in list chunks."""
for chunk in ichunked(seq, chunksize):
yield list(chunk)
Su pregunta podría usar más detalles, pero qué tal:
def iterate_over_slices(the_list, slice_size):
for start in range(0, len(the_list)-slice_size):
slice = the_list[start:start+slice_size]
foo(slice)
Usa un generador:
big_list = [1,2,3,4,5,6,7,8,9]
slice_length = 3
def sliceIterator(lst, sliceLen):
for i in range(len(lst) - sliceLen + 1):
yield lst[i:i + sliceLen]
for slice in sliceIterator(big_list, slice_length):
foo(slice)
sliceIterator
implementa una "ventana deslizante" de ancho sliceLen
sobre el squence lst, es decir, produce cortes superpuestos: [1,2,3], [2,3,4], [3,4,5], ... No estoy seguro si esa es la intención de OP, sin embargo.