Python – Как работать с большими CSV-файлами
Автор: Игорь Parsemachine
Опубликовано: 18 декабря в 20:24
Всем привет!
Сегодня мы рассмотрим возможность чтения CSV-файла, размер которого больше свободного места оперативной памяти (ОЗУ). Как это можно сделать? Один из вариантов – использовать библиотеку dask. Также рассмотрим формат работы с табличными данными .parquet. Сравним производительность в случае CSV и Parquet для одной операции на одних данных.
Подробнее о библиотеках, используемых в данной статье:
1. pandas – документация.
2. dask – официальный сайт.
Смотрите также уроки по разработке парсера на Python:
1. Разработка парсера каталога товаров в файл JSON – видео
2. Сохранение результата в Excel-таблицу – видео
3. Отправка файлов с результатами парсинга в чат Telegram – видео
4. Создание Telegram-бота для получения файлов с результатами парсинга – видео
5. Разработка парсера на Scrapy/Python – видео
Шаг 1. Подготовка
Установим интересующие нас библиотеки командой:
pip install numpy pandas "dask[dataframe]" pyarrow
Создадим скрипт main.py и импортируем в нем библиотеки.
Шаг 2. Другие варианты
Прежде, чем перейти к работе с pandas и dask, зададимся вопросом:
действительно ли использование Python для обработки CSV-файла оптимально в рамках данной задачи? Или стоит использовать другие более подходящие инструменты, например, реляционные СУБД. Создать базу данных в PostgreSQL и работать с ней. Если нужно обработать именно в Python, продолжаем.
Учитывайте также, что в Python можно прочитать построчно большие файлы с помощью функции open стандартной библиотеки Python, использовав генератор:
with open('filename.csv') as f:
for line in f:
# обработка очередной строки
process(line)
В таком случае у вас каждая очередная строка выгружается в ОЗУ, в отличии от ситуации, когда вы вызываете метод read():
with open('filename.csv') as f:
lines = f.read().splitlines()
for line in lines:
# обработка очередной строки
process(line)
В таком случае у вас произойдет сначала попытка загрузить файл целиком в ОЗУ, а потом уже разбить его на строки. Соответственно, если памяти свободной будет недостаточно, то и программа корректно не запустится.
Шаг 3. Программирование
Первым делом сгенерируем CSV-файл больше размера оперативной памяти (на моем MacBook это 4 GB). В моем примере это 10 млн. строк, 10 столбцов случайных чисел в диапазоне [1; 1 млн.). Файл получился 7,78 GB:
df = pd.DataFrame(np.random.randint(1, 10 ** 6, size=(10 ** 8, 10)), columns=list('ABCDEFGHIJ'))
df.to_csv(filename)
Согласно документации pandas библиотека предоставляет структуры данных, позволяющие анализировать файлы в оперативной памяти. Для файлов, которые слишком большие и не помещаются в ОЗУ pandas использовать уже не так удобно. Рекомендуется посмотреть в сторону других библиотек, и одной из таких библиотек является dask, предназначенная для параллельных вычислений, обработку файлов размером больше доступной оперативной памяти.
С помощью pandas можно отлично работать с большими файлами (> ОЗУ), разбивая их на части, в случае, если требуется нулевая или минимальная координация этих самых частей. Указывается максимальный размер блока в количестве строк (мы укажем 1 млн. строк). Итерируем и обрабатываем их по очереди. Связываем результаты обработки каждой отдельной части сами.
for df in pd.read_csv(filename, chunksize=10 ** 6):
process(df)
В dask же можно считать файл в логически единый датафрейм и работать с ним, как с цельным объектом. Который все же де-факто разделен на части, каждая из которых является pandas.DataFrame. dask имеет идентичный pandas API. Но стоит учитывать, что не весь функционал pandas, не все функции есть в dask. Читаем созданный нами тестовый рандомный CSV-файл:
df = dd.read_csv(filename)
Чуть позже мы проведем некоторые операции над данным датафреймом, но пока познакомимся с форматом Parquet.
Parquet – бинарный столбцовый формат хранения данных, более производительный в операциях чтения-записи, чем CSV, например. Официальный сайт – https://parquet.apache.org
Конвертируем наш CSV-файл в Parquet и сравним выполнение одной и той же операции для одних и тех же данных в двух форматах.
df.to_parquet('100m_parquet')
Вычислим среднее значение по колонке B и выведем затраченное время в случае работы с CSV и с Parquet:
st = time.time()
df1 = dd.read_csv(filename)
mean_value = df1['B'].mean().compute()
print(mean_value)
print(time.time() - st)
st = time.time()
df2 = dd.read_parquet('100m_parquet/part.*.parquet')
mean_value = df2['B'].mean().compute()
print(mean_value)
print(time.time() - st)

Скорость вычислений при использовании формата parquet на порядок выше – весьма значительно при работе с большим объемом данных.
Итоги
Сегодня мы рассмотрели варианты открытия и обработки больших CSV-файлом размером больше доступной оперативной памяти. И познакомились с форматом parquet, который является более быстрым для обработки.
На этом все, спасибо за внимание!
Если у вас возникли какие-то вопросы, можете задать их в комментариях к этой статье или под видео на YouTube. Предлагайте темы следующих уроков.
Спасибо за внимание!
0 комментариев
Для того, чтобы оставить комментарий, вам нужно быть зарегистрированным пользователем.
Введите свой e-mail для получения ссылки на регистрацию аккаунта ниже.