itertools.islice en comparación con el segmento de la lista

He estado tratando de aplicar un algoritmo para reducir una lista de python a una más pequeña según un criterio determinado. Debido al gran volumen de la lista original, en el orden de los 100k elementos, traté de hacer un conjunto de herramientas para evitar múltiples asignaciones de memoria, por lo que se me ocurrió esto:

reducedVec = [ 'F' if sum( 1 for x in islice(vec, i, i+ratio) if x == 'F' ) > ratio / 3.0 else 'T' for i in xrange(0, len(vec), ratio) ] 

El tiempo de ejecución para esto lleva un tiempo preocupantemente largo en el orden de unos pocos minutos, cuando vec tiene alrededor de 100k elementos. Cuando lo intenté en su lugar:

 reducedVec = [ 'F' if sum( 1 for x in vec[i:i+ratio] if x == 'F' ) > ratio / 3.0 else 'T' for i in xrange(0, len(vec), ratio) ] 

en esencia, reemplazar islice con un corte la ejecución es instantánea.

¿Puedes pensar en una explicación plausible para esto? Habría pensado que si evitaba asignar repetidamente una nueva lista con un número sustancial de elementos, en realidad me ahorraría algunos ciclos computacionales en lugar de paralizar toda la ejecución.

Saludos, Themis

islice trabaja con iterables arbitrarios. Para hacer esto, en lugar de saltar directamente al elemento nth, tiene que iterar sobre el primer n-1, desechándolos y luego ceder los que quieras.

Echa un vistazo a la implementación de Python pura de la documentación de itertools :

 def islice(iterable, *args): # islice('ABCDEFG', 2) --> AB # islice('ABCDEFG', 2, 4) --> CD # islice('ABCDEFG', 2, None) --> CDEFG # islice('ABCDEFG', 0, None, 2) --> ACEG s = slice(*args) it = iter(xrange(s.start or 0, s.stop or sys.maxint, s.step or 1)) nexti = next(it) for i, element in enumerate(iterable): if i == nexti: yield element nexti = next(it) 

Hablando de la documentación de itertools, si estuviera tratando de hacer esta operación, probablemente usaría la receta del grouper . En realidad no te ahorrará ningún recuerdo, pero podría hacerlo si lo reescribieras para que sea más vago, lo que no sería difícil.

 from __future__ import division from itertools import izip_longest def grouper(n, iterable, fillvalue=None): "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" args = [iter(iterable)] * n return izip_longest(fillvalue=fillvalue, *args) reducedVec = [] for chunk in grouper(ratio, vec): if sum(1 for x in chunk if x == 'F') > ratio / 3: reducedVec.append('F') else: reducedVec.append('T') 

Me gusta usar el grouper para abstraer los cortes consecutivos y encontrar este código mucho más fácil de leer que el original

Mi conjetura sería que el uso de islice() implica una llamada a la función Python para cada elemento de vec , mientras que el analizador comprende la notación de la porción extendida y se traduce directamente a las llamadas CPython.