Tarfile en Python: ¿Puedo descomprimir más eficientemente extrayendo solo algunos de los datos?

Estoy ordenando una gran cantidad de escenas de Landsat del USGS, que vienen como archivos tar.gz. Estoy escribiendo un simple script en python para descomprimirlos. Cada archivo contiene 15 imágenes tiff de 60-120 mb de tamaño, que sumn un poco más de 2 gb. Puedo extraer fácilmente un archivo completo con el siguiente código:

import tarfile fileName = "LT50250232011160-SC20140922132408.tar.gz" tfile = tarfile.open(fileName, 'r:gz') tfile.extractall("newfolder/") 

Solo necesito 6 de esos 15 tiffs, identificados como “bandas” en el título. Estos son algunos de los archivos más grandes, por lo que juntos representan aproximadamente la mitad de los datos. Entonces, pensé que podría acelerar este proceso modificando el código de la siguiente manera:

 fileName = "LT50250232011160-SC20140922132408.tar.gz" tfile = tarfile.open(fileName, 'r:gz') membersList = tfile.getmembers() namesList = tfile.getnames() bandsList = [x for x, y in zip(membersList, namesList) if "band" in y] print("extracting...") tfile.extractall("newfolder/",members=bandsList) 

Sin embargo, agregar un temporizador a ambos scripts no revela un aumento significativo de la eficiencia del segundo script (en mi sistema, ambos se ejecutan en aproximadamente un minuto en una sola escena). Si bien la extracción es algo más rápida, parece que la ganancia se compensa con el tiempo que lleva averiguar qué archivos deben extraerse en primer lugar.

La pregunta es: ¿es este compromiso inherente a lo que estoy haciendo, o simplemente el resultado de que mi código sea ineficiente? Soy relativamente nuevo en Python y solo descubrí el tarfile hoy, por lo que no me sorprendería si esto último fuera cierto, pero no he podido encontrar ninguna recomendación para la extracción eficiente de solo una parte de un archivo.

¡Gracias!

    El problema es que un archivo tar no tiene una lista de archivos central, pero almacena los archivos secuencialmente con un encabezado antes de cada archivo. El archivo tar se comprime luego a través de gzip para darle tar.gz Con un archivo tar , si no desea extraer un archivo determinado, simplemente omita el siguiente header->size bytes en un archivo y luego lea el siguiente encabezado. Si el archivo comprimido se comprime adicionalmente, aún tendrá que omitir tantos bytes, solo que no dentro del archivo comprimido sino dentro del flujo de datos descomprimido, lo que para algunos formatos de compresión funciona, pero para otros requiere que descomprima todo lo que se encuentre en el medio .

    gzip pertenece a la última clase de esquemas de compresión. Entonces, mientras ahorra tiempo al no escribir los archivos no deseados en el disco, su código aún los descomprime. Es posible que pueda superar ese problema anulando la clase _Stream para archivos que no son gzip, pero para sus archivos gz , no hay nada que pueda hacer al respecto.

    Puede hacerlo de manera más eficiente, abriendo el archivo tar como un flujo. ( https://docs.python.org/2/library/tarfile.html#tarfile.open )

     mkdir tartest cd tartest/ dd if=/dev/urandom of=file1 count=100 bs=1M dd if=/dev/urandom of=file2 count=100 bs=1M dd if=/dev/urandom of=file3 count=100 bs=1M dd if=/dev/urandom of=file4 count=100 bs=1M dd if=/dev/urandom of=file5 count=100 bs=1M cd .. tar czvf test.tgz tartest 

    Ahora lee así:

     import tarfile fileName = "test.tgz" tfile = tarfile.open(fileName, 'r|gz') for t in tfile: if "file3" in t.name: f = tfile.extractfile(t) if f: print(len(f.read())) 

    Tenga en cuenta la | en el comando de abrir. Solo leemos el file3 .

     $ time python test.py 104857600 real 0m1.201s user 0m0.820s sys 0m0.377s 

    Si cambio r|gz nuevo a r:gz obtengo:

     $ time python test.py 104857600 real 0m7.033s user 0m6.293s sys 0m0.730s 

    Aproximadamente 5 veces más rápido (ya que tenemos 5 archivos de igual tamaño). Es así porque la forma estándar de apertura permite buscar hacia atrás; solo puede hacerlo en un archivo comprimido de archivos comprimidos extrayendo (no sé la razón exacta de eso). Si abres como un flujo, no puedes buscar más al azar, pero si lees secuencialmente, lo cual es posible en tu caso, es mucho más rápido. Sin embargo, no se puede llegar antes a los getnames . Pero eso no es necesario en este caso.