Pandas & NumPy Academy · Урок

Потоковое чтение CSV с chunksize

Читайте большой CSV частями фиксированного размера с помощью pd.read_csv(chunksize=), обрабатывайте каждую часть и объединяйте или накапливайте результаты.

Урок 1 из 413 шагов

«Потоковое чтение CSV с chunksize» — бесплатный урок Pandas & NumPy Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Pandas & NumPy Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Pandas & NumPy Academy содержит 4 уроков всего.

Проблема больших CSV-файлов

Если CSV-файл больше доступного объёма RAM — например, файл журнала размером 50 GB на компьютере с 16 GB памяти, — вызов pd.read_csv('file.csv') завершится ошибкой MemoryError или заставит систему интенсивно использовать подкачку, из-за чего работа станет неприемлемо медленной. Решение — чтение частями: вместо загрузки всего файла сразу обрабатывайте его фрагментами фиксированного размера, накапливая результаты и никогда не удерживая всё содержимое в памяти одновременно.

Параметр chunksize в read_csv

Передача chunksize=N в pd.read_csv() возвращает итератор TextFileReader, а не DataFrame. На каждой итерации возвращается DataFrame, содержащий не более N строк. Файл читается лениво: данные не загружаются, пока Вы не запросите следующий фрагмент. Этот итератор можно использовать в цикле for или передать в pd.concat(). Выбирайте chunksize достаточно большим для эффективного ввода-вывода (например, 10 000–100 000 строк), но достаточно малым, чтобы он без проблем помещался в памяти.

import pandas as pd

# Returns a TextFileReader iterator, NOT a DataFrame
chunks = pd.read_csv('sales_data.csv', chunksize=10000)
print(type(chunks))  # <class 'pandas.io.parsers.readers.TextFileReader'>

for chunk in chunks:
    print(f'Chunk shape: {chunk.shape}')
    # process each chunk independently
    break   # just show the first chunk here

Независимая обработка каждого фрагмента

Наиболее распространённый подход — выполнить преобразование или фильтрацию каждого фрагмента, собрать результаты в список, а затем объединить их. Например, можно отфильтровать строки, соответствующие условию, вычислить статистику для каждого фрагмента или выбрать только нужные столбцы. Работа с подмножествами означает, что память занимает только текущий фрагмент, а остальная часть файла не затрагивается. После завершения цикла один вызов pd.concat(results) собирает итоговый DataFrame.

import pandas as pd

results = []
for chunk in pd.read_csv('orders.csv', chunksize=50000):
    # Keep only high-value orders
    filtered = chunk[chunk['amount'] > 1000]
    results.append(filtered)

# Combine all filtered chunks
high_value = pd.concat(results, ignore_index=True)
print('High-value orders:', len(high_value))

Накопление агрегатов по фрагментам

Иногда Вам не нужно сохранять вообще ни одной строки — достаточно поддерживать накопительный агрегат. Отслеживайте накопительную сумму, количество или минимум/максимум по фрагментам, не создавая список из DataFrames. Это наиболее эффективный с точки зрения памяти шаблон, поскольку объём используемой памяти остаётся постоянным независимо от размера файла. В конце вычислите итоговую статистику по накопленным значениям.

import pandas as pd

total_revenue = 0.0
total_rows = 0

for chunk in pd.read_csv('sales.csv', chunksize=100000):
    total_revenue += chunk['revenue'].sum()
    total_rows += len(chunk)

print(f'Processed {total_rows:,} rows')
print(f'Total revenue: ${total_revenue:,.2f}')

Указание dtype для ускорения чтения по фрагментам

По умолчанию Pandas определяет типы данных столбцов по данным, для чего требуется дважды просканировать каждый фрагмент: один раз для определения типа и второй — для разбора. Передача аргумента dtype устраняет эти лишние операции и предотвращает несогласованность типов между фрагментами. Например, столбец, содержащий в основном целые числа, но одну пустую ячейку, в одном фрагменте может быть определён как float64, а в другом — как object. Явное указание типов обеспечивает согласованное и более быстрое чтение всех фрагментов.

import pandas as pd

dtype_map = {
    'order_id': 'int32',
    'customer_id': 'int32',
    'amount': 'float32',
    'category': 'category'
}

for chunk in pd.read_csv('orders.csv',
                         chunksize=50000,
                         dtype=dtype_map,
                         parse_dates=['order_date']):
    print(chunk.dtypes)
    break

Выбор только необходимых столбцов

Используйте параметр usecols, чтобы загружать только столбцы, необходимые для анализа. Если CSV содержит 50 столбцов, а в агрегации используются только 3, нет смысла разбирать остальные 47. Сочетание usecols с chunksize значительно сокращает и время ввода-вывода, и объём используемой памяти. Это один из самых простых и эффективных способов оптимизировать обработку больших CSV-файлов.

import pandas as pd

# Only read the three columns we actually need
for chunk in pd.read_csv(
    'large_transactions.csv',
    chunksize=100000,
    usecols=['date', 'amount', 'region']
):
    print(chunk.columns.tolist())
    print(chunk.memory_usage(deep=True).sum() / 1e6, 'MB per chunk')
    break

Агрегация GroupBy по фрагментам

Для выполнения агрегации groupby по фрагментам необходимо накапливать промежуточные результаты. Выполните groupby внутри каждого фрагмента, а затем объедините результаты с помощью второго groupby над объединёнными промежуточными данными. Например, чтобы получить общие продажи по регионам в файле размером 10 GB, соберите суммы по регионам для каждого фрагмента в список, затем объедините их и снова выполните группировку. Такой двухпроходный шаблон агрегации иногда называют подходом map-reduce.

import pandas as pd

partials = []
for chunk in pd.read_csv('sales.csv',
                         chunksize=100000,
                         usecols=['region', 'revenue']):
    partial = chunk.groupby('region')['revenue'].sum()
    partials.append(partial)

# Combine partial sums
final = pd.concat(partials).groupby(level=0).sum()
print('Revenue by region:')
print(final.sort_values(ascending=False))

Обработка ошибок разбора по фрагментам

Большие CSV-файлы из внешних источников часто содержат некорректные строки — лишние запятые, неверную кодировку или усечённые строки. Используйте on_bad_lines='skip' (Pandas 1.3+) или error_bad_lines=False (в старых версиях Pandas), чтобы без уведомлений пропускать некорректные строки, а если разбор в UTF-8 завершается ошибкой, укажите encoding='latin-1'. Отслеживайте, при обработке каких фрагментов возникли ошибки, обернув обработку каждого фрагмента в конструкцию try-except. Так Вы построите надёжный конвейер, который не прервётся из-за одной некорректной строки в файле из 10 миллионов строк.

import pandas as pd

bad_chunks = []
all_chunks = []

for i, chunk in enumerate(pd.read_csv(
    'raw_data.csv',
    chunksize=50000,
    on_bad_lines='skip',
    encoding='utf-8',
    encoding_errors='replace'
)):
    try:
        # Your transformation here
        all_chunks.append(chunk)
    except Exception as e:
        bad_chunks.append((i, str(e)))
        print(f'Chunk {i} error: {e}')

print(f'Processed {len(all_chunks)} chunks, {len(bad_chunks)} errors')

Запись результатов по фрагментам в выходные файлы

Если обработанный результат также велик, записывайте его постепенно, а не накапливайте всё в памяти для записи в конце. Откройте CSV-файл и добавляйте каждый обработанный фрагмент с помощью mode='a' и header=False для последующих фрагментов. Благодаря этому объём памяти для выходного конвейера остаётся постоянным, а частичные результаты можно просматривать ещё до завершения всей операции.

import pandas as pd

first_chunk = True
for chunk in pd.read_csv('input.csv', chunksize=100000):
    # Transform
    processed = chunk[chunk['status'] == 'active'].copy()
    processed['revenue_usd'] = processed['revenue'] * 1.10

    # Write incrementally
    mode = 'w' if first_chunk else 'a'
    processed.to_csv('output.csv',
                     mode=mode,
                     header=first_chunk,
                     index=False)
    first_chunk = False

print('Done writing output.csv')

Оценка оптимального размера фрагмента

Выбор chunksize — это поиск баланса: слишком маленькое значение приводит к большому числу итераций цикла Python и значительным накладным расходам, а слишком большое — к тому, что фрагменты не помещаются в RAM. Практический подход состоит в том, чтобы загрузить один фрагмент, измерить его объём памяти с помощью chunk.memory_usage(deep=True).sum() и выбрать chunksize так, чтобы каждый фрагмент занимал примерно 10–20% доступной RAM. Вызов psutil.virtual_memory().available из Python возвращает объём доступной RAM во время выполнения, что позволяет рассчитывать chunksize адаптивно.

import pandas as pd

# Sample 1000 rows to estimate per-row memory
sample = pd.read_csv('big_file.csv', nrows=1000)
bytes_per_row = sample.memory_usage(deep=True).sum() / 1000
print(f'Bytes per row: {bytes_per_row:.0f}')

# Target: use at most 500 MB per chunk
target_bytes = 500 * 1024 * 1024
optimal_chunksize = int(target_bytes / bytes_per_row)
print(f'Recommended chunksize: {optimal_chunksize:,}')

Эффективное объединение результатов обработки по фрагментам

Если Вы накапливаете множество DataFrames фрагментов в списке, а затем объединяете их, учитывайте, что вызов pd.concat для сотен небольших таблиц работает медленно из-за повторного выделения памяти. Лучше выполнять агрегацию внутри каждого фрагмента и сохранять только небольшой агрегированный результат, а не весь фрагмент. Если Вам действительно нужны все строки, постепенная запись в файл Parquet с помощью pyarrow будет быстрее, чем вызов pd.concat в конце.

import pandas as pd

# Efficient: aggregate first, small list of scalars
running_total = 0
running_count = 0

for chunk in pd.read_csv('sales.csv', chunksize=100000):
    running_total += chunk['amount'].sum()
    running_count += chunk['amount'].count()

print(f'Mean amount: {running_total / running_count:.2f}')

# Avoid: accumulating full chunk DataFrames
# results = []
# for chunk in reader:
#     results.append(chunk)   # memory grows to full file size
# df = pd.concat(results)     # slow for hundreds of chunks

Быстрая проверка

Проверьте, насколько хорошо Вы усвоили концепции анализа данных из этого урока.

Итоги урока

В этом уроке Вы узнали, что chunksize в pd.read_csv возвращает итератор DataFrames, позволяющий экономно обрабатывать большие файлы; аргументы usecols и dtype уменьшают объём памяти для каждого фрагмента и ускоряют разбор; а накопительные значения (sum, count, промежуточные результаты) позволяют не создавать список из всех фрагментов. Далее мы подробнее рассмотрим шаблоны постепенной агрегации по фрагментам.

Можно начать бесплатно

Изучай Python с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
30
Уроки
120

Часто задаваемые вопросы

Урок «Потоковое чтение CSV с chunksize» бесплатный?

Да — полный текст урока «Потоковое чтение CSV с chunksize» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Pandas & NumPy Academy, подпишись на CoddyKit PRO. Курс Pandas & NumPy Academy содержит 4 уроков всего.

Чему я научусь в уроке «Потоковое чтение CSV с chunksize»?

Читайте большой CSV частями фиксированного размера с помощью pd.read_csv(chunksize=), обрабатывайте каждую часть и объединяйте или накапливайте результаты. Ты практикуешь Pandas & NumPy Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Pandas & NumPy Academy?

Предыдущий опыт не требуется. Pandas & NumPy Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Потоковое чтение CSV с chunksize»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Pandas & NumPy Academy?

Да. Каждый урок Pandas & NumPy Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Потоковое чтение CSV с chunksize
  2. Постепенное агрегирование по частям
  3. Введение в Dask DataFrames
  4. Parquet: быстрое столбцовое хранилище
← Назад к Pandas & NumPy Academy