อิมพอร์ตไฟล์ csv หลายไฟล์ไปยัง pandas และต่อเข้ากับ DataFrame เดียว


403

ฉันต้องการอ่านไฟล์ csv หลายไฟล์จากไดเร็กตอรี่ไปเป็นนุ่นและเชื่อมต่อกันเป็น DataFrame ขนาดใหญ่ ฉันไม่สามารถเข้าใจมันได้ นี่คือสิ่งที่ฉันมีจนถึงตอนนี้:

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)

ฉันเดาว่าฉันต้องการความช่วยเหลือในการวนรอบ ???


รหัสของคุณไม่ทำอะไรเลยเพราะคุณไม่ได้ผนวกกับคุณdfsรายการคุณไม่ต้องการที่จะเปลี่ยนสายกับdata = pd.read_csv(filename) dfs.append(pd.read_csv(filename)จากนั้นคุณจะต้องห่วงกว่ารายการและconcatผมไม่คิดว่าconcatการทำงานจะอยู่ในรายชื่อของdfs
EdChum

นอกจากนี้คุณกำลังผสมนามแฝงสำหรับโมดูลที่มีชื่อโมดูลในบรรทัดสุดท้ายของคุณไม่ควรbig_frame = pd.concat(dfs, ignore_index=True)ใช่หรือไม่อย่างไรก็ตามเมื่อคุณมีรายการของไฟล์ข้อมูลคุณจะต้องวนซ้ำรายการและต่อไปbig_frame
EdChum

ใช่ฉันแก้ไขโค้ด แต่ฉันยังไม่สามารถสร้างดาต้าเฟรมที่ต่อกันจากไฟล์ csv ได้ฉันใหม่กับไพ ธ อนดังนั้นฉันต้องการความช่วยเหลือเพิ่มเติมเกี่ยวกับเรื่องนี้
janas

คุณจำเป็นต้องวนซ้ำในdfsขณะนี้ดังนั้นบางสิ่งเช่นfor df in dfs: big_frame.concat(df, ignore_index=True)ควรทำงานได้คุณสามารถลองappendแทนได้concatเช่นกัน
EdChum

คุณช่วยบอกได้มากกว่านี้ว่าอะไรที่ไม่ทำงาน? เพราะconcatควรจัดการรายการ DataFrames ได้ดีเหมือนที่คุณทำ ฉันคิดว่านี่เป็นวิธีการที่ดีมาก
joris

คำตอบ:


455

หากคุณมีคอลัมน์เดียวกันในcsvไฟล์ทั้งหมดของคุณคุณสามารถลองใช้รหัสด้านล่าง ฉันได้เพิ่มheader=0เพื่อให้หลังจากที่อ่านcsvแถวแรกสามารถกำหนดเป็นชื่อคอลัมน์ได้

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)

ดูเหมือนว่าวิธีการทำสิ่งต่าง ๆ ด้วยมือแบบเก่า ๆ เนื่องจากระบบนิเวศของ Hapood มีรายการเครื่องมือที่เพิ่มขึ้นซึ่งคุณสามารถทำการสืบค้น sql ได้โดยตรงในไดเรกทอรีต่าง ๆ ที่มีไฟล์ประเภทต่าง ๆ (csv, json, txt, ฐานข้อมูล) ราวกับว่ามันเป็นแหล่งข้อมูลเดียว จะต้องมีสิ่งที่คล้ายกันในไพ ธ อนเนื่องจากมันมีการกระโดด 20 ปีในการเริ่มทำ "ข้อมูลขนาดใหญ่"
Hexatonic

275
สิ่งเดียวกันกระชับมากขึ้นและเร็วขึ้นเนื่องจากไม่ได้ใช้รายการ: df = pd.concat((pd.read_csv(f) for f in all_files)) นอกจากนี้ควรใช้อย่างใดอย่างหนึ่งos.path.join(path, "*.csv")แทนpath + "/*.csv"ซึ่งทำให้ระบบปฏิบัติการเป็นอิสระ
Sid

4
การใช้คำตอบนี้ทำให้ฉันสามารถเพิ่มคอลัมน์ใหม่ด้วยชื่อไฟล์เช่นdf['filename'] = os.path.basename(file_)ในสำหรับ file_ loop .. ไม่แน่ใจว่าคำตอบของ Sid อนุญาตหรือไม่
curtisp

4
@curtisp คุณยังคงสามารถทำได้ด้วยคำตอบของ Sid เพียงใช้pandas.read_csv(f).assign(filename = foo)ในตัวสร้าง assignจะส่งคืน dataframe ทั้งหมดรวมถึงคอลัมน์ใหม่filename
C8H10N4O2

หากคุณมีไฟล์จำนวนมากฉันจะใช้ตัวสร้างแทนการนำเข้า + ต่อท้ายรายการก่อนที่จะต่อไฟล์ทั้งหมด
gustafbstrom

289

ทางเลือกอื่นสำหรับคำตอบของ 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
@ ไมค์ @Sid pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True)สุดท้ายทั้งสองสายจะถูกแทนที่โดย: วงเล็บด้านในจำเป็นต้องใช้กับ Pandas เวอร์ชั่น 0.18.1
Igor Fobia

6
ฉันแนะนำให้ใช้glob.iglobแทนglob.glob; คนแรกและผลตอบแทนiterator (แทนรายการ)
toto_tico

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

4
ซับในที่ยอดเยี่ยมมีประโยชน์เป็นพิเศษหากไม่ต้องการอาร์กิวเมนต์ read_csv
rafaelvalle

15
หากในทางกลับกันจำเป็นต้องมีการโต้แย้งสิ่งนี้สามารถทำได้ด้วยdf = pd.concat(map(lambda file: pd.read_csv(file, delim_whitespace=True), data_files))
ลูกแกะ

^ หรือ with functools.partial, เพื่อหลีกเลี่ยง lambdas
cs95

34

ไลบรารี Dask สามารถอ่านไฟล์ข้อมูลจากไฟล์หลายไฟล์:

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

(ที่มา: http://dask.pydata.org/en/latest/examples/dataframe-csv.html )

Dask dataframes ใช้ชุดย่อยของ Pandas dataframe API หากข้อมูลทั้งหมดมีขนาดพอดีกับหน่วยความจำคุณสามารถโทรdf.compute()เพื่อแปลงดาต้าเฟรมไปเป็นดาต้าดาต้าแพนด้า


30

คำตอบเกือบทั้งหมดที่นี่มีความซับซ้อนโดยไม่จำเป็น (การจับคู่รูปแบบกลม) หรือพึ่งพาไลบรารีบุคคลที่สามเพิ่มเติม คุณสามารถทำได้ 2 บรรทัดโดยใช้ทุกอย่างใน Pandas และ python (ทุกเวอร์ชั่น) มีอยู่แล้วภายใน

สำหรับไฟล์บางไฟล์ - 1 ไลเนอร์:

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

สำหรับไฟล์จำนวนมาก:

from os import listdir

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

บรรทัดแพนด้านี้ซึ่งตั้งค่า df ใช้ 3 สิ่ง:

  1. แผนที่ Python (ฟังก์ชั่น iterable)ส่งไปยังฟังก์ชั่น (the pd.read_csv() ) the iterable (รายการของเรา) ซึ่งเป็นองค์ประกอบ csv ทุกตัวใน filepaths
  2. read_csvของ Panda ()อ่านในไฟล์ CSV แต่ละไฟล์ตามปกติ
  3. concatของ Panda ()นำสิ่งเหล่านี้มารวมกันภายใต้ตัวแปร df เดียว

3
หรือเพียงแค่df = pd.concat(map(pd.read_csv, glob.glob('data/*.csv))
muon

ฉันลองวิธีที่กำหนดโดย @muon แต่ฉันมีหลายไฟล์ที่มีส่วนหัว (ส่วนหัวเป็นเรื่องธรรมดา) ฉันไม่ต้องการให้พวกเขาต่อกันใน dataframe คุณรู้ฉันจะทำอย่างไร ฉันพยายามdf = pd.concat(map(pd.read_csv(header=0), glob.glob('data/*.csv))แต่มันมีข้อผิดพลาด "parser_f () หายไป 1 ต้องการอาร์กิวเมนต์ตำแหน่ง: 'filepath_or_buffer'"
cadip92

14

แก้ไข: ฉันไปที่https://stackoverflow.com/a/21232849/186078 https://stackoverflow.com/a/21232849/186078อย่างไรก็ตามในช่วงปลายฉันพบว่าเร็วกว่าที่จะทำการจัดการใด ๆ โดยใช้ numpy แล้วกำหนดหนึ่งครั้งให้กับ dataframe แทนที่จะจัดการกับ dataframe ด้วยตัวเองซ้ำแล้วซ้ำอีกและดูเหมือนว่าจะทำงานในโซลูชันนี้เช่นกัน

ฉันต้องการให้ทุกคนกดปุ่มหน้านี้เพื่อพิจารณาวิธีการนี้ แต่ไม่ต้องการแนบโค้ดขนาดใหญ่นี้เป็นความคิดเห็นและทำให้อ่านไม่ได้

คุณสามารถใช้ประโยชน์จาก numpy เพื่อเพิ่มความเร็วในการต่อข้อมูล dataframe

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

สถิติเวลา:

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

มีหมายเลขใดบ้างที่สามารถสำรอง "ความเร็ว" โดยเฉพาะมันเร็วกว่าstackoverflow.com/questions/20906474/หรือไม่?
ivan_pozdeev

ฉันไม่เห็น OP ขอวิธีเร่งความเร็วการต่อข้อมูลของเขานี่ดูเหมือนว่าจะปรับปรุงคำตอบที่ได้รับการยอมรับล่วงหน้า
pydsigner

2
สิ่งนี้จะไม่ทำงานหากข้อมูลมีประเภทคอลัมน์ผสม
Pimin Konstantin Kefaloukos

1
@SKG สมบูรณ์แบบ .. นี่เป็นทางออกเดียวสำหรับฉัน 500 ไฟล์ 400k แถวรวมใน 2 วินาที ขอบคุณสำหรับการโพสต์
FrankC

11

หากคุณต้องการค้นหาแบบเรียกซ้ำ ( Python 3.5 ขึ้นไป ) คุณสามารถทำสิ่งต่อไปนี้:

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)

โปรดทราบว่าสามบรรทัดสุดท้ายสามารถแสดงในบรรทัดเดียว :

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

คุณสามารถค้นหาเอกสารของที่นี่** นอกจากนี้ฉันใช้iglobแทนglobเพราะมันส่งกลับตัววนซ้ำแทนรายการ



แก้ไข: ฟังก์ชันเรียกซ้ำแบบหลายแพลตฟอร์ม:

คุณสามารถใส่ฟังก์ชันข้างต้นลงในฟังก์ชั่นหลายแพลตฟอร์ม (Linux, Windows, Mac) ดังนั้นคุณสามารถทำได้:

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

นี่คือฟังก์ชั่น:

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

ง่ายและรวดเร็ว

นำเข้าสองรายการขึ้นไป csvรายการโดยไม่ต้องสร้างรายชื่อ

import glob

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

8

ซับหนึ่งใช้mapแต่ถ้าคุณต้องการระบุ args เพิ่มเติมคุณสามารถทำได้:

import pandas as pd
import glob
import functools

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

หมายเหตุ: mapด้วยตัวของมันเองไม่อนุญาตให้คุณใส่เนื้อหาเพิ่มเติม


4

หากมีการบีบอัดไฟล์ csv หลายไฟล์คุณอาจใช้ zipfile เพื่ออ่านทั้งหมดและเชื่อมต่อกันดังต่อไปนี้:

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

อีกหนึ่งซับในที่มีรายการความเข้าใจที่อนุญาตให้ใช้อาร์กิวเมนต์กับ read_csv

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

3

ตามคำตอบที่ดีของ @ Sid

ก่อนทำการต่อข้อมูลคุณสามารถโหลดไฟล์ csv ลงในพจนานุกรมระดับกลางซึ่งให้การเข้าถึงชุดข้อมูลแต่ละชุดตามชื่อไฟล์ (ในรูปแบบdict_of_df['filename.csv']) พจนานุกรมดังกล่าวสามารถช่วยคุณระบุปัญหาเกี่ยวกับรูปแบบข้อมูลที่ต่างกันได้เมื่อชื่อคอลัมน์ไม่ตรงกัน

นำเข้าโมดูลและค้นหาพา ธ ไฟล์:

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

หมายเหตุ: OrderedDictไม่จำเป็น แต่จะเก็บลำดับของไฟล์ที่อาจเป็นประโยชน์สำหรับการวิเคราะห์

โหลดไฟล์ csv ลงในพจนานุกรม จากนั้นต่อกัน:

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

คีย์คือชื่อไฟล์fและค่าต่าง ๆ คือเนื้อหาเฟรมข้อมูลของไฟล์ csv แทนที่จะใช้fเป็นคีย์พจนานุกรมคุณสามารถใช้os.path.basename(f)หรือวิธีos.pathอื่น ๆเพื่อลดขนาดของคีย์ในพจนานุกรมให้เหลือเพียงส่วนเล็ก ๆ ที่เกี่ยวข้อง


3

ทางเลือกที่ใช้pathlibห้องสมุด (มักจะชอบมากกว่าos.path )

วิธีนี้หลีกเลี่ยงการใช้แพนด้าซ้ำ ๆconcat()/apped() /

จากเอกสารของนุ่น:
เป็นที่น่าสังเกตว่า concat () (และต่อท้าย ()) สร้างสำเนาของข้อมูลอย่างสมบูรณ์และการใช้ฟังก์ชันนี้ซ้ำอย่างต่อเนื่องสามารถสร้างประสิทธิภาพที่สำคัญ หากคุณต้องการใช้การดำเนินการกับชุดข้อมูลหลายชุดให้ใช้รายการความเข้าใจ

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

นี่คือวิธีที่คุณสามารถใช้ Colab บน 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.