web-dev-qa-db-fra.com

Concaténation de plusieurs fichiers csv en un seul csv avec le même en-tête - Python

J'utilise actuellement le code ci-dessous pour importer 6 000 fichiers csv (avec en-têtes) et les exporter dans un seul fichier csv (avec une seule ligne d'en-tête).

#import csv files from folder
path =r'data/US/market/merged_data'
allFiles = glob.glob(path + "/*.csv")
stockstats_data = pd.DataFrame()
list_ = []

for file_ in allFiles:
    df = pd.read_csv(file_,index_col=None,)
    list_.append(df)
    stockstats_data = pd.concat(list_)
    print(file_ + " has been imported.")

Ce code fonctionne bien, mais il est lent. Le traitement peut prendre jusqu'à 2 jours.

On m'a donné un script de ligne unique pour la ligne de commande du terminal qui fait la même chose (mais sans en-têtes). Ce script prend 20 secondes.

 for f in *.csv; do cat "`pwd`/$f" | tail -n +2 >> merged.csv; done 

Est-ce que quelqu'un sait comment accélérer le premier script Python? Pour réduire le temps, j'ai pensé à ne pas l'importer dans un DataFrame et à concaténer simplement les CSV, mais je ne peux pas le comprendre .

Merci.

12
mattblack

Si vous n'avez pas besoin du CSV en mémoire, il suffit de copier de l'entrée vers la sortie, ce sera beaucoup moins cher pour éviter l'analyse du tout, et copier sans construire en mémoire:

import shutil

#import csv files from folder
path = r'data/US/market/merged_data'
allFiles = glob.glob(path + "/*.csv")
with open('someoutputfile.csv', 'wb') as outfile:
    for i, fname in enumerate(allFiles):
        with open(fname, 'rb') as infile:
            if i != 0:
                infile.readline()  # Throw away header on all but first file
            # Block copy rest of file from input to output without parsing
            shutil.copyfileobj(infile, outfile)
            print(fname + " has been imported.")

C'est ça; shutil.copyfileobj gère efficacement la copie des données, réduisant considérablement le travail de niveau Python pour analyser et resérialiser).

Cela suppose que tous les fichiers CSV ont le même format, codage, fins de ligne, etc., et l'en-tête ne contient pas de nouvelles lignes incorporées, mais si c'est le cas, c'est beaucoup plus rapide que les alternatives.

14
ShadowRanger

Devez-vous le faire en Python? Si vous êtes prêt à le faire entièrement dans Shell, tout ce que vous devez faire est d'abord cat la ligne d'en-tête d'un fichier .csv d'entrée sélectionné au hasard dans merged.csv avant d'exécuter votre one-liner:

cat a-randomly-selected-csv-file.csv | head -n1 > merged.csv
for f in *.csv; do cat "`pwd`/$f" | tail -n +2 >> merged.csv; done 
6
Peter Leimbigler

Vous n'avez pas besoin de pandas pour cela, juste le simple module csv fonctionnerait bien.

import csv

df_out_filename = 'df_out.csv'
write_headers = True
with open(df_out_filename, 'wb') as fout:
    writer = csv.writer(fout)
    for filename in allFiles:
        with open(filename) as fin:
            reader = csv.reader(fin)
            headers = reader.next()
            if write_headers:
                write_headers = False  # Only write headers once.
                writer.writerow(headers)
            writer.writerows(reader)  # Write all remaining rows.
1
Alexander

Voici une approche plus simple - vous pouvez utiliser pandas (bien que je ne sais pas comment cela aidera avec RAM utilisation) -

import pandas as pd
import glob

path =r'data/US/market/merged_data'
allFiles = glob.glob(path + "/*.csv")
stockstats_data = pd.DataFrame()
list_ = []

for file_ in allFiles:
    df = pd.read_csv(file_)
    stockstats_data = pd.concat((df, stockstats_data), axis=0)
0
markroxor