Узнайте, как читать большие CSV-файлы, чтобы свести к минимуму использование памяти и время загрузки.
Большинство наборов данных, которые вы используете в реальном мире, обычно огромны, имеют гигабайты и содержат миллионы строк. В этой статье я расскажу о некоторых методах, которые вы можете использовать при работе с большими наборами данных CSV.
При работе с большими файлами CSV есть две основные проблемы:
- Объем памяти, используемый при загрузке больших файлов CSV.
- Количество времени, потраченное на загрузку больших CSV-файлов.
В идеале вы хотите свести к минимуму объем памяти вашего DataFrame, а также время его загрузки. В этой статье я расскажу вам об использовании примера набора данных.
Наш набор данных
В этой статье я буду использовать данные Японской торговой статистики, доступные по адресу https://www.kaggle.com/datasets/4e614ec846ab778f6a2ff166232d5a65f5e6786b4f5781690588bd2cccd71cb6?resource=download.
Тип лицензии: CC BY-SA 4.0
Этот набор данных содержит данные о торговле с 1988 по 2020 год. Он содержит более 100 миллионов строк, а файл CSV занимает колоссальные 4,5 ГБ. Таким образом, это идеальный набор данных для иллюстрации концепций, изложенных в этой статье.
Загрузка CSV-файла в Pandas DataFrame
Давайте сначала загрузим весь файл CSV с более чем 100 миллионами строк. Мне интересно посмотреть, сколько времени потребуется для загрузки DataFrame, а также его объем памяти:
import time import pandas as pd start = time.time() df = pd.read_csv("custom_1988_2020.csv") print(time.time() - start, ' seconds') display(df) display(df.info())
Вывод такой, как показано:
Общий объем памяти составляет колоссальные 6,8 ГБ (!), и мне потребовалось 30 секунд, чтобы загрузить его в Pandas DataFrame.
Для справки, я использую Mac Studio с 32 ГБ памяти.
Изучение столбцов
Давайте рассмотрим столбцы в кадре данных:
df.columns
Теперь вы должны понимать, что этот CSV-файл не имеет заголовка, и, следовательно, Pandas предположит, что первая строка в CSV-файле содержит заголовок:
Index(['198801', '1', '103', '100', '000000190', '0', '35843', '34353'], dtype='object')
Загрузка с заголовками
Поскольку у CSV-файла нет заголовка, по крайней мере, вы можете использовать параметр header
, чтобы сообщить Pandas, что в CSV-файле нет заголовка:
# loading with no headers specified df = pd.read_csv("custom_1988_2020.csv", header=None) display(df)
Панды теперь будут автоматически называть столбцы, начиная с 0, затем 1 и так далее.
Из описания набора данных на https://www.kaggle.com/datasets/4e614ec846ab778f6a2ff166232d5a65f5e6786b4f5781690588bd2cccd71cb6?resource=download мы знаем значение различных столбцов:
- гм (год + месяц)
- exp_imp (экспорт: 1, импорт: 2)
- hs9 (код СС)
- Таможня
- Страна
- Q1
- Q2(количество)
- Стоимость (в тысячах иен)
Давайте назовем столбцы, используя параметр names
:
df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value']) display(df)
DataFrame теперь имеет следующие имена столбцов — «YearMonth», «ExportImport», «HSCode», «Customs', 'Страна', 'Q1', 'Q2_Quantity', 'Значение'.
Загрузка определенных столбцов
Поскольку файл CSV такой большой, следующий вопрос, который вы хотите задать себе, — действительно ли вам нужны все столбцы? Чтобы загрузить определенные столбцы, вы можете использовать параметр usecols
, чтобы указать столбцы, которые вы хотите загрузить:
start = time.time() df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], usecols = ["YearMonth", "Value"]) print(time.time() - start, ' seconds') display(df) display(df.info())
Как вы можете видеть из приведенного выше вывода, объем памяти был уменьшен до 1,7 ГБ, а время, необходимое для ее загрузки, теперь уменьшено до 17 секунд.
Параметр usecols
также поддерживает индекс позиции столбца. Приведенное выше также можно переписать, используя номера столбцов — 0 и 7:
df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], usecols = [0, 7]) print(time.time() - start, ' seconds') display(df)
Обратите внимание, что вы не можете использовать -1 для указания последнего столбца, например:
usecols = [0, -1]) # -1 is not supported
Параметр usecols
также поддерживает лямбда-функцию. Например, если вы хотите получить все столбцы, кроме столбца Country, вы можете использовать следующее лямбда-выражение:
df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], usecols = lambda column: column not in ['Country']) display(df)
Столбец Страна теперь будет исключен из результатов.
Использование лямбда-функции в параметре usecols
позволяет делать некоторые интересные вещи, такие как загрузка столбцов, имя которых содержит «Q», например:
usecols = lambda column: "Q" in column
Или чья длина имени столбца превышает, скажем, семь символов:
usecols = lambda column: len(column) > 7
Загрузка первых n строк
Во многих случаях вам не нужны все строки во всем CSV-файле. Возможно, первых 100 строк будет достаточно. Для этого вы можете использовать параметр nrows
, чтобы указать первые n строк, которые вы хотите загрузить:
start = time.time() df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], nrows=100) print(time.time() - start, ' seconds') display(df[:15]) display(df.info())
Из приведенного выше результата вы видите, что теперь загрузка первых 100 строк занимает всего 0,1 секунды, а результирующий DataFrame занимает всего 6,4 КБ.
Пропуск строк
Также бывают случаи, когда вы можете захотеть пропустить определенные строки в файле CSV. Для этого используйте параметр skiprows
:
df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], skiprows=2, nrows=100 ) display(df[:15])
Результат выше показывает, что первые две строки CSV-файла пропущены:
Вы также можете пропустить определенные строки:
df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], skiprows=[0,2,4], nrows=100 ) display(df[:15])
Приведенный выше результат показывает, что строки 0, 2 и 4 были пропущены:
Вы также можете использовать объект range
, чтобы указать диапазон пропущенных строк:
df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], skiprows=range(5,10), nrows=100 ) display(df[:15])
Приведенный выше результат показывает, что строки с 5 по 9 были пропущены. Значение параметра skiprows
также можно записать с помощью лямбда-функции, например:
df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], skiprows=lambda x: 5 <= x < 10, nrows=100 )
Используя лямбда-функцию, вы можете пропустить все четные строки:
df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], skiprows=lambda x: x % 2 == 0, nrows=100 ) print(time.time() - start, ' seconds') display(df[:15])
Приведенный выше результат показывает, что все четные строки были пропущены:
Загрузка определенных строк
До этого момента вы научились загружать первые n строк, а также пропускать определенные строки в CSV-файле. Как насчет загрузки определенных строк в файле CSV? Нет параметра, позволяющего вам это сделать, но вы можете использовать параметр skiprows
, чтобы получить то, что вы хотите.
Используя лямбда-функцию в параметре skiprows
, вы можете указать, какие строки не пропускать (что по сути означает, какие строки вы хотите загрузить):
start = time.time() df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], skiprows=lambda x: x not in [1,3], nrows=100 ) print(time.time() - start, ' seconds') display(df[:15]) display(df.info())
Приведенный выше результат показывает, что строки с номерами 1 и 3 были сохранены:
Недостатком этого подхода является то, что необходимо сканировать весь CSV-файл, поэтому для загрузки только двух строк требуется 20 секунд.
Загрузка последних n строк
Последняя проблема, которую я хочу обсудить, — как загрузить последние n строк из CSV-файла. Хотя загрузить первые n строк несложно, загрузка последних n строк не так проста. Но вы можете использовать то, что вы узнали до этого момента, чтобы решить эту проблему.
Во-первых, подсчитайте, сколько строк в CSV-файле:
# read the last n rows start = time.time() row_count = sum(1 for l in open('custom_1988_2020.csv')) print(time.time() - start, ' seconds') row_count
Поскольку в файле CSV более 100 миллионов строк, подсчет количества строк занял около 10 секунд. Также помните, что для этого CSV-файла нет заголовка. Итак, 113607322 — это фактическое количество строк записей.
Затем, чтобы загрузить последние 20 строк, используйте параметр skiprows
и передайте ему лямбда-функцию, чтобы пропустить все строки, кроме последних 20:
# read the last n rows start = time.time() df = pd.read_csv("custom_1988_2020.csv", header=None, names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 'Country', 'Q1', 'Q2_Quantity', 'Value'], skiprows=lambda x: 0 <= x < row_count - 20, nrows=100) print(time.time() - start, ' seconds') display(df) display(df.info())
Результат показывает последние 20 строк, загруженных в Pandas DataFrame.
Как и в предыдущем разделе, недостатком является то, что весь файл CSV должен быть просканирован в процессе загрузки (отсюда 22 секунды для загрузки DataFrame).
Если вам нравится читать мои статьи и они помогли вашей карьере/учебе, рассмотрите возможность регистрации в качестве участника Medium. Это 5 долларов в месяц, и это дает вам неограниченный доступ ко всем статьям (включая мои) на Medium. Если вы зарегистрируетесь по следующей ссылке, я получу небольшую комиссию (без дополнительных затрат для вас). Ваша поддержка означает, что я смогу уделять больше времени написанию подобных статей.
Краткое содержание
В этой статье я рассмотрел множество методов загрузки Pandas DataFrame из CSV-файла. Часто нет необходимости загружать весь файл CSV в DataFrame. Вы не только экономите время, загружая только то, что вам нужно, но и экономите память, необходимую для хранения вашего DataFrame в памяти. В следующей статье я покажу вам методы уменьшения объема памяти вашего DataFrame. Следите за обновлениями!