Je veux parcourir chaque ligne d'un fichier entier. Une façon de faire est de lire le fichier entier, de le sauvegarder dans une liste, puis de parcourir la ligne qui vous intéresse. Cette méthode utilise beaucoup de mémoire, je cherche donc une alternative.
Mon code jusqu'ici:
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 génère un message d'erreur: device active
.
Aucune suggestion?
Le but est de calculer la similarité des chaînes par paires, ce qui signifie que pour chaque ligne du fichier, je souhaite calculer la distance de Levenshtein avec toutes les autres lignes.
La manière correcte, entièrement Pythonic, de lire un fichier est la suivante:
with open(...) as f:
for line in f:
# Do something with 'line'
L'instruction with
gère l'ouverture et la fermeture du fichier, y compris si une exception est levée dans le bloc interne. Le for line in f
traite le fichier objet f
comme un élément itérable, qui utilise automatiquement les E/S et la gestion de la mémoire en mémoire tampon de sorte que vous n'avez pas à vous soucier des fichiers volumineux.
Il devrait y avoir un - et de préférence un seul - moyen évident de le faire.
Deux moyens efficaces en termes de mémoire, classés par ordre décroissant (le premier est le meilleur) -
with
- prise en charge à partir de python 2.5 et versions ultérieuresyield
si vous voulez vraiment avoir le contrôle sur ce qu'il faut lirewith
with
est le moyen agréable et efficace utilisé par Pythonic pour lire de gros fichiers. avantages - 1) L'objet fichier est automatiquement fermé après la sortie du bloc d'exécution with
. 2) gestion des exceptions à l'intérieur du bloc with
. 3) La boucle memory for
effectue une itération ligne par ligne dans le fichier f
. en interne, il met en mémoire tampon IO (à optimiser sur des opérations coûteuses IO) et de la mémoire.
with open("x.txt") as f:
for line in f:
do something with data
yield
Parfois, on peut souhaiter un contrôle plus fin sur le nombre de lectures à chaque itération. Dans ce cas, utilisez iter & rendement . Notez qu'avec cette méthode, il faut explicitement 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é - les méthodes ci-dessous ne sont pas aussi bonnes ou moins élégantes pour la lecture de fichiers volumineux, mais veuillez les lire pour mieux comprendre.
En Python, le moyen le plus courant de lire les lignes d'un fichier consiste à:
for line in open('myfile','r').readlines():
do_something(line)
Lorsque cela est fait, cependant, la fonction readlines()
(il en va de même pour la fonction read()
) charge tout le fichier en 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 fichiers volumineux consiste à utiliser le module fileinput
, comme suit:
import fileinput
for line in fileinput.input(['myfile']):
do_something(line)
l'appel fileinput.input()
lit les lignes de manière séquentielle, mais ne les garde pas en mémoire après leur lecture ou même simplement ainsi, puisque file
dans python est itérable.
with open(file_path, 'rU') as f:
for line_terminated in f:
line = line_terminated.rstrip('\n')
...
Avec support de nouvelle ligne universel , toutes les lignes de fichier texte semblent se terminer par '\n'
, quels que soient les terminateurs du fichier, '\r'
, '\n'
ou '\r\n'
.
EDIT - Pour spécifier la prise en charge de la nouvelle ligne universelle:
open(file_path, mode='rU')
- requis [merci @ Dave ]open(file_path, mode='rU')
- facultatifopen(file_path, newline=None)
- facultatifLe paramètre newline
est uniquement pris en charge dans Python 3 et par défaut à None
. Le paramètre mode
par défaut est 'r'
dans tous les cas. La U
est obsolète dans Python 3. Dans Python 2 sous Windows, un autre mécanisme semble traduire \r\n
en \n
.
Docs:
with open(file_path, 'rb') as f:
with line_native_terminated in f:
...
Le mode binaire peut toujours analyser le fichier en lignes avec in
. Chaque ligne aura ses terminaisons dans le fichier.
Merci à @ katrielalex , réponse , à Python open () doc, et iPython = expériences.
c'est un moyen possible de lire un fichier en python:
f = open(input_file)
for line in f:
do_stuff(line)
f.close()
il n'alloue pas une liste complète. Il parcourt les lignes.
Un peu de contexte pour savoir d’où je viens. Les extraits de code sont à la fin.
Lorsque je le peux, je préfère utiliser un outil open source tel que H2O pour effectuer des lectures de fichiers CSV parallèles aux performances extrêmement élevées, mais cet outil présente un ensemble de fonctionnalités limité. Je finis par écrire beaucoup de code pour créer des pipelines de données informatiques avant d'alimenter le cluster H2O pour l'apprentissage supervisé proprement dit.
Je lisais beaucoup plus rapidement des fichiers tels que le jeu de données HIGGS de 8 Go du dépôt UCI et même des fichiers CSV de 40 Go à des fins de science des données en ajoutant beaucoup de parallélisme à la fonction objet de pool et carte de la bibliothèque de multitraitement. Par exemple, la mise en cluster avec les recherches du voisin le plus proche ainsi que les algorithmes de cluster DBSCAN et de Markov nécessitent une certaine finesse de programmation parallèle pour contourner des problèmes de mémoire et de temps très difficiles.
En général, j'aime diviser le fichier en plusieurs parties à l'aide des outils gnu, puis glob-les les masquer pour les trouver et les lire en parallèle dans le programme python. J'utilise quelque chose comme 1000 + fichiers partiels couramment. Ces astuces contribuent énormément à la vitesse de traitement et aux limites de la mémoire.
Le pandas dataframe.read_csv est à thread unique. Vous pouvez donc réaliser ces astuces pour rendre pandas beaucoup plus rapide en exécutant un map () pour une exécution parallèle. Vous pouvez utiliser htop pour constater qu'avec dataframe.read_csv séquentiel ancien pandas, 100% du processeur sur un seul noyau constitue le goulet d'étranglement actuel dans pd.read_csv, pas le disque du tout.
J'ajouterais que j'utilise un disque SSD sur un bus de carte vidéo rapide, pas un disque HD en rotation sur un bus SATA6, plus 16 cœurs de processeur.
En outre, une autre technique que j'ai trouvée fonctionne très bien dans certaines applications est le fichier CSV parallèle qui lit tout dans un fichier géant, en commençant chaque ouvrier à un décalage différent dans le fichier, plutôt que de pré-diviser un gros fichier en plusieurs fichiers de pièce. Utilisez les fichiers search () et tell () de python dans chaque opérateur parallèle pour lire le gros fichier texte en bandes, à différents emplacements de début et de fin d'octets décalés d'octet dans le même fichier simultanément. Vous pouvez effectuer une recherche rationnelle sur les octets et renvoyer le nombre de sauts de ligne. C'est une somme partielle. Enfin, résumez les sommes partielles pour obtenir la somme globale lorsque la fonction de carte reviendra une fois les tâches terminées.
Voici quelques exemples de points de repère utilisant l'astuce de décalage d'octet parallèle:
J'utilise 2 fichiers: HIGGS.csv est de 8 Go. Il provient du référentiel d’apprentissage automatique UCI. all_bin .csv, 40,4 Go, provient de mon projet actuel. J'utilise 2 programmes: GNU programme wc fourni avec Linux et le programme pur python fastread.py 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
Cela correspond à une vitesse de traitement des fichiers d'environ 4,5 Go/s ou 45 Gb/s. Ce n’est pas un disque dur en rotation, mon ami. C’est en fait un Samsung Pro 950 SSD.
Vous trouverez ci-dessous le repère de vitesse pour le même fichier 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 pur python correspond essentiellement à la vitesse du programme C gnu wc compilé dans ce cas. Python est interprété mais C est compilé, donc c'est un exploit de vitesse assez intéressant, je pense que vous seriez d'accord. Bien sûr, il faut vraiment changer le programme wc en un programme parallèle pour éviter que les programmes de mon python ne s'en mêlent. Mais dans l’état actuel des choses, gnu wc n’est qu’un programme séquentiel. Vous faites ce que vous pouvez et python peut faire en parallèle aujourd'hui. La compilation en Cython pourrait peut-être m'aider (pour une autre fois). De plus, les fichiers mappé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 pur python par rapport à un programme C. Cependant, il n’est pas suffisant d’utiliser le programme pur python par-dessus le programme C, au moins à des fins de décompte linéaire. Généralement, la technique peut être utilisée pour d'autres traitements de fichiers, donc ce code python est toujours bon.
Question: Est-ce que la compilation de la regex une seule fois et sa transmission à tous les travailleurs améliorera la vitesse? Réponse: La pré-compilation Regex n’aide en rien cette application. Je suppose que la raison en est que la surcharge de la sérialisation et de la création de processus pour tous les travailleurs est prépondérante.
Une dernière chose. La lecture de fichiers CSV en parallèle est-elle utile Le disque est-il le goulot d'étranglement ou s'agit-il de la CPU? Beaucoup de réponses dites top-notées sur stackoverflow contiennent la sagesse commune du développement selon laquelle il suffit d'un seul thread pour lire un fichier, mieux vous pouvez le faire, disent-ils. Sont-ils sûrs, cependant?
Découvrons-le:
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 c'est le cas. La lecture de fichiers en parallèle fonctionne assez bien. Eh bien voilà!
Ps. Au cas où certains d'entre vous voudraient savoir, que se passe-t-il si balanceFactor vaut 2 lors de l'utilisation d'un processus de travail unique? 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
Parties clés du programme fastread.py 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
La définition de PartitionDataToWorkers est simplement un code séquentiel ordinaire. Je l'ai laissé au cas où quelqu'un d'autre voudrait s'exercer à la programmation parallèle. J'ai donné gratuitement les parties les plus difficiles: le code parallèle testé et fonctionnel, pour votre bénéfice d'apprentissage.
Merci à: Le projet open-source H2O d’Arno and Cliff et du personnel de H2O pour leurs logiciels de qualité et leurs vidéos d’instruction, qui m’ont inspiré pour ce lecteur de décalage d'octets parallèles haute performance python pur, comme indiqué ci-dessus. H2O lit les fichiers en parallèle en utilisant Java, est appelé par les programmes python et R, et est incroyablement rapide à la lecture de gros fichiers CSV.
Katrielalex a fourni le moyen d'ouvrir et de lire un fichier.
Cependant, la façon dont votre algorithme se comporte lit tout le fichier pour chaque ligne du fichier. Cela signifie que la quantité totale de lecture d'un fichier - et le calcul de la distance de Levenshtein - sera effectuée N * N si N est la quantité de lignes dans le fichier. Puisque vous êtes préoccupé par la taille du fichier et que vous ne voulez pas le garder en mémoire, je suis préoccupé par le résultat durée d'exécution du second degré . Votre algorithme appartient à la classe d'algorithmes O (n ^ 2) qui peuvent souvent être améliorés avec la spécialisation.
Je suppose que vous connaissez déjà le compromis entre mémoire et durée d'exécution ici, mais vous voudrez peut-être vérifier s'il existe un moyen efficace de calculer plusieurs distances de Levenshtein en parallèle. Dans ce cas, il serait intéressant de partager votre solution ici.
Combien de lignes ont vos fichiers et sur quel type de machine (mem & cpu power) votre algorithme doit-il s'exécuter, et quelle est la durée d'exécution tolérée?
Le code ressemblerait à ceci:
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 tirer un avantage de la préparation, par exemple? outer_line pour le traitement ou la mise en cache de certains résultats intermédiaires pour la réutilisation.
#Using a text file for the example
with open("yourFile.txt","r") as f:
text = f.readlines()
for line in text:
print line
Si vous souhaitez, 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
Je recommande fortement de ne pas utiliser le chargement de fichier par défaut car il est extrêmement lent. Vous devriez examiner les fonctions 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/
Ensuite, vous pouvez diviser 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 de faire ensuite des opérations matricielles dessus que de le faire élément par élément !!
De la documentation python pour fileinput . Input ():
Cela parcourt les lignes de tous les fichiers listés dans
sys.argv[1:]
, par défaut àsys.stdin
si la liste est vide.
de plus, la définition de la fonction est la suivante:
fileinput.FileInput([files[, inplace[, backup[, mode[, openhook]]]]])
en lisant entre les lignes, cela me dit que files
peut être une liste pour que vous puissiez avoir quelque chose comme:
for each_line in fileinput.input([input_file, input_file]):
do_something(each_line)
Voir ici pour plus d'informations