Об обработке CSV файлов

О том, как читать и разбирать CSV-файлы различными методами (с помощью языков C#, Python, средствами MS Excel или MS SQL Server) написано и сказано достаточно, но нам хотелось бы поделиться личным опытом обработки больших объёмов в формате CSV.

Не так давно перед нами стояла задача обработки данных из двух автоматизированных систем, в виде 11-ти файлов средним размером около 3 ГБ (в нашем случае это было ~3000000 строк) и одного файла ~36 ГБ (~242000000 строк).

Суть обработки заключалась в внутреннем соединении файлов (inner join). Вариант с использованием SQL-сервера сразу отпал, так как загрузка таких объёмов занимает значительное время и высока вероятность ошибок, возникающих в процессе импорта. Использование датафреймов (DataFrame) библиотеки Pandas физически невозможно в обычном виде: объём оперативной памяти 16 ГБ, объём данных загружаемых в память 39 ГБ. Тут всё очевидно.

Изначально нами было принято решение в цикле по 11-ти файлам читать их построчно и читать по каждой строке в цикле большой файл:

with open(file) as f: for line in f: row = line.split(';') with open(bigfile) as bf: for bline in bf: brow = bline.split(';') if row[3] == brow[15]: <Тут некий код записи строки в файл, DataFrame - в контексте данной статьи это не важно>

В целом, вариант рабочий, но есть некоторые нюансы. Главная проблема такого подхода заключается во времени работы, ведь для реализации алгоритма должно пройти 11*3 000 000 * 242 000 000 итераций цикла, при этом в каждой итерации происходит выполнение функции разбиения текста (split). Так же, стоит отметить, что в каждой из 3 000 000 итераций происходит открытия файла в 242 000 000 строк. Обратный вариант однократного открытия файлов и чтения внутри него файлов по 3 000 000 строк также является неприемлемым для поставленной задачи. Решить вопрос с комфортным временем выполнения и сравнительно не сложным алгоритмом нам удалось при помощи библиотеки Pandas.

Сначала мы этот вариант «отбросили», но потом вспомнили, что функция read_csv имеет пару интересных и полезных параметров. Параметры эти nrows и skiprows, т.е. количество загружаемых и пропускаемых строк соответственно. Использование этих параметров позволило читать объёмный файл поблочно и «сталкивать» его с цельным файлом поменьше. Вот код такого решения:

for file in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]: crm = pd.read_csv(r'files\file'+str(file)+'.csv', sep = '|', encoding= 'utf-8-sig', header = None) try: row_count = 0 i = 0 j = 122 skip = 1 for i in range(j): kmp = pd.read_csv(r'files\bigfile.csv', sep = ';', nrows = 2000000, header = None, skiprows = skip, encoding= 'cp1251') row_count +=len(kmp) skip +=2000000 kmp = kmp.merge(crm, left_on = [2,7], right_on = [45,36]) kmp.to_csv(r'files\res'+str(file)+'_N.csv', sep ='|', append = True) print(row_count) except ValueError: print('Готово') continue

Таким образом нам удалось в приемлемые сроки получить необходимую выборку, не нагружая оперативную память. В процессе работы скрипта использование памяти колебалось от 35 до 54 процентов.

В заключение следует сказать, что сей метод далеко не панацея и подходит он далеко не во всех случаях, как, впрочем, и все алгоритмы на свете. Ведь любой инструмент нужно использовать с умом, чтобы он оправдал оджидания.

0
5 комментариев
Эдуард Шмелев

Попробуйте ImportExportDataSql, которая сможет загрузить большие csv файлы в Sql Server. Можно настроить поля, например, выбрать только ключевые поля и загрузить их в базу, потом сделать джойн в базе

Ответить
Развернуть ветку
Эдуард Шмелев

ImportExportDataSql загружает Csv файл в базу порциями, чтобы память не переполнялась. Данные в базу загружаются очень быстро, так как используется класс SqlBulkInsert

Ответить
Развернуть ветку
NTA
Автор

Спасибо!

Ответить
Развернуть ветку
Cody Dismal

Python сила 

Ответить
Развернуть ветку
NTA
Автор

Согласны!

Ответить
Развернуть ветку
2 комментария
Раскрывать всегда