Pandas & NumPy Academy · Lección

Lectura por bloques de archivos grandes

Procese archivos que superen la RAM leyéndolos por bloques con chunksize en read_csv y agregue los resultados de forma incremental.

Lección 4 de 413 pasos

Lectura por bloques de archivos grandes es una lección gratuita de Pandas & NumPy Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Pandas & NumPy Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

Cuando los archivos superan la RAM

Un cuello de botella habitual en las canalizaciones de datos de producción es que un archivo CSV sea más grande que la RAM disponible. Si un archivo ocupa 20 GB y el equipo tiene 16 GB de RAM, pd.read_csv('file.csv') fallará con un MemoryError. La solución es la lectura por fragmentos: cargar el archivo en partes de tamaño fijo, procesar cada parte y acumular los resultados. Esto le permite analizar archivos de cualquier tamaño sin cargarlos por completo en la memoria.

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))

Parámetro chunksize en read_csv

Pase chunksize=n a pd.read_csv() para obtener un iterador TextFileReader en lugar de un DataFrame. Cada iteración devuelve las siguientes n filas como un DataFrame. De este modo, solo hay n filas en memoria en cada momento. Un buen valor inicial para chunksize es 100.000 filas: es lo bastante pequeño para caber en la RAM, pero lo bastante grande para mantener bajo control la sobrecarga de E/S. Ajústelo según la RAM disponible y el número de columnas.

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))

Iteración sobre fragmentos

Utilice un bucle for sobre el iterador de fragmentos para procesar cada fragmento. Para operaciones sencillas, como contar filas, sumar una columna o filtrar, procese cada fragmento de forma independiente y acumule los resultados en una lista o en un total acumulado. Llame siempre al iterador dentro de una instrucción with o asegúrese de volver a crearlo en cada ejecución de la canalización: los iteradores se consumen una sola vez y no se pueden reiniciar.

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}')

Filtrado de filas durante la lectura por fragmentos

Aplique los filtros de filas dentro del bucle para descartar los datos que no necesite antes de acumularlos. Así se mantiene bajo el uso de memoria, ya que solo se conservan las filas que cumplen sus criterios. Por ejemplo, para extraer todas las filas de la región 'North' de un archivo de 10 GB, filtre cada fragmento antes de añadirlo a la lista de resultados. El pd.concat final solo procesará el subconjunto filtrado, que será mucho más pequeño.

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}')

Agregación incremental con GroupBy

Las agregaciones con GroupBy entre fragmentos son más complicadas que las sumas simples, porque un mismo grupo puede abarcar varios fragmentos. El patrón consiste en calcular las sumas y los recuentos por grupo en cada fragmento, concatenar esos resultados parciales y realizar un groupby final sobre los resultados parciales acumulados. Nunca llame a groupby().mean() en cada fragmento para después calcular la media de esas medias: los resultados serán incorrectos cuando el tamaño de los grupos varíe entre fragmentos.

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))

Especificación eficiente de tipos al cargar los datos

Reduzca el uso de memoria durante la carga por fragmentos especificando de antemano los dtypes de las columnas en read_csv. Esto evita que Pandas asigne tipos grandes para cada fragmento y después los descarte. Pase un diccionario como dtype={'region': 'category', 'sales': 'int32'} a read_csv(). Combinado con la lectura por fragmentos, esto puede reducir el uso máximo de memoria a menos del 10 % del que requeriría cargar todo el archivo de forma ingenua.

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}')

Escritura incremental de resultados

Cuando también sea necesario escribir en un archivo el resultado del procesamiento de cada fragmento, escriba cada fragmento procesado a medida que avance, en lugar de acumularlo todo en memoria. Use mode='a' (añadir) y header=False (después del primer fragmento) con to_csv(). Así, tanto la memoria ocupada por la entrada como la de la salida se mantienen limitadas al tamaño del fragmento, lo que permite procesar archivos de cualquier tamaño en un equipo con memoria limitada.

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}')

Introducción a Dask como alternativa directa

Dask proporciona una API similar a la de Pandas que ejecuta las operaciones de forma diferida y en paralelo entre fragmentos automáticamente. En lugar de escribir un bucle manual para procesar fragmentos, escriba dask_df = dd.read_csv('file.csv') y después utilice las mismas operaciones de Pandas: groupby, filter y merge. Llame a .compute() al final para iniciar la ejecución. Dask es la solución más práctica cuando la canalización incluye operaciones complejas de varios pasos que son difíciles de implementar manualmente con fragmentos.

# 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.')

Elección del tamaño de los fragmentos

El tamaño de fragmento ideal equilibra dos factores contrapuestos: un tamaño demasiado pequeño, por ejemplo, 1.000 filas, provoca una gran sobrecarga por fragmento, como la configuración de la E/S del archivo y la creación del DataFrame, y hace que el bucle tarde más. Un tamaño demasiado grande, por ejemplo, 10 millones de filas, contradice el objetivo al cargar demasiados datos en la RAM de una sola vez. Un punto de partida práctico es utilizar fragmentos de entre 50 y 200 MB en memoria. Para un DataFrame con 10 columnas de 8 bytes de media cada una, 100.000 filas ocupan aproximadamente 8 MB por fragmento: un valor predeterminado seguro que deja bastante margen.

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 frente a CSV para datos grandes

Si puede controlar el formato del archivo, prefiera Parquet frente a CSV para conjuntos de datos grandes. Parquet es un formato binario columnar que carga solo las columnas solicitadas (selección de columnas), comprime mucho mejor que CSV, conserva los tipos de datos (sin volver a inferirlos al cargar) y se lee entre 5 y 20 veces más rápido que un CSV equivalente. Convierta una vez un CSV grande a Parquet mediante pd.read_csv('file.csv', chunksize=500000) + to_parquet y, después, use pd.read_parquet(columns=['col1','col2']) para todas las lecturas posteriores.

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())

Patrón completo de una canalización por fragmentos

Un patrón completo para procesar archivos grandes: 1) Especifique los tipos de datos al leer. 2) Filtre las filas lo antes posible dentro del bucle. 3) Calcule agregaciones parciales por fragmento (suma + recuento, nunca la media directamente). 4) Después del bucle, combine los resultados parciales y calcule las estadísticas finales. 5) Escriba la salida de forma incremental si los resultados son grandes. Este patrón permite procesar archivos de cualquier tamaño en cualquier equipo ajustando adecuadamente chunksize.

Comprobación rápida

Compruebe su comprensión de la lectura por fragmentos explicada en esta lección.

Resumen de la lección

En esta lección ha aprendido que chunksize en read_csv devuelve un iterador que proporciona un DataFrame por fragmento, lo que permite procesar de forma incremental archivos más grandes que la RAM; las agregaciones parciales (suma + recuento) se acumulan correctamente entre fragmentos, mientras que no se puede calcular directamente la media de las medias; y el formato Parquet es la mejor alternativa a largo plazo frente a CSV para conjuntos de datos grandes gracias a su compresión, velocidad y conservación de los tipos de datos. Con esto termina el curso de Consejos de rendimiento de Pandas: ya está preparado para los cursos avanzados de nivel B2.

Gratis para empezar

Aprende Python con un tutor de IA — gratis

Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.

Cursos
30
Lecciones
120

Preguntas frecuentes

¿La lección «Lectura por bloques de archivos grandes» es gratis?

Sí — el texto completo de «Lectura por bloques de archivos grandes» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Pandas & NumPy Academy, actualiza a CoddyKit PRO. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Lectura por bloques de archivos grandes»?

Procese archivos que superen la RAM leyéndolos por bloques con chunksize en read_csv y agregue los resultados de forma incremental. Practicas Pandas & NumPy Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Pandas & NumPy Academy?

No se requiere experiencia previa. Pandas & NumPy Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Lectura por bloques de archivos grandes»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Pandas & NumPy Academy?

Sí. Cada lección de Pandas & NumPy Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Perfilar con timeit y memory_profiler
  2. Evitar iterrows y los bucles de Python
  3. Tipos de datos eficientes para reducir la memoria
  4. Lectura por bloques de archivos grandes
← Volver a Pandas & NumPy Academy