Nhập nhiều tệp csv vào gấu trúc và nối vào một DataFrame


403

Tôi muốn đọc một số tệp csv từ một thư mục thành gấu trúc và ghép chúng thành một DataFrame lớn. Tôi đã không thể tìm ra nó mặc dù. Đây là những gì tôi có cho đến nay:

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)

Tôi đoán tôi cần một số trợ giúp trong vòng lặp for ???


mã của bạn không làm gì vì bạn không phụ thêm để bạn dfsdanh sách, bạn không muốn thay thế dòng data = pd.read_csv(filename)với dfs.append(pd.read_csv(filename). Sau đó, bạn sẽ cần phải lặp qua danh sách và concat, tôi không nghĩ concatsẽ hoạt động trên danh sách dfs.
EdChum

ngoài ra, bạn đang trộn một bí danh cho mô-đun với tên mô-đun trong dòng cuối cùng của mình, phải không big_frame = pd.concat(dfs, ignore_index=True)?, dù sao khi bạn có một danh sách các tệp dữ liệu, bạn sẽ cần phải lặp lại danh sách và nối vớibig_frame
EdChum

Có, tôi đã chỉnh sửa mã, nhưng tôi vẫn không thể xây dựng một khung dữ liệu được nối từ các tệp csv, tôi mới sử dụng python nên tôi cần thêm trợ giúp về điều này
jonas

bạn cần phải lặp đi lặp lại dfs, vì vậy một cái gì đó như for df in dfs: big_frame.concat(df, ignore_index=True)nên hoạt động, bạn cũng có thể thử appendthay vì concatcũng có.
EdChum

Bạn có thể nói chính xác hơn những gì không hoạt động? Bởi vì concatnên xử lý một danh sách DataFrames tốt như bạn đã làm. Tôi nghĩ rằng đây là một cách tiếp cận rất tốt.
joris

Câu trả lời:


455

Nếu bạn có cùng một cột trong tất cả các csvtệp của mình thì bạn có thể thử mã bên dưới. Tôi đã thêm header=0để sau khi đọc csvhàng đầu tiên có thể được chỉ định làm tên cột.

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)

Điều này có vẻ giống như một cách làm thủ công hay còn gọi là thủ công, đặc biệt. vì hệ sinh thái Hapood có danh sách các công cụ ngày càng tăng, nơi bạn có thể thực hiện các truy vấn sql trực tiếp trên nhiều thư mục khác nhau chứa các loại tệp khác nhau (csv, json, txt, cơ sở dữ liệu) như thể đó là một nguồn dữ liệu. Phải có một cái gì đó tương tự ở trăn, vì nó đã có một bước nhảy vọt 20 năm khi thực hiện "dữ liệu lớn".
Hexatonic

275
Điều tương tự ngắn gọn hơn, và có lẽ nhanh hơn vì nó không sử dụng danh sách: df = pd.concat((pd.read_csv(f) for f in all_files)) Ngoài ra, có lẽ người ta nên sử dụng os.path.join(path, "*.csv")thay vì path + "/*.csv", điều đó làm cho hệ điều hành độc lập.
Sid

4
Sử dụng câu trả lời này cho phép tôi thêm cột mới với tên tệp, ví dụ như df['filename'] = os.path.basename(file_)trong vòng lặp for file_ .. không chắc câu trả lời của Sid có cho phép điều này không?
curtisp

4
@curtisp bạn vẫn có thể làm điều đó với câu trả lời của Sid, chỉ cần sử dụng pandas.read_csv(f).assign(filename = foo)bên trong trình tạo. assignsẽ trả về toàn bộ khung dữ liệu bao gồm cột mớifilename
C8H10N4O2

Nếu bạn có nhiều tệp, tôi sẽ sử dụng trình tạo thay vì nhập + nối thêm vào danh sách trước khi nối tất cả chúng.
gustafbstrom

289

Một thay thế cho câu trả lời của 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 hai dòng cuối cùng có thể được thay thế bằng : pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True). Dấu ngoặc trong được yêu cầu bởi Pandas phiên bản 0.18.1
Igor Fobia

6
Tôi khuyên bạn nên sử dụng glob.iglobthay vì glob.glob; Cái đầu tiên trả về và iterator (thay vì một danh sách) .
toto_tico

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

4
Tuyệt vời một lớp lót, đặc biệt hữu ích nếu không cần đối số read_csv!
rafaelvalle

15
Mặt khác, nếu cần lập luận, điều này có thể được thực hiện với lambdas:df = pd.concat(map(lambda file: pd.read_csv(file, delim_whitespace=True), data_files))
fiedl

^ hoặc với functools.partial, để tránh lambdas
cs95

34

Thư viện Dask có thể đọc một khung dữ liệu từ nhiều tệp:

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

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

Các cơ sở dữ liệu Dask triển khai một tập hợp con của API khung dữ liệu Pandas. Nếu tất cả dữ liệu vừa với bộ nhớ, bạn có thể gọidf.compute() để chuyển đổi khung dữ liệu thành khung dữ liệu Pandas.


30

Hầu như tất cả các câu trả lời ở đây đều phức tạp không cần thiết (khớp mẫu toàn cục) hoặc dựa vào các thư viện bên thứ 3 bổ sung. Bạn có thể làm điều này trong 2 dòng bằng cách sử dụng mọi thứ Pandas và python (tất cả các phiên bản) đã được tích hợp sẵn.

Đối với một vài tệp - 1 lớp lót:

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

Đối với nhiều tệp:

from os import listdir

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

Dòng gấu trúc này thiết lập df sử dụng 3 điều:

  1. Bản đồ của Python (hàm, iterable) gửi đến hàm (the pd.read_csv()) iterable (danh sách của chúng tôi) là mọi phần tử csv trong filepaths).
  2. Hàm read_csv () của Panda đọc trong mỗi tệp CSV như bình thường.
  3. Panda's concat () mang tất cả những thứ này dưới một biến df.

3
hoặc chỉdf = pd.concat(map(pd.read_csv, glob.glob('data/*.csv))
muon

Tôi đã thử phương pháp theo quy định của @muon. Nhưng, tôi có nhiều tệp với các tiêu đề (tiêu đề là phổ biến). Tôi không muốn chúng được nối vào khung dữ liệu. Bạn có biết làm thế nào tôi có thể làm điều đó? Tôi đã thử df = pd.concat(map(pd.read_csv(header=0), glob.glob('data/*.csv))nhưng nó đã báo lỗi "Parser_f () thiếu 1 đối số vị trí bắt buộc: 'filepath_or_buffer'"
cadip92 ngày

14

Chỉnh sửa: Tôi đã tìm đường vào https://stackoverflow.com/a/21232849/186078 . Tuy nhiên, tôi thấy muộn hơn khi thực hiện bất kỳ thao tác nào bằng cách sử dụng numpy và sau đó gán nó một lần cho dataframe thay vì tự thao tác với dataframe trên cơ sở lặp và nó dường như cũng hoạt động trong giải pháp này.

Tôi thực sự muốn bất kỳ ai nhấn trang này để xem xét phương pháp này, nhưng không muốn đính kèm đoạn mã lớn này như một bình luận và làm cho nó ít đọc hơn.

Bạn có thể tận dụng numpy để thực sự tăng tốc độ kết nối khung dữ liệu.

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

Thống kê thời gian:

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

Bất kỳ số nào để sao lưu "tăng tốc"? Cụ thể, nó có nhanh hơn stackoverflow.com/questions/20906474/ không?
ivan_pozdeev 17/03/2016

Tôi không thấy OP yêu cầu một cách để tăng tốc độ kết nối của anh ta, điều này trông giống như một bản sửa đổi của câu trả lời được chấp nhận từ trước.
pydsigner 17/03/2016

2
Điều đó sẽ không hoạt động nếu dữ liệu có các loại cột hỗn hợp.
Pimin Konstantin Kefaloukos

1
@SKG hoàn hảo .. đây là giải pháp làm việc duy nhất cho tôi. Tổng cộng 500 tệp 400k hàng trong 2 giây. Cảm ơn đã đăng nó.
FrankC

11

Nếu bạn muốn tìm kiếm đệ quy ( Python 3.5 trở lên ), bạn có thể làm như sau:

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)

Lưu ý rằng ba dòng cuối cùng có thể được thể hiện trong một dòng duy nhất :

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

Bạn có thể tìm thấy các tài liệu ** ở đây . Ngoài ra, tôi đã sử dụng iglobthay vì glob, vì nó trả về một iterator thay vì một danh sách.



EDIT: Hàm đệ quy đa nền tảng:

Bạn có thể gói phần trên thành một hàm đa nền tảng (Linux, Windows, Mac), vì vậy bạn có thể làm:

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

Đây là chức năng:

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

Dễ dàng và nhanh chóng

Nhập hai hoặc nhiều hơn csvmà không phải lập danh sách tên.

import glob

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

8

sử dụng một lớp lót map, nhưng nếu bạn muốn chỉ định các đối số bổ sung, bạn có thể làm:

import pandas as pd
import glob
import functools

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

Lưu ý: mapbởi chính nó không cho phép bạn cung cấp thêm đối số.


4

Nếu nhiều tệp csv được nén, bạn có thể sử dụng zipfile để đọc tất cả và nối như sau:

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

Một on-liner khác với khả năng hiểu danh sách cho phép sử dụng các đối số với read_csv.

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

3

Dựa trên câu trả lời hay của @ Sid.

Trước khi nối, bạn có thể tải các tệp csv vào một từ điển trung gian cho phép truy cập vào từng bộ dữ liệu dựa trên tên tệp (trong biểu mẫu dict_of_df['filename.csv']). Một từ điển như vậy có thể giúp bạn xác định các vấn đề với các định dạng dữ liệu không đồng nhất, ví dụ như khi các tên cột không được căn chỉnh.

Nhập mô-đun và định vị đường dẫn tệp:

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

Lưu ý: OrderedDictkhông cần thiết, nhưng nó sẽ giữ thứ tự các tệp có thể hữu ích để phân tích.

Tải tập tin csv vào từ điển. Sau đó nối lại:

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

Khóa là tên tệp fvà giá trị là nội dung khung dữ liệu của tệp csv. Thay vì sử dụng flàm khóa từ điển, bạn cũng có thể sử dụng os.path.basename(f)hoặc các phương thức os.path khác để giảm kích thước của khóa trong từ điển xuống chỉ còn phần nhỏ hơn có liên quan.


3

Thay thế bằng cách sử dụng pathlibthư viện (thường được ưa thích hơn os.path).

Phương pháp này tránh sử dụng lặp lại gấu trúc concat()/ apped().

Từ tài liệu về gấu trúc:
Điều đáng chú ý là concat () (và do đó append ()) tạo một bản sao đầy đủ của dữ liệu và việc liên tục sử dụng lại chức năng này có thể tạo ra một hiệu suất đáng kể. Nếu bạn cần sử dụng thao tác trên một số bộ dữ liệu, hãy sử dụng cách hiểu danh sách.

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

Đây là cách bạn có thể thực hiện bằng Colab trên 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.