Cómo leer un archivo grande, línea por línea, en Python

Quiero iterar sobre cada línea de un archivo completo. Una forma de hacerlo es leer el archivo completo, guardarlo en una lista y luego revisar la línea de interés. Este método usa mucha memoria, así que estoy buscando una alternativa.

Mi código hasta ahora:

for each_line in fileinput.input(input_file): do_something(each_line) for each_line_again in fileinput.input(input_file): do_something(each_line_again) 

Al ejecutar este código aparece un mensaje de error: device active .

¿Alguna sugerencia?

El propósito es calcular la similitud de la cadena por pares, lo que significa que para cada línea en el archivo, quiero calcular la distancia de Levenshtein con cada otra línea.

    La forma correcta y totalmente Pythonic de leer un archivo es la siguiente:

     with open(...) as f: for line in f: # Do something with 'line' 

    La instrucción with maneja abrir y cerrar el archivo, incluso si se produce una excepción en el bloque interno. La for line in f trata el objeto de archivo f como iterable, que utiliza automáticamente la E / S en búfer y la administración de memoria para que no tenga que preocuparse por los archivos grandes.

    Debe haber una, y preferiblemente solo una, manera obvia de hacerlo.

    Dos formas de memoria eficientes en orden ordenado (la primera es la mejor) –

    1. uso de – soportado desde python 2.5 y superior
    2. Uso del yield si realmente desea tener control sobre cuánto leer

    1. uso de with

    with es la manera agradable y eficiente de leer archivos de gran tamaño. ventajas – 1) el objeto de archivo se cierra automáticamente después de salir with bloque de ejecución. 2) manejo de excepciones dentro del bloque with . 3) la memoria for bucle se repite en línea a través del objeto f archivo. internamente, realiza IO en búfer (para optimizar en operaciones de IO costosas) y administración de memoria.

     with open("x.txt") as f: for line in f: do something with data 

    2. Uso del yield

    A veces, es posible que desee un control más preciso sobre cuánto leer en cada iteración. En ese caso usar iter y rendimiento . Tenga en cuenta que con este método uno necesita explícitamente cerrar el archivo al final.

     def readInChunks(fileObj, chunkSize=2048): """ Lazy function to read a file piece by piece. Default chunk size: 2kB. """ while True: data = fileObj.read(chunkSize) if not data: break yield data f = open('bigFile') for chuck in readInChunks(f): do_something(chunk) f.close() 

    Escollos y en aras de la integridad : los métodos a continuación no son tan buenos o no tan elegantes para leer archivos grandes, pero lea para obtener una comprensión completa.

    En Python, la forma más común de leer líneas de un archivo es hacer lo siguiente:

     for line in open('myfile','r').readlines(): do_something(line) 

    Sin embargo, cuando se hace esto, la función readlines() (lo mismo se aplica a la función read() ) carga todo el archivo en la memoria y luego se repite en él. Un método ligeramente mejor (los primeros dos métodos mencionados son los mejores) para archivos grandes es usar el módulo de entrada de fileinput , como se fileinput continuación:

     import fileinput for line in fileinput.input(['myfile']): do_something(line) 

    La llamada fileinput.input() lee las líneas de forma secuencial, pero no las guarda en la memoria después de haberlas leído o simplemente así, ya que el file en python es iterable.

    Referencias

    1. Python con statement

    Para quitar nuevas líneas:

     with open(file_path, 'rU') as f: for line_terminated in f: line = line_terminated.rstrip('\n') ... 

    Con el soporte de nueva línea universal, todas las líneas de archivos de texto parecerán terminadas con '\n' , independientemente de los terminadores del archivo, '\r' , '\n' o '\r\n' .

    EDITAR – Para especificar soporte de nueva línea universal:

    • Python 2 en Unix – open(file_path, mode='rU') – requerido [gracias @Dave ]
    • Python 2 en Windows – open(file_path, mode='rU') – opcional
    • Python 3 – open(file_path, newline=None) – opcional

    El parámetro de newline solo se admite en Python 3 y su valor predeterminado es None . El parámetro de mode defecto es 'r' en todos los casos. La U está en desuso en Python 3. En Python 2 en Windows, algún otro mecanismo parece traducir \r\n a \n .

    Docs:

    • abrir () para Python 2
    • abrir () para Python 3

    Para preservar los terminadores de línea nativos:

     with open(file_path, 'rb') as f: with line_native_terminated in f: ... 

    El modo binario todavía puede analizar el archivo en líneas con in . Cada línea tendrá los terminadores que tenga en el archivo.

    Gracias a la respuesta de @katrielalex , open () doc de Python y experimentos de iPython .

    esta es una forma posible de leer un archivo en python:

     f = open(input_file) for line in f: do_stuff(line) f.close() 

    no asigna una lista completa. Se itera sobre las líneas.

    Algún contexto por adelantado en cuanto a de dónde vengo. Los fragmentos de código están al final.

    Cuando puedo, prefiero usar una herramienta de código abierto como H2O para hacer lecturas de archivos CSV paralelos de muy alto rendimiento, pero esta herramienta está limitada en el conjunto de características. Termino escribiendo una gran cantidad de código para crear líneas de datos científicos antes de alimentar al grupo H2O para el aprendizaje supervisado adecuado.

    He estado leyendo archivos como el conjunto de datos HIGGS de 8 GB del repository de UCI e incluso archivos CSV de 40 GB para fines de ciencia de datos significativamente más rápido al agregar mucho paralelismo con la función de mapa y objeto de grupo de la biblioteca de multiprocesamiento. Por ejemplo, la agrupación en clúster con las búsquedas de vecinos más próximos y también los algoritmos de agrupación en clúster de DBSCAN y Markov requieren cierta delicadeza en la progtwigción paralela para evitar algunos problemas de tiempo de reloj de pared y de memoria que suponen un gran desafío.

    Por lo general, me gusta dividir el archivo por filas en partes utilizando primero las herramientas gnu y luego filmarlas en su totalidad para encontrarlas y leerlas en paralelo en el progtwig python. Yo uso algo así como más de 1000 archivos parciales comúnmente. Hacer estos trucos ayuda enormemente con la velocidad de procesamiento y los límites de memoria.

    Pandas dataframe.read_csv es de un solo hilo, por lo que puedes hacer estos trucos para hacerlos más rápidos ejecutando un mapa () para ejecución paralela. Puede usar htop para ver que, con un simple y simple pandas dataframe.read_csv, el 100% de la CPU en un solo núcleo es el cuello de botella real en pd.read_csv, no el disco en absoluto.

    Debo agregar que estoy usando un SSD en el bus rápido de la tarjeta de video, no un HD giratorio en el bus SATA6, más 16 núcleos de CPU.

    Además, otra técnica que descubrí que funciona muy bien en algunas aplicaciones es que el archivo CSV paralelo lee todo dentro de un archivo gigante, iniciando a cada trabajador con un desplazamiento diferente en el archivo, en lugar de dividir previamente un archivo grande en muchos archivos parciales. Utilice el archivo de python seek () y tell () en cada trabajador paralelo para leer el archivo de texto grande en tiras, en diferentes bytes de byte de inicio y de byte final en el archivo grande, todo al mismo tiempo al mismo tiempo. Puede realizar una búsqueda de expresiones regulares en los bytes y devolver el recuento de saltos de línea. Esta es una sum parcial. Finalmente, sume las sums parciales para obtener la sum global cuando la función de mapa regrese una vez que los trabajadores hayan terminado.

    A continuación se muestran algunos ejemplos de puntos de referencia que utilizan el truco de compensación de bytes paralelos

    Yo uso 2 archivos: HIGGS.csv es de 8 GB. Es del repository de aprendizaje automático de la UCI. all_bin .csv es de 40.4 GB y es de mi proyecto actual. Utilizo 2 progtwigs: el progtwig GNU wc que viene con Linux y el progtwig puro python fastread.py que desarrollé.

     HP-Z820:/mnt/fastssd/fast_file_reader$ ls -l /mnt/fastssd/nzv/HIGGS.csv -rw-rw-r-- 1 8035497980 Jan 24 16:00 /mnt/fastssd/nzv/HIGGS.csv HP-Z820:/mnt/fastssd$ ls -l all_bin.csv -rw-rw-r-- 1 40412077758 Feb 2 09:00 all_bin.csv ga@ga-HP-Z820:/mnt/fastssd$ time python fastread.py --fileName="all_bin.csv" --numProcesses=32 --balanceFactor=2 2367496 real 0m8.920s user 1m30.056s sys 2m38.744s In [1]: 40412077758. / 8.92 Out[1]: 4530501990.807175 

    Eso es aproximadamente 4.5 GB / s, o 45 Gb / s, velocidad de compresión de archivos. Eso no es ningún disco duro giratorio, amigo mío. Eso es en realidad un Samsung Pro 950 SSD.

    A continuación se muestra el índice de referencia de velocidad para el mismo archivo que se contabiliza en línea con gnu wc, un progtwig comstackdo en C puro.

    Lo que es genial es que puedes ver que mi progtwig de python puro se corresponde con la velocidad del progtwig gnu wc comstackdo C en este caso. Python se interpreta pero C se comstack, así que esta es una proeza de velocidad bastante interesante, creo que estarías de acuerdo. Por supuesto, wc realmente necesita ser cambiado a un progtwig paralelo, y entonces realmente vencería a los calcetines de mi progtwig de python. Pero tal como está hoy, gnu wc es solo un progtwig secuencial. Haces lo que puedes, y Python puede hacer paralelo hoy. La comstackción de Cython podría ayudarme (en otro momento). Tampoco se exploraron archivos asignados en la memoria todavía.

     HP-Z820:/mnt/fastssd$ time wc -l all_bin.csv 2367496 all_bin.csv real 0m8.807s user 0m1.168s sys 0m7.636s HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2 11000000 real 0m2.257s user 0m12.088s sys 0m20.512s HP-Z820:/mnt/fastssd/fast_file_reader$ time wc -l HIGGS.csv 11000000 HIGGS.csv real 0m1.820s user 0m0.364s sys 0m1.456s 

    Conclusión: la velocidad es buena para un progtwig de python puro en comparación con un progtwig de C. Sin embargo, no es lo suficientemente bueno como para usar el progtwig python puro sobre el progtwig C, al menos para propósitos de conteo de líneas. Generalmente, la técnica se puede utilizar para otro procesamiento de archivos, por lo que este código de Python sigue siendo bueno.

    Pregunta: ¿Comstackr la expresión regular solo una vez y pasarla a todos los trabajadores mejorará la velocidad? Respuesta: La precomstackción de Regex NO ayuda en esta aplicación. Supongo que la razón es que domina la sobrecarga de la serialización y creación de procesos para todos los trabajadores.

    Una cosa más. ¿La lectura de archivos CSV paralelos incluso ayuda? ¿Es el disco el cuello de botella, o es la CPU? Muchas de las llamadas respuestas mejor calificadas en stackoverflow contienen la sabiduría común de los desarrolladores de que solo necesitas un hilo para leer un archivo, lo mejor que puedes hacer, dicen. ¿Están seguros, sin embargo?

    Vamos a averiguar:

     HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2 11000000 real 0m2.256s user 0m10.696s sys 0m19.952s HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=1 11000000 real 0m17.380s user 0m11.124s sys 0m6.272s 

    Oh sí, sí lo hace. La lectura paralela de archivos funciona bastante bien. ¡Bueno, allá vas!

    PD. En caso de que algunos de ustedes quisieran saber, ¿qué pasaría si el balanceFactor fuera 2 cuando utilizaba un solo proceso de trabajo? Bueno, es horrible:

     HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=2 11000000 real 1m37.077s user 0m12.432s sys 1m24.700s 

    Partes clave del progtwig de python fastread.py:

     fileBytes = stat(fileName).st_size # Read quickly from OS how many bytes are in a text file startByte, endByte = PartitionDataToWorkers(workers=numProcesses, items=fileBytes, balanceFactor=balanceFactor) p = Pool(numProcesses) partialSum = p.starmap(ReadFileSegment, zip(startByte, endByte, repeat(fileName))) # startByte is already a list. fileName is made into a same-length list of duplicates values. globalSum = sum(partialSum) print(globalSum) def ReadFileSegment(startByte, endByte, fileName, searchChar='\n'): # counts number of searchChar appearing in the byte range with open(fileName, 'r') as f: f.seek(startByte-1) # seek is initially at byte 0 and then moves forward the specified amount, so seek(5) points at the 6th byte. bytes = f.read(endByte - startByte + 1) cnt = len(re.findall(searchChar, bytes)) # findall with implicit compiling runs just as fast here as re.compile once + re.finditer many times. return cnt 

    La definición de PartitionDataToWorkers es simplemente un código secuencial ordinario. Lo dejé afuera en caso de que alguien más quiera obtener algo de práctica sobre cómo es la progtwigción paralela. Regalé gratuitamente las partes más difíciles: el código paralelo probado y en funcionamiento, para su beneficio de aprendizaje.

    Gracias a: El proyecto H2O de código abierto, de Arno y Cliff y el personal de H2O por su excelente software y videos instructivos, que me han brindado la inspiración para este lector de offset de bytes paralelos de alto rendimiento de python puro como se muestra arriba. H2O realiza la lectura paralela de archivos con java, es invocable por los progtwigs Python y R, y es una locura rápida, más rápida que cualquier otra cosa en el planeta al leer grandes archivos CSV.

    Katrielalex proporcionó la manera de abrir y leer un archivo.

    Sin embargo, la forma en que funciona su algoritmo lee el archivo completo para cada línea del archivo. Eso significa que la cantidad total de lectura de un archivo, y el cálculo de la distancia de Levenshtein , se realizará N * N si N es la cantidad de líneas en el archivo. Ya que le preocupa el tamaño del archivo y no quiere guardarlo en la memoria, me preocupa el tiempo de ejecución cuadrático resultante. Su algoritmo está en la clase de algoritmos O (n ^ 2) que a menudo se pueden mejorar con la especialización.

    Sospecho que ya conoce el compromiso de la memoria con el tiempo de ejecución aquí, pero tal vez quiera investigar si existe una forma eficiente de calcular múltiples distancias de Levenshtein en paralelo. Si es así sería interesante compartir su solución aquí.

    ¿Cuántas líneas tienen sus archivos y en qué tipo de máquina (mem & cpu power) tiene que ejecutar su algoritmo, y cuál es el tiempo de ejecución tolerado?

    El código se vería así:

     with f_outer as open(input_file, 'r'): for line_outer in f_outer: with f_inner as open(input_file, 'r'): for line_inner in f_inner: compute_distance(line_outer, line_inner) 

    Pero las preguntas son: ¿cómo almacena las distancias (matriz?) Y puede obtener una ventaja de preparar, por ejemplo, la línea externa para el procesamiento, o almacenar en caché algunos resultados intermedios para su reutilización.

     #Using a text file for the example with open("yourFile.txt","r") as f: text = f.readlines() for line in text: print line 
    • Abre tu archivo para leer (r)
    • Lea el archivo completo y guarde cada línea en una lista (texto)
    • Recorre la lista imprimiendo cada línea.

    Si desea, por ejemplo, verificar una línea específica para una longitud mayor que 10, trabaje con lo que ya tiene disponible.

     for line in text: if len(line) > 10: print line 

    De la documentación de python para fileinput .input ():

    Esto se repite en las líneas de todos los archivos enumerados en sys.argv[1:] , de forma sys.stdin en sys.stdin si la lista está vacía

    Además, la definición de la función es:

     fileinput.FileInput([files[, inplace[, backup[, mode[, openhook]]]]]) 

    leyendo entre líneas, esto me dice que los files pueden ser una lista para que puedas tener algo como:

     for each_line in fileinput.input([input_file, input_file]): do_something(each_line) 

    Vea aquí para más información.

    Recomiendo encarecidamente no utilizar la carga de archivos predeterminada, ya que es tremendamente lento. Debe mirar las funciones numpy y las funciones IOpro (por ejemplo, numpy.loadtxt ()).

    http://docs.scipy.org/doc/numpy/user/basics.io.genfromtxt.html

    https://store.continuum.io/cshop/iopro/

    Entonces puedes dividir tu operación de pares en trozos:

     import numpy as np import math lines_total = n similarity = np.zeros(n,n) lines_per_chunk = m n_chunks = math.ceil(float(n)/m) for i in xrange(n_chunks): for j in xrange(n_chunks): chunk_i = (function of your choice to read lines i*lines_per_chunk to (i+1)*lines_per_chunk) chunk_j = (function of your choice to read lines j*lines_per_chunk to (j+1)*lines_per_chunk) similarity[i*lines_per_chunk:(i+1)*lines_per_chunk, j*lines_per_chunk:(j+1)*lines_per_chunk] = fast_operation(chunk_i, chunk_j) 

    ¡Casi siempre es mucho más rápido cargar datos en trozos y luego realizar operaciones matriciales en lugar de hacerlo elemento por elemento!

    La mejor manera de leer un archivo grande, línea por línea es usar la función de enumeración de Python

     with open(file_name, "rU") as read_file: for i, row in enumerate(read_file, 1): #do something #i in line of that line #row containts all data of that line