Tutorial · · 5 min de lectura

CSV español en Pandas: separador, decimal y encoding

Descargas datos del INE y Pandas te devuelve una columna ilegible. Te cuento cómo arreglar el separador, el decimal y el encoding de un CSV español.

pandascsvread_csvencodingpython

Descargas un CSV del INE, lo abres con pd.read_csv(), y lo que debería ser un DataFrame con doce columnas aparece como una sola: una línea enorme con punto y coma por todas partes, números con coma donde esperabas un punto, y alguna ñ convertida en ñ. Si te ha pasado, no es un bug de Pandas. Es que el CSV español tiene sus propias reglas.

Te cuento cuáles son y cómo resolverlas en tres pasos.

El problema: Pandas espera comas, España usa punto y coma

Cuando haces pd.read_csv('datos.csv'), Pandas asume que el separador de columnas es la coma. Es el estándar anglosajón y el que usan la mayoría de datasets que encontrarás en tutoriales y cursos. Pero en España —y en buena parte de Europa continental— el formato CSV usa punto y coma como separador de campos y coma como separador decimal.

No es una rareza. El INE publica así sus estadísticas de empleo, población y precios. Los ministerios (SEPE, AEAT) exportan datos en ese formato. Los ayuntamientos cuelgan CSV con punto y coma en sus portales de datos abiertos. Y si exportas desde Excel con la configuración regional española, el resultado es el mismo.

El error típico tiene esta pinta:

import pandas as pd

df = pd.read_csv('ine_parados.csv')
df.head()
#    Comunidad autónoma;Tasa de paro;Población activa;...
# 0  Andalucía;21,3;4.235.100;...
# 1  Aragón;9,8;682.300;...

Una sola columna. Todo el texto junto. Pandas no ha separado nada porque buscaba comas y no las encontró.

Paso 1: el separador (sep=';')

La solución más directa es decirle a Pandas qué carácter separa las columnas:

df = pd.read_csv('ine_parados.csv', sep=';')

Con eso ya tienes las columnas bien separadas. Con archivos del INE o de un ministerio, este parámetro suele ser suficiente.

Pero probablemente te queden dos más.

Paso 2: el decimal (decimal=',')

En el formato español, el número 1.234,56 significa “mil doscientos treinta y cuatro con cincuenta y seis”. La coma separa los decimales y el punto agrupa los miles. Pandas, por defecto, espera lo contrario: el punto como decimal y la coma como separador de miles.

Si no le dices nada, Pandas leerá "21,3" como texto, no como número. Tu columna de tasas de paro será de tipo object y cualquier operación aritmética fallará o dará resultados incorrectos.

df = pd.read_csv('ine_parados.csv', sep=';', decimal=',')

Ahora "21,3" se convierte en 21.3 (float) y "4.235.100" sigue siendo texto porque tiene puntos de miles. Para resolver también los miles:

df = pd.read_csv('ine_parados.csv', sep=';', decimal=',', thousands='.')

Con estos tres parámetros, Pandas interpreta correctamente 4.235.100 como el entero 4235100 y 21,3 como el float 21.3.

Paso 3: el encoding (encoding='utf-8' o 'latin-1')

El tercer problema no tiene que ver con números ni separadores, sino con caracteres. Si al abrir el CSV ves ñ en lugar de ñ, é en lugar de é, o ó en lugar de ó, el encoding está mal.

La regla práctica es sencilla: los archivos modernos usan UTF-8; los antiguos, latin-1. Si el CSV lo descargaste de un portal de datos abiertos reciente, prueba primero con UTF-8. Si el archivo viene de una exportación de Excel en Windows o de un sistema legacy, prueba con latin-1.

# Archivo moderno (portal de datos abiertos, descarga reciente)
df = pd.read_csv('datos.csv', sep=';', decimal=',', encoding='utf-8')

# Archivo legacy (Excel en Windows, sistema antiguo)
df = pd.read_csv('datos.csv', sep=';', decimal=',', encoding='latin-1')

Si no estás seguro de cuál es el encoding del archivo, puedes detectarlo con chardet:

import chardet

with open('datos.csv', 'rb') as f:
    resultado = chardet.detect(f.read())

print(resultado['encoding'])

Eso te dirá si el archivo es UTF-8, latin-1, ISO-8859-15 o cualquier otra codificación. No es infalible con archivos pequeños, pero con CSV de cierto tamaño acierta casi siempre.

La llamada completa

Juntando los tres pasos, la carga robusta de un CSV español queda así:

import pandas as pd

df = pd.read_csv(
    'ine_parados.csv',
    sep=';',
    decimal=',',
    thousands='.',
    encoding='utf-8',
)

Cuatro parámetros. Esa es toda la diferencia entre un DataFrame usable y una columna de texto ilegible.

Árbol de decisión rápido

Cuando te encuentres con un CSV que no se lee bien, sigue este orden:

  1. ¿Una sola columna con todo el texto junto? El problema es el separador. Añade sep=';'.
  2. ¿Los números con coma aparecen como texto? El problema es el decimal. Añade decimal=','.
  3. ¿Caracteres extraños donde debería haber tildes o eñes? El problema es el encoding. Prueba encoding='latin-1'.
  4. ¿Los tres problemas a la vez? Añade los tres parámetros. Es el caso más habitual con archivos del INE.

Este orden no es casual. El separador es lo primero que compruebas porque es lo más visible: si no hay columnas, no puedes mirar nada más. El decimal viene después porque es lo que rompe los cálculos. El encoding es el último porque a veces no se nota hasta que filtras por una comunidad autónoma con tilde.

Parámetros extra que conviene conocer

Dos parámetros más de pd.read_csv() que te van a servir con archivos reales. Yo los uso casi siempre con CSV grandes o desorganizados:

nrows — cuando el CSV pesa cientos de megas y solo quieres inspeccionar la estructura antes de cargarlo entero:

df_muestra = pd.read_csv('datos_gigante.csv', sep=';', nrows=100)

skiprows — cuando el archivo tiene filas de metadatos al principio (título, fuente, fecha de extracción) que no son datos:

df = pd.read_csv('datos.csv', sep=';', skiprows=3)

El INE suele incluir dos o tres líneas de cabecera con la fuente y la fecha antes de los datos reales. Si no las saltas, Pandas las toma como nombres de columna y todo se descuadra.

Comprobar que todo ha ido bien

Después de cargar el archivo, dedica treinta segundos a verificar:

print(df.shape)          # ¿coincide con las filas y columnas que esperas?
print(df.dtypes)         # ¿las columnas numéricas son float64 o int64, no object?
print(df.head())         # ¿los caracteres españoles se ven bien?

Este artículo cubre el caso más frecuente de carga de datos en España, pero es solo la puerta de entrada. La UT04 — Carga y acceso a datos de Análisis de Datos con Python recorre el pipeline completo: CSV, Excel, JSON, APIs REST y bases de datos SQLite, siempre con datasets reales de organismos españoles. Si estás montando un módulo de análisis de datos o dando tus primeros pasos con Pandas, ahí tienes el siguiente paso.

Un CSV español bien cargado es un CSV donde las columnas se llaman como deben, los números se pueden sumar y las tildes están donde las pusiste.

¿Qué fuente de datos española te ha dado más problemas al cargarla?