Zaimportuj wiele plików csv do pand i połącz w jedną ramkę danych

403

Chciałbym odczytać kilka plików csv z katalogu do pand i połączyć je w jedną dużą ramkę danych. Nie byłem jednak w stanie tego rozgryźć. Oto co mam do tej pory:

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)

Chyba potrzebuję pomocy w pętli for ???

jonas
źródło
Twój kod nie robi nic, ponieważ nie jesteś dołączanie do swojej dfslisty, nie chcesz zamienić linię data = pd.read_csv(filename)z dfs.append(pd.read_csv(filename). Będziesz wtedy musiał przejrzeć listę i concat, nie sądzę, concatże zadziała na liście dfs.
EdChum
także w ostatnim wierszu big_frame = pd.concat(dfs, ignore_index=True)miksujesz alias modułu z nazwą modułu, prawda ? W każdym razie, gdy będziesz mieć listę ramek danych, będziesz musiał iterować listę i big_frame
połączyć się z nią
Tak, edytowałem kod, ale nadal nie jestem w stanie zbudować skonkatowanej ramki danych z plików csv, jestem nowy w Pythonie, więc potrzebuję dodatkowej pomocy w tej sprawie
jonas
musisz dfsteraz zapętlić , więc coś takiego for df in dfs: big_frame.concat(df, ignore_index=True)powinno działać, możesz także spróbować appendzamiast tego concat.
EdChum
Czy możesz dokładniej powiedzieć, co nie działa? Ponieważ concatpowinien obsługiwać listę DataFrames tak dobrze, jak ty. Myślę, że to bardzo dobre podejście.
joris

Odpowiedzi:

454

Jeśli masz te same kolumny we wszystkich swoich csvplikach, możesz wypróbować poniższy kod. Dodałem header=0, aby po przeczytaniu csvpierwszego wiersza można było przypisać nazwy kolumn.

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
źródło
To wydaje się być staromodnym aka ręcznym sposobem robienia rzeczy, szczególnie. ponieważ ekosystem Hapood ma coraz większą listę narzędzi, w których można wykonywać zapytania SQL bezpośrednio w wielu różnych katalogach zawierających różne typy plików (csv, json, txt, bazy danych), jakby to było jedno źródło danych. W Pythonie musi być coś podobnego, ponieważ od 20 lat zaczął robić „duże zbiory danych”.
Heksatoniczny
275
Ta sama rzecz bardziej zwięzła i być może szybsza, ponieważ nie używa listy: df = pd.concat((pd.read_csv(f) for f in all_files)) Należy również użyć os.path.join(path, "*.csv")zamiast niej path + "/*.csv", co czyni ją niezależną od systemu operacyjnego.
Sid
4
Użycie tej odpowiedzi pozwoliło mi dodać nową kolumnę z nazwą pliku, np. df['filename'] = os.path.basename(file_)W pętli for file_. Nie jesteś pewien, czy odpowiedź Sid na to pozwala?
Curtisp
4
@curtisp nadal możesz to zrobić z odpowiedzią Sida, po prostu użyj pandas.read_csv(f).assign(filename = foo)w generatorze. assignzwróci całą ramkę danych, w tym nową kolumnęfilename
C8H10N4O2
Jeśli masz wiele plików, użyłbym generatora zamiast importować + dołączanie do listy przed połączeniem ich wszystkich.
gustafbstrom
289

Alternatywa dla odpowiedzi 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
źródło
2
@Mike @Sid ostatnie dwie linie mogą być zastąpione przez: pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True). Wewnętrzne nawiasy klamrowe są wymagane przez Pandas w wersji 0.18.1
Igor Fobia
6
Polecam używać glob.iglobzamiast glob.glob; Pierwszy zwraca i iterator (zamiast listy) .
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.
źródło
4
Doskonały jeden linijka, szczególnie przydatny, jeśli nie są potrzebne argumenty read_csv!
rafaelvalle
15
Z drugiej strony, jeśli potrzebne są argumenty, można to zrobić za pomocą lambdas:df = pd.concat(map(lambda file: pd.read_csv(file, delim_whitespace=True), data_files))
fiedl
^ lub z functools.partial, aby uniknąć
lambdas
34

Biblioteka Dask może odczytać ramkę danych z wielu plików:

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

(Źródło: http://dask.pydata.org/en/latest/examples/dataframe-csv.html )

Ramki danych Dask implementują podzbiór interfejsu API ramki danych Pandas. Jeśli wszystkie dane mieszczą się w pamięci, możesz wywołaćdf.compute() konwersję ramki danych w ramkę danych Pandas.

Jouni K. Seppänen
źródło
30

Prawie wszystkie odpowiedzi tutaj są albo niepotrzebnie złożone (dopasowanie wzorca globalnego), albo polegają na dodatkowych bibliotekach stron trzecich. Możesz to zrobić w 2 liniach, używając wszystkiego, co Pandas i python (wszystkie wersje) już mają.

Dla kilku plików - 1 wkładka:

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

W przypadku wielu plików:

from os import listdir

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

Ta linia pand, która ustawia df, wykorzystuje 3 rzeczy:

  1. Mapa Pythona (funkcja, iterowalna) wysyła do funkcji ( pd.read_csv() iterowalnej) (nasza lista), która jest każdym elementem csv w ścieżkach plików).
  2. Panda's read_csv () odczytuje normalnie każdy plik CSV.
  3. Concat () Pandy umieszcza je pod jedną zmienną df.
robmsmt
źródło
3
lub po prostudf = pd.concat(map(pd.read_csv, glob.glob('data/*.csv))
mion
Próbowałem metody zalecanej przez @muon. Ale mam wiele plików z nagłówkami (nagłówki są wspólne). Nie chcę, aby były konkatenowane w ramce danych. Czy wiesz jak to zrobić? Próbowałem, df = pd.concat(map(pd.read_csv(header=0), glob.glob('data/*.csv))ale dał błąd „brak parser_f () 1 wymaganego argumentu pozycyjnego: 'filepath_or_buffer'”
cadip92
14

Edycja: przeszedłem do Google na https://stackoverflow.com/a/21232849/186078 . Jednak ostatnio uważam, że szybsze jest wykonywanie jakichkolwiek operacji przy użyciu numpy, a następnie przypisywanie go raz do ramki danych zamiast manipulowania samą ramką danych w sposób iteracyjny i wydaje się, że działa również w tym rozwiązaniu.

Szczerze chcę, aby ktokolwiek odwiedzający tę stronę rozważał to podejście, ale nie chcę dołączać tego ogromnego fragmentu kodu jako komentarza i uczynić go mniej czytelnym.

Możesz użyć numpy, aby naprawdę przyspieszyć konkatenację ramki danych.

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

Statystyki czasu:

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
źródło
Jakieś liczby, które wspierają „przyspieszenie”? W szczególności, czy jest szybszy niż stackoverflow.com/questions/20906474/... ?
ivan_pozdeev
Nie widzę OP proszącego o sposób przyspieszenia jego konkatenacji, to po prostu wygląda na przeróbkę wcześniej zaakceptowanej odpowiedzi.
pydsigner
2
To nie zadziała, jeśli dane mają mieszane typy kolumn.
Pimin Konstantin Kefaloukos
1
@SKG idealne .. to dla mnie jedyne działające rozwiązanie. 500 plików 400k wierszy łącznie w 2 sekundy. Dzięki za opublikowanie.
FrankC
11

Jeśli chcesz wyszukiwać rekurencyjnie ( Python 3.5 lub nowszy ), możesz wykonać następujące czynności:

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)

Zauważ, że trzy ostatnie linie mogą być wyrażone w jednym pojedynczym wierszu :

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

Dokumentację można znaleźć ** tutaj . Ponadto użyłem iglobzamiast glob, ponieważ zwraca iterator zamiast listy.



EDYCJA: Wieloplatformowa funkcja rekurencyjna:

Możesz zawinąć powyższe w funkcję wieloplatformową (Linux, Windows, Mac), dzięki czemu możesz:

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

Oto funkcja:

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
źródło
11

Łatwo i szybko

Zaimportuj dwa lub więcej csvbez konieczności tworzenia listy nazwisk.

import glob

df = pd.concat(map(pd.read_csv, glob.glob('data/*.csv')))
MrFun
źródło
8

używasz jednego linera map, ale jeśli chcesz podać dodatkowe argumenty, możesz:

import pandas as pd
import glob
import functools

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

Uwaga: mapsamo w sobie nie pozwala na dostarczenie dodatkowych argumentów.

mion
źródło
4

Jeśli wiele plików csv jest skompresowanych, możesz użyć pliku zip do odczytu wszystkich i połączenia w następujący sposób:

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.
źródło
4

Kolejny on-lineer ze zrozumieniem listy, który pozwala na użycie argumentów z read_csv.

df = pd.concat([pd.read_csv(f'dir/{f}') for f in os.listdir('dir') if f.endswith('.csv')])
mjspier
źródło
3

Na podstawie dobrej odpowiedzi @ Sid.

Przed konkatenacją możesz załadować pliki csv do słownika pośredniego, który daje dostęp do każdego zestawu danych na podstawie nazwy pliku (w formularzu dict_of_df['filename.csv']). Taki słownik może pomóc zidentyfikować problemy z heterogenicznymi formatami danych, gdy na przykład nazwy kolumn nie są wyrównane.

Zaimportuj moduły i zlokalizuj ścieżki plików:

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

Uwaga: OrderedDictnie jest konieczne, ale zachowa kolejność plików, które mogą być przydatne do analizy.

Załaduj pliki csv do słownika. Następnie połącz:

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

Klucze to nazwy plików, fa wartości to zawartość ramki danych plików csv. Zamiast używać fjako klucza słownika, możesz także użyć os.path.basename(f)lub innych metod os.path , aby zmniejszyć rozmiar klucza w słowniku tylko do odpowiedniej, mniejszej części.

Paul Rougieux
źródło
3

Alternatywne użycie pathlibbiblioteki (często preferowane os.path).

Ta metoda pozwala uniknąć iteracyjnego używania pand concat()/ apped().

Z dokumentacji pand:
Warto zauważyć, że concat () (a zatem append ()) tworzy pełną kopię danych, a ciągłe ponowne użycie tej funkcji może spowodować znaczny spadek wydajności. Jeśli chcesz użyć operacji na kilku zestawach danych, skorzystaj ze zrozumienia listy.

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
źródło
-2

W ten sposób możesz zrobić, używając Colab na Dysku Google

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
źródło
-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
źródło