Importa più file CSV in Panda e concatena in un DataFrame


404

Vorrei leggere diversi file CSV da una directory in Panda e concatenarli in un grande DataFrame. Non sono stato in grado di capirlo però. Ecco quello che ho finora:

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)

Immagino di aver bisogno di aiuto all'interno del ciclo for ???


il tuo codice non fa nulla perché non stai aggiungendo alla tua dfslista, non vuoi sostituire la linea data = pd.read_csv(filename)con dfs.append(pd.read_csv(filename). Dovresti quindi scorrere l'elenco e concat, non credo concatche funzionerà su un elenco di dfs.
EdChum,

inoltre stai mescolando un alias per il modulo con il nome del modulo nell'ultima riga, non dovrebbe essere big_frame = pd.concat(dfs, ignore_index=True)?, comunque, una volta che hai un elenco di frame di dati, dovrai scorrere l'elenco e big_frame
concederti

Sì, ho modificato il codice, ma non sono ancora in grado di creare un frame di dati concatenato dai file CSV, sono nuovo su Python, quindi ho bisogno di ulteriore aiuto su questo
jonas,

è necessario eseguire il ciclo dfsora, quindi qualcosa come for df in dfs: big_frame.concat(df, ignore_index=True)dovrebbe funzionare, si potrebbe anche provare appendinvece che concatanche.
EdChum,

Puoi dire più esattamente cosa non funziona? Perché concatdovrebbe gestire un elenco di DataFrames proprio come hai fatto tu. Penso che questo sia un ottimo approccio.
joris,

Risposte:


457

Se hai le stesse colonne in tutti i tuoi csvfile, puoi provare il codice qui sotto. Ho aggiunto in header=0modo che dopo aver letto la csvprima riga possano essere assegnati come nomi di colonna.

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)

Sembra un vecchio modo di fare le cose in modo manuale, esp. poiché l'ecosistema Hapood ha un elenco crescente di strumenti in cui è possibile eseguire query sql direttamente su molte directory diverse contenenti diversi tipi di file (csv, json, txt, database) come se fosse una fonte di dati. Deve esserci qualcosa di simile in Python, dato che ha avuto un inizio di 20 anni nel fare "big data".
Esatonico,

275
La stessa cosa è più concisa e forse più veloce in quanto non utilizza un elenco: df = pd.concat((pd.read_csv(f) for f in all_files)) inoltre, si dovrebbe forse usare al os.path.join(path, "*.csv")posto di path + "/*.csv", il che lo rende indipendente dal sistema operativo.
Sid,

4
L'utilizzo di questa risposta mi ha permesso di aggiungere una nuova colonna con il nome del file, ad esempio con df['filename'] = os.path.basename(file_)nel ciclo for file_ .. non sei sicuro che la risposta di Sid lo consenta?
curtisp,

4
@curtisp puoi ancora farlo con la risposta di Sid, basta usare pandas.read_csv(f).assign(filename = foo)all'interno del generatore. assignrestituirà l'intero frame di dati inclusa la nuova colonnafilename
C8H10N4O2

Se hai molti file, userei un generatore invece di importare + aggiungendo a un elenco prima di concatenarli tutti.
gustafbstrom,

289

Un'alternativa alla risposta di 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

2
@ Mike @Sid le ultime due righe possono essere sostituiti da: pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True). Le parentesi interne sono richieste dalla versione 0.18.1 di Panda
Igor Fobia,

6
Consiglio di usare glob.iglobinvece di glob.glob; Il primo ritorna e iteratore (anziché un elenco) .
toto_tico,

54
import glob, os    
df = pd.concat(map(pd.read_csv, glob.glob(os.path.join('', "my_files*.csv"))))

4
Eccellente copertina, utile specialmente se non sono necessari argomenti read_csv!
rafaelvalle,

15
Se, d'altra parte, sono necessari argomenti, questo può essere fatto con df = pd.concat(map(lambda file: pd.read_csv(file, delim_whitespace=True), data_files))
lambdas

^ o con functools.partial, per evitare
lambdas


30

Quasi tutte le risposte qui sono o inutilmente complesse (corrispondenza del modello glob) o si basano su librerie di terze parti aggiuntive. Puoi farlo in 2 righe usando tutto ciò che Pandas e Python (tutte le versioni) hanno già incorporato.

Per alcuni file - 1 linea:

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

Per molti file:

from os import listdir

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

Questa linea di panda che imposta il df utilizza 3 cose:

  1. La mappa di Python (funzione, iterabile) invia alla funzione (il pd.read_csv()) iterabile (la nostra lista) che è ogni elemento CSV nei percorsi dei file).
  2. La funzione read_csv () di Panda legge normalmente in ogni file CSV.
  3. Panda's concat () porta tutti questi sotto una variabile df.

3
o solodf = pd.concat(map(pd.read_csv, glob.glob('data/*.csv))
muon

Ho provato il metodo prescritto da @muon. Ma ho più file con le intestazioni (le intestazioni sono comuni). Non voglio che vengano concatenati nel dataframe. Sai come posso farlo? Ho provato df = pd.concat(map(pd.read_csv(header=0), glob.glob('data/*.csv))ma ha dato un errore "parser_f () mancante 1 argomento posizionale richiesto: 'filepath_or_buffer'"
cadip92

14

Modifica: ho cercato su Google in https://stackoverflow.com/a/21232849/186078 . Comunque di recente sto trovando più velocemente qualsiasi manipolazione usando numpy e poi assegnandolo una volta al dataframe piuttosto che manipolare il dataframe stesso su base iterativa e sembra funzionare anche in questa soluzione.

Voglio sinceramente che qualcuno che colpisce questa pagina consideri questo approccio, ma non voglio allegare questo enorme pezzo di codice come commento e renderlo meno leggibile.

Puoi sfruttare l'intorpidimento per accelerare davvero la concatenazione dei frame di dati.

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"....]

Statistiche di cronometraggio:

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

Qualche numero per sostenere la "velocità"? In particolare, è più veloce di stackoverflow.com/questions/20906474/… ?
ivan_pozdeev,

Non vedo l'OP chiedere un modo per accelerare la sua concatenazione, questo sembra solo una rielaborazione di una risposta accettata preesistente.
pydsigner,

2
Ciò non funzionerà se i dati hanno tipi di colonne miste.
Pimin Konstantin Kefaloukos,

1
@SKG perfetto .. questa è l'unica soluzione funzionante per me. 500 file 400k righe in totale in 2 secondi. Grazie per averlo pubblicato.
FrankC

11

Se si desidera cercare ricorsivamente ( Python 3.5 o versioni successive ), è possibile effettuare le seguenti operazioni:

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)

Si noti che le ultime tre righe possono essere espresse in un'unica riga :

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

Puoi trovare la documentazione di ** qui . Inoltre, ho usato iglobinvece di glob, poiché restituisce un iteratore anziché un elenco.



EDIT: funzione ricorsiva multipiattaforma:

Puoi racchiudere quanto sopra in una funzione multipiattaforma (Linux, Windows, Mac), in modo da poter fare:

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

Ecco la funzione:

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)

11

Facile e veloce

Importa due o più csvsenza dover creare un elenco di nomi.

import glob

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

8

utilizzando un solo liner map, ma se desideri specificare argomenti aggiuntivi, puoi fare:

import pandas as pd
import glob
import functools

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

Nota: mapda solo non consente di fornire argomenti aggiuntivi.


4

Se i file csv multipli sono compressi, puoi usare zipfile per leggere tutto e concatenare come di seguito:

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

4

Un altro in linea con comprensione dell'elenco che consente di utilizzare argomenti con read_csv.

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

3

Basato sulla buona risposta di @ Sid.

Prima di concatenare, è possibile caricare i file CSV in un dizionario intermedio che consente di accedere a ciascun set di dati in base al nome del file (nel modulo dict_of_df['filename.csv'] ). Tale dizionario può aiutarti a identificare i problemi con formati di dati eterogenei, ad esempio quando i nomi delle colonne non sono allineati.

Importa i moduli e individua i percorsi dei file:

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

Nota: OrderedDict non è necessario, ma manterrà l'ordine dei file che potrebbero essere utili per l'analisi.

Carica i file CSV in un dizionario. Quindi concatenare:

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

Le chiavi sono nomi di file fe valori sono il contenuto del frame di dati dei file CSV. Invece di utilizzare fcome chiave del dizionario, è anche possibile utilizzare os.path.basename(f)o altri metodi os.path per ridurre la dimensione della chiave nel dizionario solo alla parte più piccola rilevante.


3

Alternativa usando la pathliblibreria (spesso preferita sopra os.path).

Questo metodo evita l'uso iterativo di panda concat()/ apped().

Dalla documentazione di Panda:
Vale la pena notare che concat () (e quindi append ()) crea una copia completa dei dati, e che il riutilizzo costante di questa funzione può creare un significativo aumento delle prestazioni. Se è necessario utilizzare l'operazione su più set di dati, utilizzare una comprensione dell'elenco.

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)

-2

Ecco come puoi utilizzare Colab su 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')

-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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.