Как объединить несколько CSV файлов в один?
Есть несколько CSV файлов, которые я хочу объединить в один.
Изначально воспользовался ответом, который предлагает следующее:
import fileinput
import glob
file_list = glob.glob("*.csv")
with open('result.csv', 'w') as file:
input_lines = fileinput.input(file_list)
file.writelines(input_lines)
Проблема оказалась в том, что при объединении не добавляется перенос строки после очередного файла (т.е. первая строка второго файла оказывается совмещена с последней строкой первого файла). Кроме того, каждый CSV файл имеет заголовок (одинаковый для всех), который хорошо бы убрать у всех фалов, кроме первого.
Сейчас объединяю вручную:
with open(target_filename, 'w') as target_file:
file_counter = 0
for file_ in files:
line_counter = 0
for line in open(file_, 'r'):
if line_counter == 0 and file_counter > 0:
target_file.write('\r\n')
else:
target_file.write(line)
line_counter += 1
file_counter += 1
Каким образом сделать объединение эффективнее?
Ответы (3 шт):
Воспользуйтесь модулем Pandas:
import pandas as pd # pip install pandas
from pathlib import Path
data_dir = Path("/path/to/csv/files/")
df = pd.concat([pd.read_csv(f) for f in data_dir.glob("*.csv")], ignore_index=True)
df.to_csv("/path/to/result.csv", index=False)
PS данное решение подразумевает, что объединенный (склеенный) CSV файл умещается в RAM компьютера.
Вариант предусматривающий жесткие ограничения по памяти - читаем каждый CSV по частям (параметр: chunksize - число строк в каждой части) и дописываем по частям в результирующий файл:
import pandas as pd # pip install pandas
from pathlib import Path
def csv_merger(path, out_filename="res.csv", globmask="*.csv", chunksize=5000, **kwargs):
path = Path(path)
need_header = True
for f in path.glob(globmask):
for chunk in pd.read_csv(f, chunksize=chunksize, **kwargs):
chunk.to_csv(out_filename, index=False, header=need_header, mode="a")
need_header = False
Пример использования:
csv_merger("/data", "/tmp/res.csv", globmask="*.csv", chunksize=1000)
Отказ от ответственности: данный код не тестировался, поэтому возможны ошибки.
Вариант решения с использованием UNIX утилиты awk:
awk '(NR == 1) || (FNR > 1)' *.csv > /path/to/res.csv
PS данный вариант должен работать гораздо быстрее, т.к. не парсит данные и awk написан на C/C++