Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Accélérer la lecture d'un fichier

+4 votes

Dans mon projet j'ai une fonction qui lit plusieurs fichiers faisant plus de 25 millions de lignes et le traitement peut donc prendre plusieurs minutes.
Y'a t-il un moyen d'augmenter la vitesse de lecture d'un fichier sans avoir à réécrire la fonction dans un autre langage ?
Est-ce que des approches de type Cython peuvent répondre à ce genre de problème ?

demandé 5-Jun-2015 par Jev (370 points)
edité 5-Jun-2015 par max

Un generateur pourrait eventuellement aider, maintenant faudrait savoir a quoi ressemble la fonction, et que fait elle exactement.

Il s'agit de récupérer le résultat d'un autre programme donc la fonction n'est pas en soi compliquée. En gros on a trois colonnes, les deux premières correspondent à la position de deux marqueurs (la position du premier étant toujours avant celle du second) tandis que le troisième correspond à une valeur. L'objectif est de faire la moyenne des valeurs de marqueurs de même distance. Ce n'est pas exactement ça en réalité (il y a plus de colonnes) mais le code peut se résumer du coup à quelque chose comme ça :

def read_file(openfile, dic) :
    with open(openfile, "r") as infile : 
        for line in infile :
            ls = [float(value) for value in line.strip().split("\t")]
            distance = ls[1] - ls[0]
            try : dic[distance] = (dic[distance][0] + ls[2], dic[distance][1] + 1)
            except KeyError :   dic[distance] = (ls[2], 1)

Du coup je ne suis pas sûr qu'un générateur soit la solution.

Non, en effet :( Voici une autre proposition (je n'ai fait aucun bechmark, mais je pense que ça ns fera gagner quelques millisecondes)

with open('openfile') as infile:
    while True:
        lines = infile.readlines(8192) # lire une certaine qte d'octets a chaque fois
        if not lines:
            break
        for line in lines:
            do_something(line)

4 Réponses

+1 vote

Je pense que tu as besoin de faire de la programmation parallèle pour ton cas, via le module multiprocessing par exemple.

Edit :

ma question porte d'avantage sur l'amélioration de la vitesse de
lecture de chaque fichier individuellement.

post SO traitant de la problématique :

There should be one -- and preferably only one -- obvious way to do
it.

Code opti :

with open(...) as f:
    for line in f:
        <do something with line>

Explications :

The with statement handles opening and closing the file, including if
an exception is raised in the inner block. The for line in f treats
the file object f as an iterable, which automatically uses buffered IO
and memory management so you don't have to worry about large files.

répondu 5-Jun-2015 par boblinux (3,092 points)
edité 6-Jun-2015 par boblinux

J'imagine que tu entends par la le fait de traiter simultanément plusieurs fichiers. C'est une possibilité que j'avais envisagé effectivement, mais ma question porte d'avantage sur l'amélioration de la vitesse de lecture de chaque fichier individuellement.

ça change tout :)
j'ai édité la réponse

J'utilise déjà la clause with pour la lecture du fichier (commentaire du 1er post), l'idée de Nsukami_ est peut être à étudier du coup.

Oki, prochaine fois pense à poster un bout de code dans ta question initiale afin qu'on puisse voir ce que tu as déjà fait ou pas, au moins ça évitera les répétitions =D

+1 vote

Quand tu dis "lecture du fichier", c'est bien lecture du fichier + traitement ?

Si c'est uniquement la lecture, tu ne vas pas pouvoir faire beaucoup mieux que ta solution actuelle avec un contexte manager + traitement.

Si c'est lecture plus traitement (la totalité de ta fonction read_file() en fait), je tenterais de porter le code sous cython.
La transition se fait vraiment bien et tu peux gagner en perfs sur les nombreuses itérations.

Si tu peux utiliser des lib tiereces, la lib pandas peu répondre a ton besoin pour le traitement de la volumétrie.

Avec un peu de chance, ca peut se régler en onliner du style :

# avec df la dataframe construite a partir de ton fichier
df.groupby(df[col1] - df[col2]).mean()

Je n'ai pas beaucoup utilisé pandas donc ce code est probablement faux :) , mais tu trouveras de bons exemples ici

répondu 7-Jun-2015 par jc (2,674 points)

Ok du coup ça revient un peu à ce que j'imaginais quand j'ai posté ma question. Je n'avais pas pensé à panda mais je me demande si on gagne vraiment en perf en passant par là. Par contre pour Cython je me pose aussi une autre question, est-ce que ce procédé augmente les performance de lecture au sens strict ou alors juste dans le traitement des données ?

Si la lecture est plus rapide avec cython le temps gagné sera négligeable. Ce qui prend du temps ici c'est ton traitement.
L'avantage de Pandas c'est que c'est fait pour traiter de la volumétrie.

Si tu veux/peux, teste les différentes solutions avec un petit benchmark et poste ici les résultats.

0 votes

J'ai l'impression que tes données sont sous forme de fichier .csv ou équivalent. Pour charger (et je dis bien charger) ce type de fichier, la lib pandas est la plus rapide, avec la fonction pandas.read_csv. Pandas a beaucoup optimisé la lecture de ces fichiers car les statisticiens en recontrent souvent, et des gros.
Je ne suis pas sûr que tu arrives aux même perfs avec cython (dans un temps raisonnable), car là le problème est plutôt une gestion d'accès disque.

L'avantage, c'est que le traitement aussi est rapide, que tu le fasses avec pandas directement ou que tu utilises numpy (les dataframes pandas sont facilement convertibles en tableaux numpy). cf par exemple la réponse de @jc.

répondu 2-Mai-2016 par Kontre (128 points)

J'ai pas mal parcouru la question depuis et dans le cadre strict du topic, c'est aussi quelque chose à laquelle j'ai pensé. Cependant dans la pratique je n'ai pas modifié grand chose, j'ai gardé la boucle for classique avec le contexte manager, étant donné que je peux traiter les données au fur et à mesure, ce qui me permet d'économiser de la mémoire.

0 votes

Si les fichiers sont gros et la mémoire limité, il existe un certains nombre de libs spécialisés permettant de récupérer des data et de les traiter par "chunk" (aka par morceau).
Par exemple, l'écosystème Blaze pourra sans doute faire le taff:
Blaze fourni un proxy au fichier de donnée, et Dask permet de travailler sur les données par morceau (afin d'éviter de saturer la mémoire).

Si la solution te semble overkill, la lib pandas permet de lire des fichiers textes morceau par morceau (voir http://pandas.pydata.org/pandas-docs/stable/io.html#io-chunking).

Il te sera donc possible de faire ton traitement de façon beaucoup plus efficace.

répondu 4-Mai-2016 par Poisson (272 points)
...