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 для получения ссылки на регистрацию аккаунта ниже.