create - gzip python example
Una forma más limpia de leer/gunzip un archivo enorme en python (2)
Así que tengo algunos archivos .gz bastante gigantescos: estamos hablando de 10 a 20 gb cada uno cuando se descomprimen.
Necesito recorrer cada línea de ellos, así que estoy usando el estándar:
import gzip
f = gzip.open(path+myFile, ''r'')
for line in f.readlines():
#(yadda yadda)
f.close()
Sin embargo, los comandos open()
y close()
toman AGES, usando hasta el 98% de la memoria + CPU. Tanto es así que el programa sale e imprime Killed
to the terminal. ¿Tal vez está cargando todo el archivo extraído en la memoria?
Ahora estoy usando algo como:
from subprocess import call
f = open(path+''myfile.txt'', ''w'')
call([''gunzip'', ''-c'', path+myfile], stdout=f)
#do some looping through the file
f.close()
#then delete extracted file
Esto funciona. ¿Pero hay una forma más limpia?
Echa un vistazo a los pandas, en particular las herramientas IO . Son compatibles con la compresión gzip al leer archivos y puede leer archivos en trozos. Además, los pandas son muy rápidos y eficientes en la memoria.
Como nunca lo intenté, no sé qué tan bien viven juntos la compresión y la lectura en fragmentos, pero podría valer la pena intentarlo.
Estoy 99% seguro de que su problema no está en gzip.open()
, sino en readlines()
.
Como explica la documentación :
f.readlines () devuelve una lista que contiene todas las líneas de datos en el archivo.
Obviamente, eso requiere leer leer y descomprimir todo el archivo, y construir una lista absolutamente gigantesca.
Lo más probable es que, en realidad, las llamadas a malloc
para asignar toda esa memoria se lleven para siempre. Y luego, al final de este alcance (asumiendo que estás usando CPython), tiene que GC toda esa lista gigantesca, que también durará para siempre.
Casi nunca quieres usar readlines
. A menos que estés usando un Python muy antiguo, haz esto:
for line in f:
Un file
es un iterable lleno de líneas, al igual que la list
devuelta por las líneas de lectura. Excepto que en realidad no es una list
, genera más líneas sobre la marcha mediante la lectura de un búfer. Por lo tanto, en un momento dado, solo tendrá una línea y un par de búferes del orden de 10 MB cada uno, en lugar de una list
25 GB. Y la lectura y la descompresión se extenderán a lo largo de la vida útil del bucle, en lugar de hacerlo de una vez.
Desde una prueba rápida, con un archivo gzip de gzip.open()
, gzip.open()
es efectivamente instantáneo, for line in f: pass
toma unos segundos, gzip.close()
es efectivamente instantánea. Pero si hago for line in f.readlines(): pass
, me toma ... bueno, no estoy seguro de cuánto tiempo, porque después de un minuto mi sistema entró en el infierno y tuve que matar al intérprete haz que responda a cualquier cosa ...
Dado que esto ha surgido una docena de veces más desde esta respuesta, escribí esta publicación del blog que explica un poco más.