Импортируйте несколько CSV-файлов в панды и объединяйте их в один DataFrame


404

Я хотел бы прочитать несколько 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-файлов, я новичок в python, поэтому мне нужна дополнительная помощь по этому вопросу
jonas

dfsТеперь вам нужно повторить цикл , чтобы что-то вроде for df in dfs: big_frame.concat(df, ignore_index=True)этого работало, вы также можете попробовать appendвместо него concat.
EdChum

Можете ли вы сказать более точно, что не работает? Потому что concatдолжен обрабатывать список DataFrames так же хорошо, как вы. Я думаю, что это очень хороший подход.
Йорис

Ответы:


457

Если у вас есть одинаковые столбцы во всех ваших 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-летнее начало работы с «большими данными».
Hexatonic

276
То же самое более лаконично и, возможно, быстрее, поскольку не использует список: 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!
Рафаэльваль

15
Если, с другой стороны, нужны аргументы, это можно сделать с помощью лямбды:df = pd.concat(map(lambda file: pd.read_csv(file, delim_whitespace=True), data_files))
fiedl

^ или с 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 (function, iterable) отправляет функции (the pd.read_csv()) iterable (наш список), который является каждым элементом csv в filepaths).
  2. Функция Panda read_csv () читает в каждом файле CSV как обычно.
  3. Concat () от Panda объединяет все это в одну переменную 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

Я не вижу, чтобы ФП попросил ускорить его объединение, это просто похоже на переделку ранее принятого принятого ответа.
pydsigner

2
Это не сработает, если данные имеют смешанные типы столбцов.
Пимин Константин Кефалукос

1
@SKG отлично .. это единственное рабочее решение для меня. 500 файлов 400 тыс. Строк всего за 2 секунды. Спасибо за публикацию.
Франк

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, но если вы хотите указать дополнительные аргументы, вы можете сделать:

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

Еще один on-liner со списком, который позволяет использовать аргументы с read_csv.

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

3

На основании хорошего ответа @ Сид.

Перед объединением вы можете загрузить 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().

Из документации Pandas:
Стоит отметить, что concat () (и, следовательно, append ()) создает полную копию данных и что постоянное повторное использование этой функции может привести к значительному снижению производительности. Если вам нужно использовать операцию над несколькими наборами данных, используйте понимание списка.

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.