Streaming CSV with chunksize
Read a large CSV in fixed-size chunks with pd.read_csv(chunksize=), process each chunk, and concatenate or accumulate results.
Streaming CSV with chunksize is a free Pandas & NumPy Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Pandas & NumPy Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
The Problem with Large CSV Files
When a CSV file is larger than available RAM — say, a 50 GB log file on a machine with 16 GB of memory — calling pd.read_csv('file.csv') fails with a MemoryError or causes the system to swap heavily, making it unusably slow. The solution is chunked reading: instead of loading the entire file at once, you process it in fixed-size pieces, accumulating results without ever holding everything in memory simultaneously.
The chunksize Parameter in read_csv
Passing chunksize=N to pd.read_csv() returns a TextFileReader iterator rather than a DataFrame. Each iteration yields a DataFrame of at most N rows. The file is read lazily — no data is loaded until you ask for the next chunk. This iterator can be used in a for loop or passed to pd.concat(). Choose a chunksize large enough for efficient I/O (e.g., 10,000–100,000 rows) but small enough to fit comfortably in memory.
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 hereProcessing Each Chunk Independently
The most common pattern is to perform a transformation or filter on each chunk and collect results in a list, then concatenate. For example, you might filter rows matching a condition, compute per-chunk statistics, or select only the columns you need. Working on subsets means only the current chunk occupies memory, and the rest of the file is untouched. After the loop, a single pd.concat(results) assembles the final 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))Accumulating Aggregates Across Chunks
Sometimes you do not need to keep any rows at all — you just need a running aggregate. Track a running sum, count, or min/max across chunks without building up a list of DataFrames. This is the most memory-efficient pattern because the memory usage stays constant regardless of file size. At the end, compute the final statistic from your accumulators.
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}')Specifying dtype to Speed Up Chunked Reading
By default, Pandas infers column dtypes from the data, which requires scanning each chunk twice (once to infer, once to parse). Providing the dtype argument avoids this overhead and also prevents dtype inconsistencies between chunks. For example, a column containing mostly integers but one empty cell might infer as float64 in one chunk and object in another. Explicitly specifying dtypes ensures consistent, faster reading across all chunks.
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)
breakSelecting Only Needed Columns
Use the usecols parameter to load only the columns your analysis needs. If a CSV has 50 columns but your aggregation only uses 3, there is no reason to parse the other 47. Combining usecols with chunksize dramatically reduces both I/O time and memory usage. This is one of the simplest and most impactful optimisations for large CSV processing.
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')
breakGroupBy Aggregation in Chunks
Performing a groupby aggregation across chunks requires accumulating partial results. Compute the groupby within each chunk, then combine using a second groupby on the concatenated partial results. For example, to get total sales by region across a 10 GB file, collect per-chunk region sums in a list, then concatenate and group again. This two-pass aggregation pattern is sometimes called a map-reduce approach.
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))Handling Parse Errors Across Chunks
Large CSV files from external sources often have malformed rows — extra commas, wrong encoding, or truncated lines. Use on_bad_lines='skip' (Pandas 1.3+) or error_bad_lines=False (older Pandas) to silently skip bad rows, and encoding='latin-1' if UTF-8 parsing fails. Track which chunks produced errors with a try-except around each chunk's processing to build a robust pipeline that does not crash on a single bad row in a 10 million-row file.
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')Chunked Writing to Output Files
When your processed output is also large, write results incrementally rather than accumulating everything in memory and writing at the end. Open a CSV file and append each processed chunk using mode='a' and header=False for subsequent chunks. This keeps the output pipeline memory-constant and allows you to inspect partial results before the full run completes.
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')Estimating Optimal Chunk Size
Choosing chunksize is a balance: too small means many Python loop iterations and high overhead; too large means chunks don't fit in RAM. A practical approach is to load one chunk, measure its memory with chunk.memory_usage(deep=True).sum(), and set chunksize so each chunk uses roughly 10–20% of available RAM. Python's psutil.virtual_memory().available gives available RAM at runtime, enabling adaptive chunksize calculation.
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:,}')Combining Chunked Results Efficiently
When accumulating many chunk DataFrames in a list and then concatenating, be aware that calling pd.concat on hundreds of small frames is slow due to repeated memory allocation. A better pattern is to aggregate within each chunk and only store the small aggregated result, not the full chunk. If you truly need all rows, writing to a Parquet file incrementally (using pyarrow) is faster than pd.concat at the end.
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 chunksQuick Check
Test your understanding of Data Analysis concepts from this lesson.
Lesson Recap
In this lesson you learned: chunksize in pd.read_csv returns an iterator of DataFrames enabling memory-efficient large-file processing, usecols and dtype arguments reduce per-chunk memory and speed up parsing, and running accumulators (sum, count, partials) avoid building up a list of all chunks. Next up we look at incremental aggregation patterns across chunks in more depth.
Frequently asked questions
Is the “Streaming CSV with chunksize” lesson free?
Yes — the full text of “Streaming CSV with chunksize” is free to read here on the web, and the Pandas & NumPy Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Pandas & NumPy Academy course, upgrade to CoddyKit PRO.
What will I learn in “Streaming CSV with chunksize”?
Read a large CSV in fixed-size chunks with pd.read_csv(chunksize=), process each chunk, and concatenate or accumulate results. You practise Pandas & NumPy Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Pandas & NumPy Academy?
No prior experience is required. Pandas & NumPy Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Streaming CSV with chunksize” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Pandas & NumPy Academy lesson?
Yes. Every Pandas & NumPy Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Streaming CSV with chunksize
- Incremental Aggregation Across Chunks
- Introduction to Dask DataFrames
- Parquet: Fast Columnar Storage