Comment lire un gros Fichier, ligne par ligne en Python

je veux itérer sur chaque ligne d'un fichier entier. Une façon de le faire est par la lecture de tout fichier, l'enregistrer sur une liste, puis aller sur la ligne d'intérêt. Cette méthode utilise beaucoup de mémoire, donc je suis à la recherche d'une alternative.

mon code jusqu'à présent:

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)

L'exécution de ce code donne un message d'erreur: device active .

des suggestions?

le but est de calculer similarité de chaîne, signification pour chaque ligne dans le fichier, je veux calculer la distance Levenshtein avec chaque autre ligne.

441
demandé sur Peter Mortensen 2011-11-04 17:26:29

10 réponses

la façon correcte et entièrement pythonique de lire un fichier est la suivante:

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

la déclaration with traite de l'ouverture et de la fermeture du dossier, y compris si une exception est soulevée dans le bloc intérieur. Le for line in f traite l'objet de fichier f comme un itérable, qui utilise automatiquement les entrées/sorties et la gestion de la mémoire tampon de sorte que vous n'avez pas à vous soucier des gros fichiers.

il devrait y en avoir un ... et de préférence, seulement une façon évidente de le faire.

1113
répondu Katriel 2018-06-28 11:45:20

deux façons efficaces de mémoriser par ordre de priorité (la première est la meilleure) -

  1. utilisation de with - supporté par python 2.5 et plus
  2. utilisation de yield si vous voulez vraiment avoir le contrôle sur combien lire

1. utilisation de with

with est la façon agréable et efficace pythonic de lire de grands fichiers. avantages - 1) l'objet du fichier est automatiquement fermé après avoir quitté le bloc d'exécution with . 2) Traitement des exceptions à l'intérieur du bloc with . 3) la boucle de mémoire for itère à travers l'objet de fichier f ligne par ligne. en interne, il fait io tamponné (optimisé sur les opérations d'IO coûteuses) et la gestion de la mémoire.

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

2. utilisation de yield

parfois, on pourrait vouloir plus de contrôle à grain fin sur la quantité à lire dans chaque itération. Dans ce cas utiliser iter & "1519350920 le" rendement . Remarque avec cette méthode explicitement les besoins de fermer le fichier à la fin.

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()

pièges et par souci d'exhaustivité - ci-dessous les méthodes ne sont pas aussi bonnes ou pas aussi élégant pour la lecture de grands fichiers, mais s'il vous plaît lire pour obtenir la compréhension arrondie.

En Python, la façon la plus courante de lire les lignes d'un fichier est de faire ce qui suit:

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

lorsque cela est fait, cependant, la fonction readlines() (la même chose s'applique à la fonction read() ) charge le fichier entier dans la mémoire, puis itère dessus. Une approche légèrement meilleure (les deux premières méthodes mentionnées sont les meilleures) pour les gros fichiers est d'utiliser le module fileinput , comme suit:

import fileinput

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

l'appel fileinput.input() lit les lignes séquentiellement, mais ne les garde pas en mémoire après qu'ils ont depuis file en python est itérable.

Références

  1. Python avec l'énoncé
96
répondu Srikar Appalaraju 2017-06-16 06:05:55

pour dénuder les lignes:

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

Avec universelle retour à la ligne de soutien toutes les lignes du fichier texte semble être résilié avec '\n' , quels que soient les indicateurs de fin dans le fichier '\r' , '\n' , ou '\r\n' .

EDITION Pour spécifier universelle de retour à la ligne de soutien:

  • Python 2 sur Unix - open(file_path, mode='rU') - required [merci @Dave ]
  • Python 2 sur Windows - open(file_path, mode='rU') - optionnel
  • Python 3 - open(file_path, newline=None) - optionnel

le paramètre newline n'est supporté que dans Python 3 et par défaut dans None . Le paramètre mode est par défaut 'r' dans tous les cas. Le U est déprécié en Python 3. En Python 2 sous Windows, un autre mécanisme apparaît traduisez \r\n en \n .

Docs:

Pour préserver natif de la ligne de terminateurs:

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

peut encore diviser le fichier en lignes avec in . Chaque ligne aura les terminateurs qu'elle a dans le fichier.

merci à @katrielalex 's réponse , Python open () doc, et iPython expériences.

30
répondu Bob Stein 2017-12-05 14:48:28

c'est une façon possible de lire un fichier en python:

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

il n'attribue pas de liste complète. Il itère sur les lignes.

15
répondu Simon 2017-01-04 16:13:52

le contexte d'où je viens. Les bribes de Code sont à la fin.

quand je peux, je préfère utiliser un outil open source comme H2O pour faire des lectures de fichiers CSV parallèles super haute performance, mais cet outil est limité dans l'ensemble des fonctionnalités. Je finis par écrire beaucoup de code pour créer des pipelines de science de données avant de nourrir à H2O cluster pour l'apprentissage supervisé propre.

j'ai lu des fichiers comme 8 Go Higgs dataset de UCI repo et même les fichiers CSV de 40 Go à des fins de science des données beaucoup plus rapidement en ajoutant beaucoup de parallélisme avec l'objet de la bibliothèque multiprocessing et la fonction de carte. Par exemple, le clustering avec des recherches de voisins les plus proches et aussi des algorithmes de clustering DBSCAN et Markov nécessite une certaine finesse de programmation parallèle pour contourner certains problèmes de mémoire et d'horloge murale très difficiles.

d'habitude, j'aime briser le fichier row-wise en pièces en utilisant les outils gnu d'abord et ensuite glob-filemask tous de trouver et de lire en parallèle dans le programme python. J'utilise quelque chose comme 1000+ fichiers partiels couramment. Faire ces trucs aide immensément avec la vitesse de traitement et les limites de mémoire.

Les pandas dataframe.read_csv est un thread simple, vous pouvez donc faire ces trucs pour rendre les pandas plus rapides en exécutant une map() pour l'exécution parallèle. Vous pouvez utiliser htop pour le voir avec une vieille dataframe de pandas séquentielle.read_csv, 100% cpu sur juste un noyau est le réel goulot d'étranglement dans la maladie de parkinson.read_csv, pas le disque.

je devrais ajouter que j'utilise un SSD sur le bus de cartes vidéo rapide, pas un HD tournant sur le bus SATA6, plus 16 cœurs CPU.

en outre, une autre technique que j'ai découvert fonctionne très bien dans certaines applications est parallèle fichier CSV lit tout dans un fichier géant, en commençant chaque travailleur à décalage différent dans le fichier, plutôt que pré-séparation d'un grand fichier en plusieurs fichiers de la partie. Utilisez les fichiers de python seek () et tell() dans chaque opérateur parallèle doit lire le gros fichier texte en strips, à des emplacements de début-byte et de fin-byte différents dans le gros fichier, le tout en même temps. Vous pouvez faire un findall regex sur les octets, et retourner le nombre de linefeeds. C'est une somme partielle. Enfin, additionnez les sommes partielles pour obtenir la somme globale lorsque la fonction de carte retourne après les ouvriers finis.

voici quelques exemples de benchmarks utilisant l'effet offset des octets parallèles:

j'utilise 2 fichiers: HIGGS.csv est de 8 Go. Il provient du référentiel d'apprentissage machine de L'UCI. all_bin .csv est de 40,4 GO et est de mon projet actuel. J'utilise 2 programmes: le programme GNU wc qui est livré avec Linux, et le Python pur fastread.py programme que j'ai développé.

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

C'est environ 4.5 GB/s, ou 45 Gb/s, la vitesse de défilement des fichiers. Ce n'est pas pas de la filature de disque dur, mon ami. C'est en fait un Samsung Pro 950 SSD.

ci-Dessous est la vitesse benchmark pour le même fichier étant compté en ligne par gnu wc, un programme compilé en C pur.

ce qui est cool, c'est que vous pouvez voir que mon programme Python pur a essentiellement correspondu à la vitesse du programme C compilé par gnu wc dans ce cas. Python est interprété mais C est compilé, donc c'est un exploit assez intéressant de vitesse, je pense que vous en conviendrez. Bien sûr, nous avons vraiment besoin d'être changés en un programme parallèle, et puis il battrait vraiment les chaussettes de mon programme python. Mais, comme il se aujourd'hui, gnu wc n'est qu'un programme séquentiel. Vous faites ce que vous pouvez, et python peut faire le parallèle aujourd'hui. Cython la compilation pourrait être en mesure de m'aider (pour une autre fois). De plus, les fichiers cartographiés en mémoire n'ont pas encore été explorés.

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

Conclusion: la vitesse est bonne pour un programme python pur comparé à un programme C. Cependant, il n'est pas suffisant d'utiliser le programme python pur sur le programme C, au moins pour le calcul en ligne. Généralement, la technique peut être utilisée pour d'autres traitement de fichier, donc ce code python est toujours bon.

Question: Est-ce que le fait de compiler le regex une seule fois et de le transmettre à tous les travailleurs améliorera la vitesse? Réponse: la pré-compilation Regex n'aide pas dans cette application. Je suppose que la raison en est que les frais généraux de la sérialisation des processus et de la création pour tous les travailleurs sont dominants.

encore une chose. Est-ce que la lecture parallèle de fichiers CSV aide même? Le disque est-il le goulot d'étranglement, ou est-ce le CPU? Beaucoup les soi-disant meilleures réponses sur stackoverflow contiennent la sagesse commune de dev que vous n'avez besoin que d'un seul fil pour lire un fichier, le mieux que vous pouvez faire, disent-ils. Est-on bien sûr, si?

voyons:

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 oui, oui. La lecture de fichiers en parallèle fonctionne très bien. Eh bien là vous allez!

Ps. Dans le cas où certains d'entre vous voulaient savoir, que faire si le balanceFactor était 2 lors de l'utilisation d'un processus d'un seul travailleur? Eh bien, c'est 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

fastread.py programme python:

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

le def pour Partitiondataworkers est juste un code séquentiel ordinaire. Je l'ai laissée dans le cas où quelqu'un d'autre veut obtenir un peu de pratique sur ce que la programmation parallèle. J'ai donné gratuitement les parties les plus difficiles: le code parallèle testé et utilisé, pour votre bénéfice d'apprentissage.

merci à: le projet open-source H2O, par Arno et Cliff et le Personnel H2O pour leur grand logiciel et des vidéos instructives, qui m'ont fourni l'inspiration pour ce pur lecteur offset d'octets parallèle haute performance python comme montré ci-dessus. H2O fait de la lecture de fichiers en parallèle en utilisant java, est appelable par les programmes python et R, et est follement rapide, plus rapide que n'importe quoi sur la planète à lire de gros fichiers CSV.

5
répondu Geoffrey Anderson 2017-02-02 16:48:32

Katrielalex a fourni le moyen d'ouvrir et de lire un fichier.

quelle que soit la façon dont votre algorithme fonctionne, il lit le fichier entier pour chaque ligne du fichier. Cela signifie que la quantité globale de lecture d'un fichier - et le calcul de la distance Levenshtein - sera fait N*n Si N est la quantité de lignes dans le fichier. Puisque vous vous souciez de la taille du fichier et que vous ne voulez pas le garder en mémoire, je m'inquiète de l'exécution quadratique qui en résulte. . Votre algorithme est dans la classe O (N^2) des algorithmes qui peuvent souvent être améliorés avec la spécialisation.

je soupçonne que vous connaissez déjà le compromis entre la mémoire et le temps d'exécution ici, mais peut-être que vous voudriez enquêter s'il y a un moyen efficace de calculer plusieurs distances Levenshtein en parallèle. Si oui il serait intéressant de partager votre solution ici.

Combien de lignes de vos fichiers, et sur quel type de machine (mem & cpu power) votre algorithme doit-il fonctionner, et quelle est la durée d'exécution tolérée?

Le Code

ressemblerait à:

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)

mais les questions sont comment stockez-vous les distances (matrice?) et pouvez-vous obtenir un avantage de préparer par exemple la ligne extérieure pour le traitement, ou la mise en cache de certains résultats intermédiaires pour la réutilisation.

4
répondu cfi 2011-11-16 17:33:05
#Using a text file for the example
with open("yourFile.txt","r") as f:
    text = f.readlines()
for line in text:
    print line
  • ouvrir votre fichier pour lire (r)
  • lire le fichier en entier et enregistrer chaque ligne dans un liste (texte)
  • boucle à travers la liste d'impression de chaque ligne.

si vous voulez, par exemple, vérifier une ligne spécifique pour une longueur supérieure à 10, travaillez avec ce que vous avez déjà disponible.

for line in text:
    if len(line) > 10:
        print line
3
répondu Bishop 2016-11-24 12:54:10

de la documentation python pour fileinput .input ():

cette itération sur les lignes de tous les fichiers énumérés dans sys.argv[1:] , en défaut à sys.stdin si la liste est vide

en outre, la définition de la fonction est:

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

en lisant entre les lignes, cela me dit que files peut être une liste donc vous pourriez avoir quelque chose comme:

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

voir ici pour plus d'information

2
répondu KevinDTimm 2014-06-26 19:17:43

je recommande fortement de ne pas utiliser le chargement de fichier par défaut car il est terriblement lent. Vous devriez regarder dans les fonctions de numpy et les fonctions IOpro (par exemple numpy.loadtxt()).

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

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

alors vous pouvez casser votre opération par paires en morceaux:

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) 

il est presque toujours beaucoup plus rapide de charger des données en morceaux et d'effectuer ensuite des opérations matricielles que de le faire élément par élément!!

2
répondu John Haberstroh 2014-10-17 19:39:11

la Meilleure façon de lire de gros fichiers, ligne par ligne est d'utiliser python énumérer fonction

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
-3
répondu Anurag Misra 2017-08-24 07:02:13