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

Об обработке 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 процентов.

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

77
5 комментариев

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

1
Ответить

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

1
Ответить
Автор

Спасибо!

Ответить

Python сила 

1
Ответить
Автор

Согласны!

Ответить