Saltar al contenido

lección 3

Leer cualquier cosa: CSV, JSON, Excel, Parquet con Pandas

Domina la lectura de los 4 formatos más comunes y entiende cuándo usar cada uno.

55 min

En la lección anterior usaste `pd.read_csv('ventas.csv')` y funcionó a la primera. Una línea, cero problemas. Ahora imagina esto: te llega un fichero de un proveedor francés donde el separador es punto y coma, los decimales van con coma, las tildes salen como interrogaciones, y la cabecera está en la fila 4. Pandas sigue pudiendo con ello — pero necesitas decirle cómo. Esta lección es el manual de supervivencia para cuando los datos no vienen limpios.

### El problema de los mil formatos

En el mundo real, los datos no llegan limpios ni en un formato estándar. Un proveedor te manda un Excel con macros. El equipo de producto exporta a CSV con separador punto y coma (porque son europeos). El frontend genera logs en JSON anidado. Y el equipo de datos guarda en Parquet porque es más eficiente para análisis. Tu trabajo como analista es leerlos TODOS sin pestañear.

Pandas es políglota: sabe leer decenas de formatos. Pero los 4 que usarás el 95% del tiempo son CSV, JSON, Excel y Parquet. Cada uno tiene sus trampas y sus parámetros clave. Vamos a dominarlos uno a uno.

### CSV: el formato universal (y sus 47 variantes)

CSV significa "Comma Separated Values". Suena simple: valores separados por comas. Pero en la práctica te encontrarás: separadores que no son comas (punto y coma, tabulador, pipe), encodings que corrompen acentos (UTF-8, Latin-1, Windows-1252), cabeceras que faltan o están en la fila 3, y fechas en 15 formatos distintos. read_csv tiene más de cuarenta parámetros por algo.

1import pandas as pd
2
3# Lectura básica — funciona si tienes suerte
4df = pd.read_csv('ventas.csv')
5
6# Lectura defensiva — para el mundo real
7df = pd.read_csv(
8 'ventas_europa.csv',
9 sep=';', # Separador europeo
10 encoding='latin-1', # Encoding para acentos
11 parse_dates=['fecha'], # Convierte strings a datetime
12 decimal=',', # Decimal europeo (1.234,56)
13 na_values=['N/A', '-', ''], # Valores que significan "no hay dato"
14 dtype={'codigo_postal': str} # Forzar tipo (evita que 08001 sea 8001)
15)
16
17print(df.head())
18print(df.dtypes)

read_csv defensivo: los parámetros que necesitarás en el 80% de CSVs reales

Consejo de senior: la primera vez que leas un CSV nuevo, SIEMPRE mira las primeras líneas en un editor de texto (VS Code) antes de cargarlo en Pandas. Así descubres el separador, el encoding y las sorpresas ANTES de pelear con errores crípticos de pandas.

Trampa clásica: códigos postales, teléfonos y IDs que empiezan por 0 (como 08001 Barcelona). Si Pandas los interpreta como números, el 0 desaparece y 08001 se convierte en 8001. SIEMPRE usa dtype={"columna": str} para columnas con ceros a la izquierda.

Cuidado al combinar parámetros. thousands="." le dice a Pandas que el punto separa los miles. Si tus fechas también llevan puntos — 05.03.2024, como en media Europa — y te olvidas de meter esa columna en parse_dates, Pandas se come los puntos y te deja 5032024. Un número entero donde debía haber una fecha, sin un solo error por medio. Cuando un CSV lleva puntos en las fechas Y en los números, parse_dates no es opcional.

### JSON: el formato de las APIs

JSON es el idioma de las APIs web. Cuando tu app se comunica con un servidor, los datos viajan en JSON. El problema es que JSON puede estar anidado (objetos dentro de objetos dentro de listas) y Pandas espera datos planos (filas y columnas). Ahí entra json_normalize.

1import pandas as pd
2
3# JSON plano (cada elemento = una fila) — fácil
4df = pd.read_json('usuarios.json')
5
6# JSON anidado — necesitas aplanarlo
7import json
8
9with open('pedidos_api.json') as f:
10 data = json.load(f)
11
12# Si la estructura es: [{"id": 1, "cliente": {"nombre": "Ana", "ciudad": "Madrid"}, "total": 150}]
13df = pd.json_normalize(
14 data,
15 sep='_' # cliente.nombre → cliente_nombre
16)
17print(df.columns.tolist())
18# ['id', 'total', 'cliente_nombre', 'cliente_ciudad']

json_normalize aplana estructuras anidadas en columnas con prefijo

### Excel: el formato del "business"

Te guste o no, Excel sigue siendo el rey en departamentos de finanzas, RRHH y operaciones. Recibirás archivos .xlsx con múltiples hojas, celdas fusionadas, fórmulas y formatos. Pandas puede leerlos, pero necesita la librería openpyxl como motor.

1# Instalar el motor para Excel
2pip install openpyxl==3.1.5

openpyxl es necesario para leer archivos .xlsx

1import pandas as pd
2
3# Leer la primera hoja
4df = pd.read_excel('informe_financiero.xlsx')
5
6# Leer una hoja específica
7df = pd.read_excel('informe_financiero.xlsx', sheet_name='Q4_2024')
8
9# Leer TODAS las hojas → devuelve un diccionario {nombre_hoja: DataFrame}
10todas = pd.read_excel('informe_financiero.xlsx', sheet_name=None)
11for nombre, datos in todas.items():
12 print(f"Hoja '{nombre}': {datos.shape}")
13
14# Saltar filas de cabecera decorativa
15df = pd.read_excel(
16 'informe_financiero.xlsx',
17 header=2, # La fila 3 es la cabecera real (0-indexed)
18 usecols='B:F' # Solo columnas B a F
19)

Lectura de Excel: hojas, cabeceras y selección de columnas

### Parquet: el formato que se acuerda de los tipos

Parquet es el formato columnar creado por el ecosistema Apache (Hadoop, Spark) para almacenar datos analíticos de forma eficiente. ¿Por qué columnar? Porque las consultas analíticas típicas leen pocas columnas de muchas filas (ej: "dame la suma de ventas" solo necesita la columna ventas, no las 50 columnas restantes). Parquet comprime cada columna por separado: si una columna repite los mismos valores — un país, un canal, un estado de pedido — los guarda una sola vez y apunta a ellos. Con tablas grandes y repetitivas eso son reducciones de 5 a más de 30 veces frente al CSV. Con tablas pequeñas no: por debajo de unos pocos cientos de filas, la ficha técnica que Parquet escribe para poder leerse por columnas pesa tanto que el fichero sale igual o más grande que el CSV. La ventaja en tamaño se paga con volumen; la ventaja en tipos, que es la de abajo, la tienes desde la primera fila.

Y hay algo que Parquet gana desde la primera fila, sin necesidad de volumen: se acuerda de los tipos. Un CSV es texto. Cuando guardas una columna de fechas y la vuelves a leer, vuelve como texto y tienes que reconstruirla con parse_dates otra vez; y si tenías códigos postales con cero delante, el cero ha desaparecido y toca volver a poner dtype. Con JSON pasa lo mismo. Excel al menos se acuerda de las fechas, pero tampoco de los ceros. Parquet guarda la fecha como fecha, el texto como texto y el decimal como decimal, y al leerlo te lo devuelve tal cual: los dos parámetros que has tenido que pelear en el primer ejercicio de esta lección, con Parquet no hacen falta. Si guardas un paso intermedio de tu análisis y lo vas a volver a abrir mañana, esa es la razón para usar Parquet — mucho antes que el tamaño.

1# Instalar el motor para Parquet
2pip install pyarrow==16.1.0

PyArrow es el motor recomendado para Parquet en Pandas

1import pandas as pd
2
3# Leer Parquet — es así de simple
4df = pd.read_parquet('ventas_historico.parquet')
5
6# Leer SOLO las columnas que necesitas (poda de columnas)
7df = pd.read_parquet(
8 'ventas_historico.parquet',
9 columns=['fecha', 'producto', 'importe']
10)
11
12# Leer SOLO las filas que cumplen una condición (predicate pushdown)
13df = pd.read_parquet(
14 'ventas_historico.parquet',
15 filters=[('importe', '>', 400)]
16)
17
18# Guardar un DataFrame como Parquet
19df.to_parquet('resultado.parquet', index=False)
20
21# Comparación de tamaños (ejemplo con tabla grande y repetitiva):
22# ventas.csv → 450 MB
23# ventas.parquet → 52 MB
24# Con 200 filas esta diferencia no aparece: hace falta volumen.

Parquet: poda de columnas, filtro de filas y tipos preservados

Regla de oro para formatos: CSV para intercambio rápido con humanos, JSON para APIs, Excel para enviar al CFO, Parquet para guardar los pasos intermedios de tu análisis. Si guardas datos intermedios en CSV estás malgastando disco y tiempo de lectura.

### Tabla resumen: cuándo usar cada formato

Los tamaños son de una tabla grande y repetitiva. Con pocas filas Parquet no gana en tamaño: lo que gana siempre es que se acuerda de los tipos.

### Escribir datos: to_csv, to_json, to_parquet

Leer es solo la mitad. Después de transformar los datos, necesitas guardarlos. Pandas ofrece métodos to_* simétricos a los read_*.

1import pandas as pd
2
3df = pd.DataFrame({
4 'cliente': ['Ana', 'Carlos', 'Lucía'],
5 'total_compras': [1500, 3200, 800],
6 'fecha_ultimo_pedido': pd.to_datetime(['2024-01-15', '2024-03-20', '2024-02-10'])
7})
8
9# Guardar como CSV (para enviar a alguien)
10df.to_csv('resumen_clientes.csv', index=False)
11
12# Guardar como JSON (para una API)
13df.to_json('resumen_clientes.json', orient='records', date_format='iso')
14
15# Guardar como Parquet (para reutilizar mañana sin limpiar de nuevo)
16df.to_parquet('resumen_clientes.parquet', index=False)
17
18# Guardar como Excel (para el jefe)
19df.to_excel('resumen_clientes.xlsx', index=False, sheet_name='Resumen')

Guardar en distintos formatos: index=False evita la columna de índice innecesaria

Cuando guardas en CSV, Pandas escribe el índice por defecto (esa columna 0,1,2... que no sirve para nada). SIEMPRE usa index=False a menos que tu índice tenga significado real (como fechas en una serie temporal).

### Truco pro: leer solo lo que necesitas

Con archivos grandes, cargar todo en memoria es un desperdicio si solo necesitas unas columnas o unas filas. Pandas permite leer de forma parcial:

1import pandas as pd
2
3# Solo las columnas que necesitas
4df = pd.read_csv('ventas_grande.csv', usecols=['fecha', 'producto', 'importe'])
5
6# Solo las primeras N filas (para explorar)
7df = pd.read_csv('ventas_grande.csv', nrows=1000)
8
9# Lectura por chunks (para archivos que no caben en RAM)
10for chunk in pd.read_csv('ventas_enorme.csv', chunksize=100_000):
11 # Procesar cada trozo de 100K filas
12 resultado = chunk.groupby('producto')['importe'].sum()
13 print(resultado.head())

Lectura parcial: usecols, nrows y chunksize para archivos grandes

Regístrate para guardar tu progreso.

## comentarios

Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.

Inicia sesión para comentar y responder.

cargando comentarios...