Імпортуйте кілька файлів CSV в панди та об'єднайте в один 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)

Я думаю, мені потрібна допомога в циклі for?


ваш код нічого не робить, оскільки ви не додаєте до свого 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
присвоїти

Так, я відредагував код, але все ще не в змозі створити об'єднаний кадр даних з CSV-файлів, я новачок у python, тому мені потрібна ще допомога щодо цього
jonas

зараз вам потрібно перетворити цикл 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, бази даних), як ніби це одне джерело даних. У python має бути щось подібне, оскільки він мав 20-річний стрибок у виконанні "великих даних".
Гексатонічний

275
Те ж саме, більш стисле і, можливо, швидше, оскільки воно не використовує список: df = pd.concat((pd.read_csv(f) for f in all_files)) Також, можливо, його слід використовувати os.path.join(path, "*.csv")замість path + "/*.csv", що робить його ОС незалежною.
Сід

4
Використовуючи цю відповідь, я дозволив мені додати новий стовпець із назвою файлу, наприклад, з df['filename'] = os.path.basename(file_)в циклі for_ file_. Не знаю, чи дозволяє це відповідь?
curtisp

4
@curtisp ви все ще можете це зробити з відповіддю Сіда, просто використовуйте pandas.read_csv(f).assign(filename = foo)всередині генератора. assignповерне весь фрейм даних, включаючи новий стовпець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 @ Mike останні дві лінії можуть бути замінені: pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True). Внутрішні дужки потрібні версіями Pandas 0.18.1
Ігор Фобія

6
Рекомендую використовувати glob.iglobзамість glob.glob; Перший повертається та ітератор (замість списку) .
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))
п'ятниця

^ або з functools.partial, щоб уникнути лямбда
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 реалізують підмножину API фрейму даних Pandas. Якщо всі дані вписуються в пам'ять, ви можете зателефонувати,df.compute() щоб перетворити кадр даних у фрейм даних Pandas.


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 (функція, ітерабельна) надсилає до функції (the pd.read_csv() ) ітерабельний (наш список), який є кожним елементом csv у файлових шляхах).
  2. Panda's read_csv () читається у кожному файлі CSV як звичайне.
  3. Panda concat () приводить все це під одну df змінну.

3
або простоdf = pd.concat(map(pd.read_csv, glob.glob('data/*.csv))
мюон

Я спробував метод, призначений @muon. Але у мене є кілька файлів із заголовками (загальні заголовки). Я не хочу, щоб вони були об'єднані в рамки даних. Чи знаєте ви, як я можу це зробити? Я спробував, 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 . Однак пізно мені здається, що швидше робити будь-які маніпуляції за допомогою numpy, а потім призначати її один раз до фрейму даних, а не маніпулювати самим фреймом даних на ітераційній основі, і, здається, працює і в цьому рішенні.

Я щиро хочу, щоб хтось, хто потрапив на цю сторінку, розглядав цей підхід, але не хочу додавати цей величезний фрагмент коду як коментар і робити його менш читабельним.

Ви можете використати numpy, щоб дійсно пришвидшити об'єднання кадрів даних.

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

Я не бачу, щоб ОП просила спосіб прискорити його конкатенацію, це просто виглядає як переробка попередньо прийнятої відповіді.
підсигнер

2
Це не спрацює, якщо дані мають змішані типи стовпців.
Пімін Костянтин Кефалукос

1
@SKG perfect .. це єдине робоче рішення для мене. 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, оскільки він повертає ітератор замість списку.



EDIT: Мультиплатформна рекурсивна функція:

Ви можете перетворити вищезазначене на функцію мультиплатформи (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, але якщо ви хочете вказати додаткові аргументи, ви можете зробити:

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 Диску

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.