0Pricing
Pandas & NumPy Academy · 강의

대용량 파일 청크 단위 읽기

read_csv에서 chunksize를 사용해 RAM을 초과하는 파일을 청크 단위로 읽고 결과를 점진적으로 집계합니다.

대용량 파일 청크 단위 읽기은(는) CoddyKit의 무료 Pandas & NumPy Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Pandas & NumPy Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Pandas & NumPy Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

파일이 RAM을 초과할 때

운영 환경의 데이터 파이프라인에서 흔히 발생하는 병목은 사용 가능한 RAM보다 큰 CSV 파일입니다. 파일이 20GB이고 컴퓨터에 RAM이 16GB만 있다면 pd.read_csv('file.csv')는 MemoryError와 함께 중단됩니다. 해결 방법은 청크 단위 읽기입니다. 파일을 일정한 크기의 조각으로 나누어 로드하고, 각 조각을 처리한 후 결과를 누적합니다. 이렇게 하면 파일 전체를 메모리에 로드하지 않고도 크기에 제한 없이 대용량 파일을 분석할 수 있습니다.

import pandas as pd
import numpy as np

# Simulate a large CSV by writing one
np.random.seed(0)
sample = pd.DataFrame({
    'date': pd.date_range('2023-01-01', periods=100000, freq='h'),
    'region': np.random.choice(['North','South','East','West'], 100000),
    'sales': np.random.randint(100, 1000, 100000)
})
sample.to_csv('/tmp/large_sales.csv', index=False)
print(f'File size: {pd.io.common.get_handle("/tmp/large_sales.csv", "r").handle.seek(0, 2)/1e6:.1f} MB... (simulated)')
print('Rows:', len(sample))

read_csv의 chunksize 매개변수

pd.read_csv()에 chunksize=n을 전달하면 DataFrame 대신 TextFileReader 반복자가 반환됩니다. 반복할 때마다 다음 n개의 행이 DataFrame으로 생성됩니다. 따라서 한 번에 메모리에 유지되는 행은 n개뿐입니다. chunksize의 시작값으로는 100,000행이 적절합니다. RAM에 들어갈 만큼 작으면서도 입출력 오버헤드를 감당할 만큼 크기 때문입니다. 사용 가능한 RAM과 열 개수에 따라 조정합니다.

import pandas as pd

# Read file in chunks of 25,000 rows
chunk_iter = pd.read_csv('/tmp/large_sales.csv', chunksize=25000)

# Peek at the first chunk
first_chunk = next(chunk_iter)
print('First chunk shape:', first_chunk.shape)
print(first_chunk.head(3))

청크 반복하기

청크 반복자에 for 반복문을 사용하여 각 청크를 처리합니다. 행 개수 세기, 열 합계 계산 또는 필터링처럼 간단한 작업은 각 청크를 독립적으로 처리한 다음 결과를 목록이나 누적 합계에 더합니다. 반복자는 한 번 소비하면 다시 시작할 수 없으므로 항상 반복자를 with 문 안에서 호출하거나 각 파이프라인 실행마다 다시 생성해야 합니다.

import pandas as pd

total_rows = 0
total_sales = 0.0
chunk_count = 0

for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
    total_rows += len(chunk)
    total_sales += chunk['sales'].sum()
    chunk_count += 1

print(f'Chunks processed: {chunk_count}')
print(f'Total rows: {total_rows:,}')
print(f'Total sales: {total_sales:,.0f}')
print(f'Avg sales per row: {total_sales/total_rows:.2f}')

청크 처리 중 행 필터링

누적되기 전에 원하지 않는 데이터를 버리도록 반복문 안에서 행 필터를 적용합니다. 이렇게 하면 조건에 맞는 행만 유지하므로 메모리 사용량을 낮게 유지할 수 있습니다. 예를 들어 10GB 파일에서 'North' 지역의 모든 행을 추출하려면 각 청크를 결과 목록에 추가하기 전에 필터링합니다. 최종 pd.concat은 훨씬 작은 필터링 결과만 처리하게 됩니다.

import pandas as pd

filtered_chunks = []

for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
    # Keep only North region rows
    north = chunk[chunk['region'] == 'North']
    if len(north) > 0:
        filtered_chunks.append(north)

north_df = pd.concat(filtered_chunks, ignore_index=True)
print(f'North region rows: {len(north_df):,}')
print(f'North total sales: {north_df["sales"].sum():,.0f}')

증분 GroupBy 집계

청크에 걸친 GroupBy 집계는 단순한 합계보다 까다롭습니다. 여러 청크에 걸쳐 같은 그룹이 나뉘어 있을 수 있기 때문입니다. 패턴은 다음과 같습니다. 각 청크에서 그룹별 합계와 개수를 계산하고, 이러한 부분 결과를 연결한 다음, 누적된 부분 결과에 최종 groupby를 수행합니다. 청크마다 groupby().mean()을 호출한 뒤 평균값들의 평균을 계산하면 안 됩니다. 청크마다 그룹 크기가 다를 때 잘못된 결과가 나오기 때문입니다.

import pandas as pd

partials = []

for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
    # Compute sum and count per region in this chunk
    partial = chunk.groupby('region')['sales'].agg(['sum', 'count'])
    partials.append(partial)

# Combine all partial results
combined = pd.concat(partials).groupby(level=0).sum()
combined['mean'] = combined['sum'] / combined['count']

print('Regional aggregation (chunked):')
print(combined.round(2))

로드 시 메모리 효율적인 데이터 형식 지정

read_csv에서 열의 dtypes를 미리 지정하여 청크를 로드하는 동안 메모리를 줄입니다. 이렇게 하면 Pandas가 각 청크에 넓은 형식을 할당했다가 버리는 일을 방지할 수 있습니다. dtype={'region': 'category', 'sales': 'int32'}와 같은 사전을 read_csv()에 전달합니다. 청크 단위 읽기와 함께 사용하면 단순하게 파일 전체를 로드하는 방식에 비해 최대 메모리를 10% 미만으로 줄일 수 있습니다.

import pandas as pd

specified_dtypes = {
    'region': 'category',
    'sales': 'int32'
}

total_sales = 0
for chunk in pd.read_csv(
    '/tmp/large_sales.csv',
    chunksize=25000,
    dtype=specified_dtypes,
    parse_dates=['date']
):
    total_sales += chunk['sales'].sum()
    # Only 25k rows * (category + int32 + datetime) in RAM at once

print(f'Total sales (memory-efficient): {total_sales:,.0f}')

결과를 증분 방식으로 쓰기

각 청크를 처리한 결과도 파일에 기록해야 한다면 모든 결과를 메모리에 누적하지 말고 처리하는 즉시 각 청크를 기록합니다. 첫 번째 청크 이후에는 mode='a'(추가)와 header=False를 to_csv()와 함께 사용합니다. 이렇게 하면 입력과 출력 모두의 메모리 사용량이 청크 크기 수준으로 유지되므로, 메모리가 제한된 컴퓨터에서도 크기에 제한 없이 파일을 처리할 수 있습니다.

import pandas as pd
import os

output_path = '/tmp/filtered_output.csv'

# Remove previous output if it exists
if os.path.exists(output_path):
    os.remove(output_path)

first_chunk = True
for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
    # Process: keep only high-value rows
    processed = chunk[chunk['sales'] > 800].copy()
    
    # Write: header only on first chunk, append thereafter
    processed.to_csv(output_path,
                     mode='a',
                     header=first_chunk,
                     index=False)
    first_chunk = False

result = pd.read_csv(output_path)
print(f'High-value rows written: {len(result):,}')
print(f'Average sales (>800): {result["sales"].mean():.1f}')

즉시 사용 가능한 대안인 Dask 소개

Dask는 Pandas와 유사한 API를 제공하며, 청크 전체에서 작업을 지연 실행하고 자동으로 병렬 처리합니다. 수동으로 청크 처리 반복문을 작성하는 대신 dask_df = dd.read_csv('file.csv')를 작성한 후 groupby, 필터링, 병합과 같은 동일한 Pandas 작업을 수행하면 됩니다. 실행을 시작하려면 마지막에 .compute()를 호출합니다. 수동 청크 처리로 구현하기 어려운 복잡한 여러 단계의 작업이 파이프라인에 포함되어 있다면 Dask가 가장 실용적인 해결책입니다.

# pip install dask
# import dask.dataframe as dd

# Read the large CSV as a Dask DataFrame (lazy — no data loaded yet)
# ddf = dd.read_csv('/tmp/large_sales.csv')

# Operations are lazy — no computation happens until .compute()
# result = ddf.groupby('region')['sales'].mean().compute()
# print(result)

# Key Dask advantages:
# - Automatic chunking (no manual chunksize loops)
# - Parallel execution (multi-core)
# - Familiar Pandas API
# - Works with files larger than RAM

print('Dask extends Pandas to datasets larger than RAM with minimal API changes.')

청크 크기 선택

이상적인 청크 크기는 서로 경쟁하는 두 요소의 균형을 맞춥니다. 너무 작으면(예: 1,000행) 청크마다 발생하는 오버헤드(파일 입출력 설정, DataFrame 생성)가 커져 반복문 실행 시간이 길어집니다. 너무 크면(예: 1,000만 행) 한 번에 너무 많은 데이터를 RAM에 로드하므로 청크 처리의 목적을 잃게 됩니다. 실용적인 시작점은 메모리에서 50~200MB가 되는 청크를 목표로 하는 것입니다. 평균 8바이트인 열 10개를 가진 DataFrame이라면 100,000행은 청크당 약 8MB이므로, 충분한 여유 공간을 남기는 안전한 기본값입니다.

import pandas as pd
import timeit

# Benchmark different chunk sizes
for chunksize in [10000, 50000, 100000]:
    t = timeit.timeit(
        lambda: sum(chunk['sales'].sum()
                    for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=chunksize)),
        number=3
    )
    print(f'chunksize={chunksize:>7,}: {t/3:.3f}s per pass')

대용량 데이터에서 Parquet과 CSV 비교

파일 형식을 직접 선택할 수 있다면 대용량 데이터세트에는 CSV보다 Parquet을 사용합니다. Parquet은 열 기반 이진 형식으로, 요청한 열만 로드하고(열 가지치기), CSV보다 훨씬 효율적으로 압축하며, 데이터 형식을 보존하고(로드할 때 다시 추론할 필요가 없음), 동일한 CSV보다 5~20배 빠르게 읽습니다. pd.read_csv('file.csv', chunksize=500000)와 to_parquet을 사용하여 대용량 CSV를 한 번 Parquet으로 변환한 후, 이후에는 pd.read_parquet(columns=['col1','col2'])를 사용하여 읽습니다.

import pandas as pd

# Convert our CSV to Parquet (do this once)
df = pd.read_csv('/tmp/large_sales.csv', parse_dates=['date'])
df['region'] = df['region'].astype('category')
df['sales'] = df['sales'].astype('int32')
df.to_parquet('/tmp/large_sales.parquet', index=False)

# Now read only the columns needed — much faster than CSV
sales_by_region = pd.read_parquet(
    '/tmp/large_sales.parquet',
    columns=['region', 'sales']
)
print('Parquet read result:')
print(sales_by_region.groupby('region')['sales'].mean().round(1))
print('\ndtypes preserved:', sales_by_region.dtypes.to_dict())

전체 청크 처리 파이프라인 패턴

대용량 파일을 처리하는 전체 패턴은 다음과 같습니다. 1) 읽을 때 데이터 형식을 지정합니다. 2) 반복문 안에서 가능한 한 이른 단계에 행을 필터링합니다. 3) 청크마다 부분 집계(합계 + 개수)를 계산합니다. 평균을 직접 계산해서는 안 됩니다. 4) 반복문이 끝난 후 부분 결과를 결합하고 최종 통계를 계산합니다. 5) 결과가 크다면 출력을 증분 방식으로 기록합니다. 이 패턴은 chunksize를 적절히 조정하면 어떤 컴퓨터에서도 모든 크기의 파일을 처리할 수 있습니다.

빠른 확인

이 강의에서 배운 청크 단위 읽기에 대한 이해도를 확인합니다.

강의 요약

이 강의에서는 다음을 배웠습니다. read_csv의 chunksize는 청크마다 하나의 DataFrame을 생성하는 반복자를 반환하므로 RAM보다 큰 파일도 증분 방식으로 처리할 수 있습니다. 부분 집계(합계 + 개수)는 청크 전체에서 올바르게 누적되지만 평균은 직접 평균을 낼 수 없습니다. 또한 압축, 속도 및 데이터 형식 보존 측면에서 Parquet 형식은 대용량 데이터세트에 사용하기 위한 장기적인 CSV 대안으로 가장 적합합니다. 이것으로 Pandas 성능 팁 과정을 마칩니다. 이제 고급 B2 과정을 학습할 준비가 되었습니다!

자주 묻는 질문

“대용량 파일 청크 단위 읽기” 강의는 무료인가요?

네 — “대용량 파일 청크 단위 읽기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Pandas & NumPy Academy 강의 전체를 잠금 해제할 수 있습니다. Pandas & NumPy Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“대용량 파일 청크 단위 읽기”에서 뭘 배우나요?

read_csv에서 chunksize를 사용해 RAM을 초과하는 파일을 청크 단위로 읽고 결과를 점진적으로 집계합니다. 브라우저에서 직접 실행하는 실습 코드로 Pandas & NumPy Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Pandas & NumPy Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Pandas & NumPy Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“대용량 파일 청크 단위 읽기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Pandas & NumPy Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Pandas & NumPy Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. timeit과 memory_profiler로 프로파일링하기
  2. iterrows와 Python 반복문 피하기
  3. 메모리 절약을 위한 효율적인 데이터 형식
  4. 대용량 파일 청크 단위 읽기
← Pandas & NumPy Academy(으)로 돌아가기