Importez plusieurs fichiers csv dans des pandas et concaténez dans un DataFrame

404

Je voudrais lire plusieurs fichiers csv d'un répertoire dans pandas et les concaténer en un grand DataFrame. Je n'ai pas pu le comprendre cependant. Voici ce que j'ai jusqu'à présent:

import glob
import pandas as pd

# get data file names
path =r'C:\DRO\DCL_rawdata_files'
filenames = glob.glob(path + "/*.csv")

dfs = []
for filename in filenames:
    dfs.append(pd.read_csv(filename))

# Concatenate all data into one DataFrame
big_frame = pd.concat(dfs, ignore_index=True)

Je suppose que j'ai besoin d'aide dans la boucle for ???

Jonas
la source
votre code ne fait rien car vous ne l'ajoutez pas à votre dfsliste, ne voulez-vous pas remplacer la ligne data = pd.read_csv(filename)par dfs.append(pd.read_csv(filename). Vous auriez alors besoin de parcourir la liste et concat, je ne pense pas concat, fonctionnera sur une liste de dfs.
EdChum du
vous êtes également le mélange d' un alias pour le module avec le nom du module dans votre dernière ligne, devrait - il pas big_frame = pd.concat(dfs, ignore_index=True), de toute façon une fois que vous avez une liste de dataframes vous devrez parcourir la liste et concat à?big_frame
EdChum
Oui, j'ai édité le code, mais je ne suis toujours pas en mesure de construire une trame de données concaténée à partir des fichiers csv, je suis nouveau sur python donc j'ai besoin de plus d'aide à ce sujet
jonas
vous devez faire une boucle dfsmaintenant, donc quelque chose comme ça for df in dfs: big_frame.concat(df, ignore_index=True)devrait fonctionner, vous pouvez également essayer appendau lieu de concataussi.
EdChum du
Pouvez-vous dire plus exactement ce qui ne fonctionne pas? Parce que concatdevrait gérer une liste de DataFrames très bien comme vous l'avez fait. Je pense que c'est une très bonne approche.
joris

Réponses:

457

Si vous avez les mêmes colonnes dans tous vos csvfichiers, vous pouvez essayer le code ci-dessous. J'ai ajouté header=0afin qu'après avoir lu la csvpremière ligne puisse être assignée comme noms de colonne.

import pandas as pd
import glob

path = r'C:\DRO\DCL_rawdata_files' # use your path
all_files = glob.glob(path + "/*.csv")

li = []

for filename in all_files:
    df = pd.read_csv(filename, index_col=None, header=0)
    li.append(df)

frame = pd.concat(li, axis=0, ignore_index=True)
Gaurav Singh
la source
Cela semble être une manière manuelle à l'ancienne de faire les choses, en particulier. car l'écosystème Hapood a une liste croissante d'outils où vous pouvez effectuer des requêtes SQL directement sur de nombreux répertoires différents contenant différents types de fichiers (csv, json, txt, bases de données) comme s'il s'agissait d'une seule source de données. Il doit y avoir quelque chose de similaire en python, car il a eu un démarrage rapide de 20 ans pour faire du "big data".
Hexatonic
276
La même chose est plus concise, et peut-être plus rapide car elle n'utilise pas de liste: df = pd.concat((pd.read_csv(f) for f in all_files)) En outre, on devrait peut-être utiliser à la os.path.join(path, "*.csv")place de path + "/*.csv", ce qui le rend indépendant du système d'exploitation.
Sid
4
L'utilisation de cette réponse m'a permis d'ajouter une nouvelle colonne avec le nom de fichier, par exemple avec df['filename'] = os.path.basename(file_)dans la boucle for file_ .. vous ne savez pas si la réponse de Sid le permet?
Curtisp
4
@curtisp vous pouvez toujours le faire avec la réponse de Sid, utilisez simplement pandas.read_csv(f).assign(filename = foo)à l'intérieur du générateur. assignrenverra l'intégralité de la trame de données, y compris la nouvelle colonnefilename
C8H10N4O2
Si vous avez beaucoup de fichiers, j'utiliserais un générateur au lieu d'importer + d'ajouter à une liste avant de les concaténer tous.
gustafbstrom
289

Une alternative à la réponse de darindaCoder :

path = r'C:\DRO\DCL_rawdata_files'                     # use your path
all_files = glob.glob(os.path.join(path, "*.csv"))     # advisable to use os.path.join as this makes concatenation OS independent

df_from_each_file = (pd.read_csv(f) for f in all_files)
concatenated_df   = pd.concat(df_from_each_file, ignore_index=True)
# doesn't create a list, nor does it append to one
Sid
la source
2
Les deux @Sid @ Mike dernières lignes peuvent être remplacées par: pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True). Les supports intérieurs sont requis par la version 0.18.1 de Pandas
Igor Fobia
6
Je recommande d'utiliser glob.iglobau lieu de glob.glob; Le premier retourne et itérateur (au lieu d'une liste) .
toto_tico
54
import glob, os    
df = pd.concat(map(pd.read_csv, glob.glob(os.path.join('', "my_files*.csv"))))
Jose Antonio Martin H
la source
4
Excellent one liner, particulièrement utile si aucun argument read_csv n'est nécessaire!
rafaelvalle
15
Si, en revanche, des arguments sont nécessaires, cela peut être fait avec des lambdas:df = pd.concat(map(lambda file: pd.read_csv(file, delim_whitespace=True), data_files))
fiedl
^ ou avec functools.partial, pour éviter les lambdas
cs95
34

La bibliothèque Dask peut lire une trame de données à partir de plusieurs fichiers:

>>> import dask.dataframe as dd
>>> df = dd.read_csv('data*.csv')

(Source: http://dask.pydata.org/en/latest/examples/dataframe-csv.html )

Les trames de données Dask implémentent un sous-ensemble de l'API de trame de données Pandas. Si toutes les données tiennent en mémoire, vous pouvez appelerdf.compute() pour convertir la trame de données en trame de données Pandas.

Jouni K. Seppänen
la source
30

Presque toutes les réponses ici sont soit inutilement complexes (correspondance de motifs globaux) ou reposent sur des bibliothèques tierces supplémentaires. Vous pouvez le faire en 2 lignes en utilisant tout ce que Pandas et python (toutes les versions) ont déjà intégré.

Pour quelques fichiers - 1 doublure:

df = pd.concat(map(pd.read_csv, ['data/d1.csv', 'data/d2.csv','data/d3.csv']))

Pour de nombreux fichiers:

from os import listdir

filepaths = [f for f in listdir("./data") if f.endswith('.csv')]
df = pd.concat(map(pd.read_csv, filepaths))

Cette ligne de pandas qui définit le df utilise 3 choses:

  1. La carte de Python (fonction, itérable) envoie à la fonction (la pd.read_csv()) l'itérable (notre liste) qui est chaque élément csv dans les chemins de fichiers).
  2. La fonction read_csv () de Panda lit normalement chaque fichier CSV.
  3. Concat () de Panda rassemble tout cela sous une variable df.
robmsmt
la source
3
ou tout simplementdf = pd.concat(map(pd.read_csv, glob.glob('data/*.csv))
muon
J'ai essayé la méthode prescrite par @muon. Mais, j'ai plusieurs fichiers avec des en-têtes (les en-têtes sont courants). Je ne veux pas qu'ils soient concaténés dans la trame de données. Savez-vous comment faire ça? J'ai essayé df = pd.concat(map(pd.read_csv(header=0), glob.glob('data/*.csv))mais cela a donné une erreur "parser_f () manque 1 argument positionnel requis: 'filepath_or_buffer'"
cadip92
14

Edit: j'ai googlé mon chemin dans https://stackoverflow.com/a/21232849/186078 . Cependant, récemment, je trouve plus rapide de faire des manipulations à l'aide de numpy, puis de l'affecter une fois à la trame de données plutôt que de manipuler la trame de données elle-même sur une base itérative et cela semble fonctionner également dans cette solution.

Je souhaite sincèrement que quiconque accède à cette page considère cette approche, mais je ne veux pas joindre cet énorme morceau de code en tant que commentaire et le rendre moins lisible.

Vous pouvez utiliser numpy pour vraiment accélérer la concaténation de la trame de données.

import os
import glob
import pandas as pd
import numpy as np

path = "my_dir_full_path"
allFiles = glob.glob(os.path.join(path,"*.csv"))


np_array_list = []
for file_ in allFiles:
    df = pd.read_csv(file_,index_col=None, header=0)
    np_array_list.append(df.as_matrix())

comb_np_array = np.vstack(np_array_list)
big_frame = pd.DataFrame(comb_np_array)

big_frame.columns = ["col1","col2"....]

Statistiques de synchronisation:

total files :192
avg lines per file :8492
--approach 1 without numpy -- 8.248656988143921 seconds ---
total records old :1630571
--approach 2 with numpy -- 2.289292573928833 seconds ---
SKG
la source
Des chiffres pour soutenir la "vitesse"? Plus précisément, est-ce plus rapide que stackoverflow.com/questions/20906474/… ?
ivan_pozdeev
Je ne vois pas le PO demander un moyen d'accélérer sa concaténation, cela ressemble simplement à une refonte d'une réponse acceptée préexistante.
pydsigner
2
Cela ne fonctionnera pas si les données ont des types de colonnes mixtes.
Pimin Konstantin Kefaloukos
1
@SKG perfect .. c'est la seule solution de travail pour moi. 500 fichiers 400k lignes au total en 2 secondes. Merci de l'avoir posté.
FrankC
11

Si vous souhaitez effectuer une recherche récursive ( Python 3.5 ou supérieur ), vous pouvez effectuer les opérations suivantes:

from glob import iglob
import pandas as pd

path = r'C:\user\your\path\**\*.csv'

all_rec = iglob(path, recursive=True)     
dataframes = (pd.read_csv(f) for f in all_rec)
big_dataframe = pd.concat(dataframes, ignore_index=True)

Notez que les trois dernières lignes peuvent être exprimées en une seule ligne :

df = pd.concat((pd.read_csv(f) for f in iglob(path, recursive=True)), ignore_index=True)

Vous pouvez trouver la documentation ** ici . Aussi, j'ai utilisé à la iglobplace de glob, car il renvoie un itérateur au lieu d'une liste.



EDIT: fonction récursive multiplateforme:

Vous pouvez envelopper ce qui précède dans une fonction multiplateforme (Linux, Windows, Mac), vous pouvez donc faire:

df = read_df_rec('C:\user\your\path', *.csv)

Voici la fonction:

from glob import iglob
from os.path import join
import pandas as pd

def read_df_rec(path, fn_regex=r'*.csv'):
    return pd.concat((pd.read_csv(f) for f in iglob(
        join(path, '**', fn_regex), recursive=True)), ignore_index=True)
toto_tico
la source
11

Facile et rapide

Importez deux ou plus csvsans avoir à faire une liste de noms.

import glob

df = pd.concat(map(pd.read_csv, glob.glob('data/*.csv')))
MrFun
la source
8

un liner en utilisant map, mais si vous souhaitez spécifier des arguments supplémentaires, vous pouvez faire:

import pandas as pd
import glob
import functools

df = pd.concat(map(functools.partial(pd.read_csv, sep='|', compression=None), 
                    glob.glob("data/*.csv")))

Remarque: mapen soi, ne vous permet pas de fournir des arguments supplémentaires.

muon
la source
4

Si les multiples fichiers csv sont zippés, vous pouvez utiliser zipfile pour tout lire et concaténer comme ci-dessous:

import zipfile
import numpy as np
import pandas as pd

ziptrain = zipfile.ZipFile('yourpath/yourfile.zip')

train=[]

for f in range(0,len(ziptrain.namelist())):
    if (f == 0):
        train = pd.read_csv(ziptrain.open(ziptrain.namelist()[f]))
    else:
        my_df = pd.read_csv(ziptrain.open(ziptrain.namelist()[f]))
        train = (pd.DataFrame(np.concatenate((train,my_df),axis=0), 
                          columns=list(my_df.columns.values)))
Nim J
la source
4

Un autre sur-liner avec compréhension de liste qui permet d'utiliser des arguments avec read_csv.

df = pd.concat([pd.read_csv(f'dir/{f}') for f in os.listdir('dir') if f.endswith('.csv')])
mjspier
la source
3

Basé sur la bonne réponse de @ Sid.

Avant de concaténer, vous pouvez charger des fichiers csv dans un dictionnaire intermédiaire qui donne accès à chaque ensemble de données en fonction du nom de fichier (dans le formulaire dict_of_df['filename.csv']). Un tel dictionnaire peut vous aider à identifier les problèmes liés aux formats de données hétérogènes, lorsque les noms de colonne ne sont pas alignés par exemple.

Importez des modules et localisez les chemins de fichiers:

import os
import glob
import pandas
from collections import OrderedDict
path =r'C:\DRO\DCL_rawdata_files'
filenames = glob.glob(path + "/*.csv")

Remarque: OrderedDictn'est pas nécessaire, mais il conservera l'ordre des fichiers qui pourraient être utiles pour l'analyse.

Chargez des fichiers csv dans un dictionnaire. Concaténer ensuite:

dict_of_df = OrderedDict((f, pandas.read_csv(f)) for f in filenames)
pandas.concat(dict_of_df, sort=True)

Les clés sont des noms de fichiers fet les valeurs sont le contenu de la trame de données des fichiers csv. Au lieu de l'utiliser fcomme clé de dictionnaire, vous pouvez également utiliser os.path.basename(f)ou d'autres méthodes os.path pour réduire la taille de la clé dans le dictionnaire uniquement à la plus petite partie pertinente.

Paul Rougieux
la source
3

Alternative utilisant la pathlibbibliothèque (souvent préférée à os.path).

Cette méthode évite l'utilisation itérative des pandas concat()/ apped().

D'après la documentation de pandas:
il convient de noter que concat () (et donc append ()) fait une copie complète des données et que la réutilisation constante de cette fonction peut créer un impact significatif sur les performances. Si vous devez utiliser l'opération sur plusieurs jeux de données, utilisez une compréhension de liste.

import pandas as pd
from pathlib import Path

dir = Path("../relevant_directory")

df = (pd.read_csv(f) for f in dir.glob("*.csv"))
df = pd.concat(df)
Henrik
la source
-2

Voici comment vous pouvez utiliser Colab sur Google Drive

import pandas as pd
import glob

path = r'/content/drive/My Drive/data/actual/comments_only' # use your path
all_files = glob.glob(path + "/*.csv")

li = []

for filename in all_files:
    df = pd.read_csv(filename, index_col=None, header=0)
    li.append(df)

frame = pd.concat(li, axis=0, ignore_index=True,sort=True)
frame.to_csv('/content/drive/onefile.csv')
Shaina Raza
la source
-3
import pandas as pd
import glob

path = r'C:\DRO\DCL_rawdata_files' # use your path
file_path_list = glob.glob(path + "/*.csv")

file_iter = iter(file_path_list)

list_df_csv = []
list_df_csv.append(pd.read_csv(next(file_iter)))

for file in file_iter:
    lsit_df_csv.append(pd.read_csv(file, header=0))
df = pd.concat(lsit_df_csv, ignore_index=True)
YASH GUPTA
la source