Потоковое чтение CSV с chunksize
Читайте большой CSV частями фиксированного размера с помощью pd.read_csv(chunksize=), обрабатывайте каждую часть и объединяйте или накапливайте результаты.
«Потоковое чтение 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 — локальная установка не требуется.
Все уроки этого курса
- Потоковое чтение CSV с chunksize
- Постепенное агрегирование по частям
- Введение в Dask DataFrames
- Parquet: быстрое столбцовое хранилище